2
0

wagtail_hooks.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. from django.urls import include, path, reverse, reverse_lazy
  2. from django.utils.translation import gettext, ngettext
  3. from django.utils.translation import gettext_lazy as _
  4. import wagtail.admin.rich_text.editors.draftail.features as draftail_features
  5. from wagtail import hooks
  6. from wagtail.admin.admin_url_finder import (
  7. ModelAdminURLFinder,
  8. register_admin_url_finder,
  9. )
  10. from wagtail.admin.menu import MenuItem
  11. from wagtail.admin.navigation import get_site_for_user
  12. from wagtail.admin.search import SearchArea
  13. from wagtail.admin.site_summary import SummaryItem
  14. from wagtail.images import admin_urls, get_image_model, image_operations
  15. from wagtail.images.api.admin.views import ImagesAdminAPIViewSet
  16. from wagtail.images.forms import GroupImagePermissionFormSet
  17. from wagtail.images.permissions import permission_policy
  18. from wagtail.images.rich_text import ImageEmbedHandler
  19. from wagtail.images.rich_text.contentstate import ContentstateImageConversionRule
  20. from wagtail.images.rich_text.editor_html import EditorHTMLImageConversionRule
  21. from wagtail.images.views.bulk_actions import (
  22. AddTagsBulkAction,
  23. AddToCollectionBulkAction,
  24. DeleteBulkAction,
  25. )
  26. from wagtail.images.views.chooser import viewset as chooser_viewset
  27. @hooks.register("register_admin_urls")
  28. def register_admin_urls():
  29. return [
  30. path("images/", include(admin_urls, namespace="wagtailimages")),
  31. ]
  32. @hooks.register("construct_admin_api")
  33. def construct_admin_api(router):
  34. router.register_endpoint("images", ImagesAdminAPIViewSet)
  35. class ImagesMenuItem(MenuItem):
  36. def is_shown(self, request):
  37. return permission_policy.user_has_any_permission(
  38. request.user, ["add", "change", "delete"]
  39. )
  40. @hooks.register("register_admin_menu_item")
  41. def register_images_menu_item():
  42. return ImagesMenuItem(
  43. _("Images"),
  44. reverse("wagtailimages:index"),
  45. name="images",
  46. icon_name="image",
  47. order=300,
  48. )
  49. @hooks.register("register_rich_text_features")
  50. def register_image_feature(features):
  51. # define a handler for converting <embed embedtype="image"> tags into frontend HTML
  52. features.register_embed_type(ImageEmbedHandler)
  53. # define how to convert between editorhtml's representation of images and
  54. # the database representation
  55. features.register_converter_rule(
  56. "editorhtml", "image", EditorHTMLImageConversionRule
  57. )
  58. # define a draftail plugin to use when the 'image' feature is active
  59. features.register_editor_plugin(
  60. "draftail",
  61. "image",
  62. draftail_features.EntityFeature(
  63. {
  64. "type": "IMAGE",
  65. "icon": "image",
  66. "description": gettext("Image"),
  67. # We do not want users to be able to copy-paste hotlinked images into rich text.
  68. # Keep only the attributes Wagtail needs.
  69. "attributes": ["id", "src", "alt", "format"],
  70. # Keep only images which are from Wagtail.
  71. "allowlist": {
  72. "id": True,
  73. },
  74. "chooserUrls": {
  75. "imageChooser": reverse_lazy("wagtailimages_chooser:choose")
  76. },
  77. },
  78. js=[
  79. "wagtailimages/js/image-chooser-modal.js",
  80. ],
  81. ),
  82. )
  83. # define how to convert between contentstate's representation of images and
  84. # the database representation
  85. features.register_converter_rule(
  86. "contentstate", "image", ContentstateImageConversionRule
  87. )
  88. # add 'image' to the set of on-by-default rich text features
  89. features.default_features.append("image")
  90. @hooks.register("register_image_operations")
  91. def register_image_operations():
  92. return [
  93. ("original", image_operations.DoNothingOperation),
  94. ("fill", image_operations.FillOperation),
  95. ("min", image_operations.MinMaxOperation),
  96. ("max", image_operations.MinMaxOperation),
  97. ("width", image_operations.WidthHeightOperation),
  98. ("height", image_operations.WidthHeightOperation),
  99. ("scale", image_operations.ScaleOperation),
  100. ("jpegquality", image_operations.JPEGQualityOperation),
  101. ("webpquality", image_operations.WebPQualityOperation),
  102. ("avifquality", image_operations.AvifQualityOperation),
  103. ("format", image_operations.FormatOperation),
  104. ("bgcolor", image_operations.BackgroundColorOperation),
  105. ]
  106. class ImagesSummaryItem(SummaryItem):
  107. order = 200
  108. template_name = "wagtailimages/homepage/site_summary_images.html"
  109. def get_context_data(self, parent_context):
  110. site_name = get_site_for_user(self.request.user)["site_name"]
  111. return {
  112. "total_images": permission_policy.instances_user_has_any_permission_for(
  113. self.request.user, {"add", "change", "delete", "choose"}
  114. ).count(),
  115. "site_name": site_name,
  116. }
  117. def is_shown(self):
  118. return permission_policy.user_has_any_permission(
  119. self.request.user, ["add", "change", "delete"]
  120. )
  121. @hooks.register("construct_homepage_summary_items")
  122. def add_images_summary_item(request, items):
  123. items.append(ImagesSummaryItem(request))
  124. class ImagesSearchArea(SearchArea):
  125. def is_shown(self, request):
  126. return permission_policy.user_has_any_permission(
  127. request.user, ["add", "change", "delete"]
  128. )
  129. @hooks.register("register_admin_search_area")
  130. def register_images_search_area():
  131. return ImagesSearchArea(
  132. _("Images"),
  133. reverse("wagtailimages:index"),
  134. name="images",
  135. icon_name="image",
  136. order=200,
  137. )
  138. @hooks.register("register_group_permission_panel")
  139. def register_image_permissions_panel():
  140. return GroupImagePermissionFormSet
  141. @hooks.register("describe_collection_contents")
  142. def describe_collection_docs(collection):
  143. images_count = get_image_model().objects.filter(collection=collection).count()
  144. if images_count:
  145. url = reverse("wagtailimages:index") + ("?collection_id=%d" % collection.id)
  146. return {
  147. "count": images_count,
  148. "count_text": ngettext("%(count)s image", "%(count)s images", images_count)
  149. % {"count": images_count},
  150. "url": url,
  151. }
  152. class ImageAdminURLFinder(ModelAdminURLFinder):
  153. edit_url_name = "wagtailimages:edit"
  154. permission_policy = permission_policy
  155. register_admin_url_finder(get_image_model(), ImageAdminURLFinder)
  156. for action_class in [AddTagsBulkAction, AddToCollectionBulkAction, DeleteBulkAction]:
  157. hooks.register("register_bulk_action", action_class)
  158. @hooks.register("register_admin_viewset")
  159. def register_image_chooser_viewset():
  160. return chooser_viewset