revisions.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. from django.conf import settings
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.core.exceptions import PermissionDenied
  4. from django.shortcuts import get_object_or_404, redirect
  5. from django.template.loader import render_to_string
  6. from django.template.response import TemplateResponse
  7. from django.urls import reverse
  8. from django.utils.decorators import method_decorator
  9. from django.utils.safestring import mark_safe
  10. from django.utils.translation import gettext as _
  11. from django.utils.translation import gettext_lazy
  12. from wagtail.admin import messages
  13. from wagtail.admin.action_menu import PageActionMenu
  14. from wagtail.admin.auth import user_has_any_page_permission, user_passes_test
  15. from wagtail.admin.ui.components import MediaContainer
  16. from wagtail.admin.ui.side_panels import (
  17. CommentsSidePanel,
  18. PageStatusSidePanel,
  19. PreviewSidePanel,
  20. )
  21. from wagtail.admin.views.generic.models import (
  22. RevisionsCompareView,
  23. RevisionsUnscheduleView,
  24. )
  25. from wagtail.admin.views.generic.preview import PreviewRevision
  26. from wagtail.models import Page
  27. from wagtail.utils.timestamps import render_timestamp
  28. def revisions_index(request, page_id):
  29. return redirect("wagtailadmin_pages:history", page_id)
  30. def revisions_revert(request, page_id, revision_id):
  31. # TODO: refactor this into a class-based view that extends the EditView
  32. page = get_object_or_404(Page, id=page_id).specific
  33. page_perms = page.permissions_for_user(request.user)
  34. if not page_perms.can_edit():
  35. raise PermissionDenied
  36. revision = get_object_or_404(page.revisions, id=revision_id)
  37. revision_page = revision.as_object()
  38. scheduled_page = page.get_scheduled_revision_as_object()
  39. content_type = ContentType.objects.get_for_model(page)
  40. page_class = content_type.model_class()
  41. if getattr(settings, "WAGTAIL_I18N_ENABLED", False):
  42. locale = page.locale
  43. translations = [
  44. {
  45. "locale": translation.locale,
  46. "url": reverse("wagtailadmin_pages:edit", args=[translation.id]),
  47. }
  48. for translation in page.get_translations()
  49. .only("id", "locale", "depth")
  50. .select_related("locale")
  51. if translation.permissions_for_user(request.user).can_edit()
  52. ]
  53. else:
  54. locale = None
  55. translations = []
  56. edit_handler = page_class.get_edit_handler()
  57. form_class = edit_handler.get_form_class()
  58. form = form_class(instance=revision_page, for_user=request.user)
  59. edit_handler = edit_handler.get_bound_panel(
  60. instance=revision_page, request=request, form=form
  61. )
  62. preview_url = reverse("wagtailadmin_pages:preview_on_edit", args=[page.id])
  63. lock = page.get_lock()
  64. action_menu = PageActionMenu(
  65. request,
  66. view="revisions_revert",
  67. page=page,
  68. lock=lock,
  69. locked_for_user=lock is not None and lock.for_user(request.user),
  70. )
  71. side_panels = [
  72. PageStatusSidePanel(
  73. revision_page,
  74. request,
  75. show_schedule_publishing_toggle=form.show_schedule_publishing_toggle,
  76. live_object=page,
  77. scheduled_object=scheduled_page,
  78. locale=locale,
  79. translations=translations,
  80. ),
  81. ]
  82. if page.is_previewable():
  83. side_panels.append(PreviewSidePanel(page, request, preview_url=preview_url))
  84. if form.show_comments_toggle:
  85. side_panels.append(CommentsSidePanel(page, request))
  86. side_panels = MediaContainer(side_panels)
  87. media = MediaContainer([edit_handler, form, action_menu, side_panels]).media
  88. user_avatar = render_to_string(
  89. "wagtailadmin/shared/user_avatar.html", {"user": revision.user}
  90. )
  91. messages.warning(
  92. request,
  93. mark_safe(
  94. _(
  95. "You are viewing a previous version of this page from <b>%(created_at)s</b> by %(user)s"
  96. )
  97. % {
  98. "created_at": render_timestamp(revision.created_at),
  99. "user": user_avatar,
  100. }
  101. ),
  102. )
  103. page_title = _("Editing %(page_type)s") % {
  104. "page_type": page_class.get_verbose_name()
  105. }
  106. page_subtitle = page.get_admin_display_title()
  107. header_title = f"{page_title}: {page_subtitle}"
  108. return TemplateResponse(
  109. request,
  110. "wagtailadmin/pages/edit.html",
  111. {
  112. "page": page,
  113. "revision": revision,
  114. "is_revision": True,
  115. "content_type": content_type,
  116. "edit_handler": edit_handler,
  117. "errors_debug": None,
  118. "action_menu": action_menu,
  119. "side_panels": side_panels,
  120. "header_title": header_title,
  121. "form": form, # Used in unit tests
  122. "media": media,
  123. },
  124. )
  125. @method_decorator(user_passes_test(user_has_any_page_permission), name="dispatch")
  126. class RevisionsView(PreviewRevision):
  127. model = Page
  128. def setup(self, request, page_id, revision_id, *args, **kwargs):
  129. # Rename path kwargs from pk to page_id
  130. return super().setup(request, page_id, revision_id, *args, **kwargs)
  131. def get_object(self):
  132. page = get_object_or_404(Page, id=self.pk).specific
  133. perms = page.permissions_for_user(self.request.user)
  134. if not (perms.can_publish() or perms.can_edit()):
  135. raise PermissionDenied
  136. return page
  137. class RevisionsCompare(RevisionsCompareView):
  138. history_label = gettext_lazy("Page history")
  139. edit_label = gettext_lazy("Edit this page")
  140. history_url_name = "wagtailadmin_pages:history"
  141. edit_url_name = "wagtailadmin_pages:edit"
  142. header_icon = "doc-empty-inverse"
  143. @method_decorator(user_passes_test(user_has_any_page_permission))
  144. def dispatch(self, request, *args, **kwargs):
  145. return super().dispatch(request, *args, **kwargs)
  146. def get_object(self, queryset=None):
  147. return get_object_or_404(Page, id=self.pk).specific
  148. def get_edit_handler(self):
  149. return self.object.get_edit_handler()
  150. def get_page_subtitle(self):
  151. return self.object.get_admin_display_title()
  152. class RevisionsUnschedule(RevisionsUnscheduleView):
  153. model = Page
  154. edit_url_name = "wagtailadmin_pages:edit"
  155. history_url_name = "wagtailadmin_pages:history"
  156. revisions_unschedule_url_name = "wagtailadmin_pages:revisions_unschedule"
  157. header_icon = "doc-empty-inverse"
  158. def setup(self, request, page_id, revision_id, *args, **kwargs):
  159. # Rename path kwargs from pk to page_id
  160. return super().setup(request, page_id, revision_id, *args, **kwargs)
  161. def get_object(self, queryset=None):
  162. page = get_object_or_404(Page, id=self.pk).specific
  163. if not page.permissions_for_user(self.request.user).can_unschedule():
  164. raise PermissionDenied
  165. return page
  166. def get_object_display_title(self):
  167. return self.object.get_admin_display_title()