views.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. from django.contrib import messages
  2. from django.db.models import ObjectDoesNotExist
  3. from django.shortcuts import redirect
  4. from django.urls import reverse
  5. from django.utils.translation import gettext, gettext_lazy as _
  6. from ...admin.views import generic
  7. from ..models import Theme
  8. from .css import move_css_down, move_css_up
  9. from .forms import ThemeForm, UploadCssForm, UploadMediaForm
  10. class ThemeAdmin(generic.AdminBaseMixin):
  11. root_link = "misago:admin:appearance:themes:index"
  12. model = Theme
  13. form = ThemeForm
  14. templates_dir = "misago/admin/themes"
  15. message_404 = _("Requested theme does not exist.")
  16. class ThemesList(ThemeAdmin, generic.ListView):
  17. pass
  18. class NewTheme(ThemeAdmin, generic.ModelFormView):
  19. message_submit = _('New theme "%(name)s" has been saved.')
  20. def initialize_form(self, form, request, _):
  21. if request.method == "POST":
  22. return form(request.POST, request.FILES)
  23. try:
  24. initial = {"parent": int(request.GET.get("parent"))}
  25. except (TypeError, ValueError):
  26. initial = {}
  27. return form(initial=initial)
  28. class EditTheme(ThemeAdmin, generic.ModelFormView):
  29. message_submit = _('Theme "%(name)s" has been updated.')
  30. def check_permissions(self, request, target):
  31. if target.is_default:
  32. return gettext("Default theme can't be edited.")
  33. class DeleteTheme(ThemeAdmin, generic.ModelFormView):
  34. message_submit = _('Theme "%(name)s" has been deleted.')
  35. def check_permissions(self, request, target):
  36. if target.is_default:
  37. return gettext("Default theme can't be deleted.")
  38. class ActivateTheme(ThemeAdmin, generic.ButtonView):
  39. def button_action(self, request, target):
  40. set_theme_as_active(request, target)
  41. message = gettext('Active theme has been changed to "%(name)s".')
  42. messages.success(request, message % {"name": target})
  43. def set_theme_as_active(request, theme):
  44. Theme.objects.update(is_active=False)
  45. Theme.objects.filter(pk=theme.pk).update(is_active=True)
  46. class ThemeAssetsAdmin(ThemeAdmin):
  47. def check_permissions(self, request, theme):
  48. if theme.is_default:
  49. return gettext("Default theme assets can't be edited.")
  50. def redirect_to_theme_assets(self, theme):
  51. link = reverse("misago:admin:appearance:themes:assets", kwargs={"pk": theme.pk})
  52. return redirect(link)
  53. class ThemeAssets(ThemeAssetsAdmin, generic.TargetedView):
  54. template = "assets/list.html"
  55. def real_dispatch(self, request, theme):
  56. return self.render(request, {"theme": theme})
  57. class ThemeAssetsActionAdmin(ThemeAssetsAdmin):
  58. def real_dispatch(self, request, theme):
  59. if request.method == "POST":
  60. self.action(request, theme)
  61. return self.redirect_to_theme_assets(theme)
  62. def action(self, request, theme):
  63. raise NotImplementedError(
  64. "action method must be implemented in inheriting class"
  65. )
  66. class UploadThemeAssets(ThemeAssetsActionAdmin, generic.TargetedView):
  67. message_partial_success = _(
  68. "Some css files could not have been added to the theme."
  69. )
  70. message_submit = None
  71. form = None
  72. def action(self, request, theme):
  73. form = self.form( # pylint: disable=not-callable
  74. request.POST, request.FILES, instance=theme
  75. )
  76. if not form.is_valid():
  77. if form.cleaned_data.get("assets"):
  78. messages.info(request, self.message_partial_success)
  79. for error in form.errors["assets"]:
  80. messages.error(request, error)
  81. if form.cleaned_data.get("assets"):
  82. form.save()
  83. messages.success(request, self.message_success)
  84. class UploadThemeCss(UploadThemeAssets):
  85. message_success = _("New CSS files have been added to the theme.")
  86. form = UploadCssForm
  87. class UploadThemeMedia(UploadThemeAssets):
  88. message_success = _("New media files have been added to the theme.")
  89. form = UploadMediaForm
  90. class DeleteThemeAssets(ThemeAssetsActionAdmin, generic.TargetedView):
  91. message_submit = None
  92. queryset_attr = None
  93. def action(self, request, theme):
  94. items = self.clean_items_list(request)
  95. if items:
  96. queryset = getattr(theme, self.queryset_attr)
  97. for item in items:
  98. self.delete_asset(queryset, item)
  99. messages.success(request, self.message_submit)
  100. def clean_items_list(self, request):
  101. try:
  102. return {int(i) for i in request.POST.getlist("item")[:20]}
  103. except (ValueError, TypeError):
  104. pass
  105. def delete_asset(self, queryset, item):
  106. try:
  107. queryset.get(pk=item).delete()
  108. except ObjectDoesNotExist:
  109. pass
  110. class DeleteThemeCss(DeleteThemeAssets):
  111. message_submit = _("Selected CSS files have been deleted.")
  112. queryset_attr = "css"
  113. class DeleteThemeMedia(DeleteThemeAssets):
  114. message_submit = _("Selected media have been deleted.")
  115. queryset_attr = "media"
  116. class ThemeCssAdmin(ThemeAssetsAdmin, generic.TargetedView):
  117. def wrapped_dispatch(self, request, pk, css_pk):
  118. theme = self.get_target_or_none(request, {"pk": pk})
  119. if not theme:
  120. messages.error(request, self.message_404)
  121. return redirect(self.root_link)
  122. error = self.check_permissions( # pylint: disable=assignment-from-no-return
  123. request, theme
  124. )
  125. if error:
  126. messages.error(request, error)
  127. return redirect(self.root_link)
  128. try:
  129. css = theme.css.select_for_update().get(pk=css_pk)
  130. except ObjectDoesNotExist:
  131. css_error = gettext("Requested CSS could not be found in the theme.")
  132. messages.error(request, css_error)
  133. return self.redirect_to_theme_assets(theme)
  134. return self.real_dispatch(request, theme, css)
  135. class MoveThemeCssUp(ThemeCssAdmin):
  136. def real_dispatch(self, request, theme, css):
  137. if request.method == "POST" and move_css_up(theme, css):
  138. messages.success(request, gettext('"%s" was moved up.') % css)
  139. return self.redirect_to_theme_assets(theme)
  140. class MoveThemeCssDown(ThemeCssAdmin):
  141. def real_dispatch(self, request, theme, css):
  142. if request.method == "POST" and move_css_down(theme, css):
  143. messages.success(request, gettext('"%s" was moved down.') % css)
  144. return self.redirect_to_theme_assets(theme)