mirror of
https://github.com/Hopiu/wagtail.git
synced 2026-05-18 04:01:11 +00:00
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
299 lines
10 KiB
Python
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
|