wagtail/wagtail/admin/utils.py
Karl Hobley 80ff06f988 Rename wagtail.wagtailusers to wagtail.users
Conflicts:
	wagtail/admin/tests/test_pages_views.py
	wagtail/admin/utils.py
	wagtail/admin/views/account.py
	wagtail/users/views/groups.py
	wagtail/users/views/users.py
	wagtail/users/wagtail_hooks.py
2017-11-26 22:43:47 +00:00

299 lines
10 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import logging
from functools import wraps
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
from django.core.mail import send_mail as django_send_mail
from django.db.models import Count, Q
from django.shortcuts import redirect
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from django.utils.translation import override, ugettext_lazy
from modelcluster.fields import ParentalKey
from taggit.models import Tag
from wagtail.core.models import GroupPagePermission, Page, PageRevision
from wagtail.users.models import UserProfile
logger = logging.getLogger('wagtail.admin')
# Wagtail languages with >=90% coverage
# This list is manually maintained
WAGTAILADMIN_PROVIDED_LANGUAGES = [
('ca', ugettext_lazy('Catalan')),
('de', ugettext_lazy('German')),
('el', ugettext_lazy('Greek')),
('en', ugettext_lazy('English')),
('es', ugettext_lazy('Spanish')),
('fi', ugettext_lazy('Finnish')),
('fr', ugettext_lazy('French')),
('gl', ugettext_lazy('Galician')),
('is-is', ugettext_lazy('Icelandic')),
('it', ugettext_lazy('Italian')),
('ko', ugettext_lazy('Korean')),
('lt', ugettext_lazy('Lithuanian')),
('nb', ugettext_lazy('Norwegian Bokmål')),
('nl-nl', ugettext_lazy('Netherlands Dutch')),
('fa', ugettext_lazy('Persian')),
('pl', ugettext_lazy('Polish')),
('pt-br', ugettext_lazy('Brazilian Portuguese')),
('pt-pt', ugettext_lazy('Portuguese')),
('ro', ugettext_lazy('Romanian')),
('ru', ugettext_lazy('Russian')),
('se', ugettext_lazy('Swedish')),
('zh-cn', ugettext_lazy('Chinese (China)')),
]
def get_available_admin_languages():
return getattr(settings, 'WAGTAILADMIN_PERMITTED_LANGUAGES', WAGTAILADMIN_PROVIDED_LANGUAGES)
def get_object_usage(obj):
"Returns a queryset of pages that link to a particular object"
pages = Page.objects.none()
# get all the relation objects for obj
relations = [f for f in type(obj)._meta.get_fields(include_hidden=True)
if (f.one_to_many or f.one_to_one) and f.auto_created]
for relation in relations:
related_model = relation.related_model
# if the relation is between obj and a page, get the page
if issubclass(related_model, Page):
pages |= Page.objects.filter(
id__in=related_model._base_manager.filter(**{
relation.field.name: obj.id
}).values_list('id', flat=True)
)
else:
# if the relation is between obj and an object that has a page as a
# property, return the page
for f in related_model._meta.fields:
if isinstance(f, ParentalKey) and issubclass(f.remote_field.model, Page):
pages |= Page.objects.filter(
id__in=related_model._base_manager.filter(
**{
relation.field.name: obj.id
}).values_list(f.attname, flat=True)
)
return pages
def popular_tags_for_model(model, count=10):
"""Return a queryset of the most frequently used tags used on this model class"""
content_type = ContentType.objects.get_for_model(model)
return Tag.objects.filter(
taggit_taggeditem_items__content_type=content_type
).annotate(
item_count=Count('taggit_taggeditem_items')
).order_by('-item_count')[:count]
def users_with_page_permission(page, permission_type, include_superusers=True):
# Get user model
User = get_user_model()
# Find GroupPagePermission records of the given type that apply to this page or an ancestor
ancestors_and_self = list(page.get_ancestors()) + [page]
perm = GroupPagePermission.objects.filter(permission_type=permission_type, page__in=ancestors_and_self)
q = Q(groups__page_permissions__in=perm)
# Include superusers
if include_superusers:
q |= Q(is_superuser=True)
return User.objects.filter(is_active=True).filter(q).distinct()
def permission_denied(request):
"""Return a standard 'permission denied' response"""
if request.is_ajax():
raise PermissionDenied
from wagtail.admin import messages
messages.error(request, _('Sorry, you do not have permission to access this area.'))
return redirect('wagtailadmin_home')
def user_passes_test(test):
"""
Given a test function that takes a user object and returns a boolean,
return a view decorator that denies access to the user if the test returns false.
"""
def decorator(view_func):
# decorator takes the view function, and returns the view wrapped in
# a permission check
@wraps(view_func)
def wrapped_view_func(request, *args, **kwargs):
if test(request.user):
# permission check succeeds; run the view function as normal
return view_func(request, *args, **kwargs)
else:
# permission check failed
return permission_denied(request)
return wrapped_view_func
return decorator
def permission_required(permission_name):
"""
Replacement for django.contrib.auth.decorators.permission_required which returns a
more meaningful 'permission denied' response than just redirecting to the login page.
(The latter doesn't work anyway because Wagtail doesn't define LOGIN_URL...)
"""
def test(user):
return user.has_perm(permission_name)
# user_passes_test constructs a decorator function specific to the above test function
return user_passes_test(test)
def any_permission_required(*perms):
"""
Decorator that accepts a list of permission names, and allows the user
to pass if they have *any* of the permissions in the list
"""
def test(user):
for perm in perms:
if user.has_perm(perm):
return True
return False
return user_passes_test(test)
class PermissionPolicyChecker(object):
"""
Provides a view decorator that enforces the given permission policy,
returning the wagtailadmin 'permission denied' response if permission not granted
"""
def __init__(self, policy):
self.policy = policy
def require(self, action):
def test(user):
return self.policy.user_has_permission(user, action)
return user_passes_test(test)
def require_any(self, *actions):
def test(user):
return self.policy.user_has_any_permission(user, actions)
return user_passes_test(test)
def send_mail(subject, message, recipient_list, from_email=None, **kwargs):
if not from_email:
if hasattr(settings, 'WAGTAILADMIN_NOTIFICATION_FROM_EMAIL'):
from_email = settings.WAGTAILADMIN_NOTIFICATION_FROM_EMAIL
elif hasattr(settings, 'DEFAULT_FROM_EMAIL'):
from_email = settings.DEFAULT_FROM_EMAIL
else:
from_email = 'webmaster@localhost'
return django_send_mail(subject, message, from_email, recipient_list, **kwargs)
def send_notification(page_revision_id, notification, excluded_user_id):
# Get revision
revision = PageRevision.objects.get(id=page_revision_id)
# Get list of recipients
if notification == 'submitted':
# Get list of publishers
recipients = users_with_page_permission(revision.page, 'publish')
elif notification in ['rejected', 'approved']:
# Get submitter
recipients = [revision.user]
else:
return False
# Get list of email addresses
email_recipients = [
recipient for recipient in recipients
if recipient.email and recipient.pk != excluded_user_id and getattr(
UserProfile.get_for_user(recipient),
notification + '_notifications'
)
]
# Return if there are no email addresses
if not email_recipients:
return True
# Get template
template_subject = 'wagtailadmin/notifications/' + notification + '_subject.txt'
template_text = 'wagtailadmin/notifications/' + notification + '.txt'
template_html = 'wagtailadmin/notifications/' + notification + '.html'
# Common context to template
context = {
"revision": revision,
"settings": settings,
}
# Send emails
sent_count = 0
for recipient in email_recipients:
try:
# update context with this recipient
context["user"] = recipient
# Translate text to the recipient language settings
with override(recipient.wagtail_userprofile.get_preferred_language()):
# Get email subject and content
email_subject = render_to_string(template_subject, context).strip()
email_content = render_to_string(template_text, context).strip()
kwargs = {}
if getattr(settings, 'WAGTAILADMIN_NOTIFICATION_USE_HTML', False):
kwargs['html_message'] = render_to_string(template_html, context)
# Send email
send_mail(email_subject, email_content, [recipient.email], **kwargs)
sent_count += 1
except Exception:
logger.exception(
"Failed to send notification email '%s' to %s",
email_subject, recipient.email
)
return sent_count == len(email_recipients)
def user_has_any_page_permission(user):
"""
Check if a user has any permission to add, edit, or otherwise manage any
page.
"""
# Can't do nothin if you're not active.
if not user.is_active:
return False
# Superusers can do anything.
if user.is_superuser:
return True
# At least one of the users groups has a GroupPagePermission.
# The user can probably do something.
if GroupPagePermission.objects.filter(group__in=user.groups.all()).exists():
return True
# Specific permissions for a page type do not mean anything.
# No luck! This user can not do anything with pages.
return False