cms.utils.moderator: 130 total statements, 0.0% covered

Generated: Wed 2013-03-13 10:33 CET

Source file: /media/Envs/Envs/filer-gallery/lib/python2.7/site-packages/cms/utils/moderator.py

Stats: 0 executed, 119 missed, 11 excluded, 128 ignored

  1. # -*- coding: utf-8 -*-
  2. import datetime
  3. from django.utils.translation import ugettext as _
  4. from django.conf import settings
  5. from cms.models import Page, PageModeratorState, PageModerator, CMSPlugin, Title
  6. from cms.utils import timezone
  7. I_APPROVE = 100 # current user should approve page
  8. I_APPROVE_DELETE = 200
  9. def page_changed(page, old_page=None, force_moderation_action=None):
  10. """Called from page post save signal. If page already had pk, old version
  11. of page is provided in old_page argument.
  12. """
  13. # get user from thread locals
  14. from cms.utils.permissions import get_current_user
  15. user = get_current_user()
  16. force_moderation_action = force_moderation_action or getattr(page, 'force_moderation_action', None)
  17. if force_moderation_action:
  18. PageModeratorState(user=user, page=page, action=force_moderation_action).save()
  19. return
  20. if not old_page:
  21. # just newly created page
  22. PageModeratorState(user=user, page=page, action=PageModeratorState.ACTION_ADD).save()
  23. if (old_page is None and page.published) or \
  24. (old_page and not old_page.published == page.published):
  25. action = page.published and PageModeratorState.ACTION_PUBLISH or PageModeratorState.ACTION_UNPUBLISH
  26. PageModeratorState(user=user, page=page, action=action).save()
  27. if ((old_page and not old_page.moderator_state == page.moderator_state) or not old_page) \
  28. and page.requires_approvement():
  29. # update_moderation_message can be called after this :S -> recipient will not
  30. # see the last message
  31. mail_approvement_request(page, user)
  32. # TODO: if page was changed, remove all approvements from higher instances,
  33. # but keep approvements by lower instances, if there are any
  34. def update_moderation_message(page, message):
  35. """This is bit special.. It updates last page state made from current user
  36. for given page. Its called after page is saved - page state is created when
  37. page gets saved (in signal), so this might have a concurrency issue, but
  38. probably will work in 99,999%.
  39. If any page state is'nt found in last UPDATE_TOLERANCE seconds, a new state
  40. will be created instead of affecting old message.
  41. """
  42. UPDATE_TOLERANCE = 30 # max in last 30 seconds
  43. from cms.utils.permissions import get_current_user
  44. user = get_current_user()
  45. created = timezone.now() - datetime.timedelta(seconds=UPDATE_TOLERANCE)
  46. try:
  47. state = page.pagemoderatorstate_set.filter(user=user, created__gt=created).order_by('-created')[0]
  48. # just state without message!!
  49. assert not state.message
  50. except (IndexError, AssertionError):
  51. state = PageModeratorState(user=user, page=page, action=PageModeratorState.ACTION_CHANGED)
  52. state.message = message
  53. state.save()
  54. def page_moderator_state(request, page):
  55. """Return moderator page state from page.moderator_state, but also takes
  56. look if current user is in the approvement path, and should approve the this
  57. page. In this case return 100 as an state value.
  58. Returns:
  59. dict(state=state, label=label)
  60. """
  61. state, label = page.moderator_state, ""
  62. under_moderation = page.get_moderator_queryset()
  63. # TODO: OPTIMIZE!! calls 1 or 2 q per list item (page)
  64. if settings.CMS_MODERATOR:
  65. if state == Page.MODERATOR_APPROVED_WAITING_FOR_PARENTS:
  66. label = _('parent first')
  67. elif page.requires_approvement() and page.has_moderate_permission(request) \
  68. and under_moderation.filter(user=request.user).count() \
  69. and not page.pagemoderatorstate_set.filter(user=request.user, action=PageModeratorState.ACTION_APPROVE).count():
  70. # only if he didn't approve already...
  71. is_delete = state == Page.MODERATOR_NEED_DELETE_APPROVEMENT
  72. state = is_delete and I_APPROVE_DELETE or I_APPROVE
  73. label = is_delete and _('delete') or _('approve')
  74. elif not page.is_approved():
  75. # if no moderator, we have just 2 states => changed / unchanged
  76. state = Page.MODERATOR_NEED_APPROVEMENT
  77. if not page.is_approved() and not label:
  78. if under_moderation.count():
  79. label = dict(page.moderator_state_choices)[state]
  80. return dict(state=state, label=label)
  81. def moderator_should_approve(request, page):
  82. """Says if user should approve given page. (just helper)
  83. """
  84. return page_moderator_state(request, page)['state'] >= I_APPROVE
  85. def requires_moderation(page):
  86. """Returns True if page requires moderation
  87. """
  88. return bool(page.get_moderator_queryset().count())
  89. def will_require_moderation(target_id, position):
  90. """Check if newly added page will require moderation
  91. """
  92. if not settings.CMS_MODERATOR:
  93. return False
  94. target = Page.objects.get(pk=target_id)
  95. if position == 'first-child':
  96. return requires_moderation(target)
  97. elif position in ('left', 'right'):
  98. if target.parent:
  99. return requires_moderation(target.parent)
  100. return False
  101. def get_test_moderation_level(page, user=None, include_user=True):
  102. """Returns min moderation level for page, and result of user test if
  103. user is given, so output is always tuple of:
  104. (moderation_level, requires_approvement)
  105. Meaning of requires_approvement is - somebody of higher instance must
  106. approve changes made on this page by given user.
  107. NOTE: May require some optimization, might call 3 huge sql queries in
  108. worse case
  109. """
  110. qs = page.get_moderator_queryset()
  111. if not settings.CMS_MODERATOR or (user and user.is_superuser):
  112. if include_user and qs.filter(user__id=user.id, moderate_page=True).count():
  113. return 0, True
  114. return 0, False
  115. if qs.filter(user__is_superuser=True).count():
  116. return 0, True
  117. if user:
  118. if qs.filter(user__id=user.id, user__globalpagepermission__gt=0).count():
  119. return 0, False
  120. try:
  121. moderator = qs.filter(user__id=user.id).select_related()[0]
  122. return moderator.page.level, False
  123. except IndexError:
  124. pass
  125. else:
  126. if qs.filter(user__globalpagepermission__gt=0).count():
  127. return 0, True
  128. try:
  129. moderator = qs.select_related()[0]
  130. except IndexError:
  131. return PageModerator.MAX_MODERATION_LEVEL, False
  132. return moderator.page.level, True
  133. def approve_page(request, page):
  134. """Main approving function. Two things can happen here, depending on user
  135. level:
  136. 1.) User is somewhere in the approvement path, but not on the top. In this
  137. case just mark this page as approved by this user.
  138. 2.) User is on top of approvement path. Draft page with all dependencies
  139. will be `copied` to public model, page states log will be cleaned.
  140. """
  141. moderation_level, moderation_required = get_test_moderation_level(page, request.user, False)
  142. if not moderator_should_approve(request, page):
  143. # escape soon if there isn't any approval required by this user
  144. if not page.publisher_public or page.get_absolute_url() != page.publisher_public.get_absolute_url():
  145. page.publish()
  146. else:
  147. return
  148. if not moderation_required:
  149. # this is a second case - user can publish changes
  150. if page.pagemoderatorstate_set.get_delete_actions().count():
  151. # it is a delete request for this page!!
  152. page.delete_with_public()
  153. else:
  154. page.publish()
  155. else:
  156. # first case - just mark page as approved from this user
  157. PageModeratorState(user=request.user, page=page, action=PageModeratorState.ACTION_APPROVE).save()
  158. page.save(change_state=False)
  159. def get_model_queryset(model, request=None):
  160. """Decision function used in frontend - says which model should be used.
  161. Public models are used only if CMS_MODERATOR.
  162. """
  163. if not settings.CMS_MODERATOR:
  164. # We do not use moderator
  165. return model.objects.drafts()
  166. # We do use moderator
  167. if request:
  168. preview_draft = ('preview' in request.GET and 'draft' in request.GET)
  169. edit_mode = ('edit' in request.GET or request.session.get('cms_edit', False))
  170. if preview_draft or edit_mode:
  171. return model.objects.drafts()
  172. # Default case / moderator is used but there is no request
  173. return model.objects.public()
  174. # queryset helpers for basic models
  175. get_page_queryset = lambda request=None: get_model_queryset(Page, request)
  176. get_title_queryset = lambda request=None: Title.objects.all() # not sure if we need to only grab public items here
  177. get_cmsplugin_queryset = lambda request=None: CMSPlugin.objects.all() # CMSPlugin is no longer extending from Publisher
  178. def mail_approvement_request(page, user=None):
  179. """Sends approvement request over email to all users which should approve
  180. this page if they have an email entered.
  181. Don't send it to current user - he should know about it, because he made the
  182. change.
  183. """
  184. if not settings.CMS_MODERATOR or not page.requires_approvement():
  185. return
  186. recipient_list = []
  187. for moderator in page.get_moderator_queryset():
  188. email = moderator.user.email
  189. if email and not email in recipient_list:
  190. recipient_list.append(email)
  191. if user and user.email in recipient_list:
  192. recipient_list.remove(user.email)
  193. if not recipient_list:
  194. return
  195. from django.core.urlresolvers import reverse
  196. from django.contrib.sites.models import Site
  197. from cms.utils.urlutils import urljoin
  198. from cms.utils.mail import send_mail
  199. site = Site.objects.get_current()
  200. subject = _('CMS - Page %s requires approvement.') % unicode(page)
  201. context = {
  202. 'page': page,
  203. 'admin_url': "http://%s" % urljoin(site.domain, reverse('admin:index'), 'cms/page', page.id),
  204. }
  205. send_mail(subject, 'admin/cms/mail/approvement_required.txt', recipient_list, context, 'admin/cms/mail/approvement_required.html')