actions.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. from django.contrib import messages
  2. from django.shortcuts import redirect
  3. from django.utils.translation import ugettext_lazy as _
  4. from misago.core.exceptions import AjaxError
  5. from misago.threads.moderation import ModerationError
  6. __all__ = ['ActionsBase', 'ReloadAfterDelete']
  7. class ReloadAfterDelete(object):
  8. pass
  9. class ActionsBase(object):
  10. query_key = 'action'
  11. invalid_action_message = _("Requested action is invalid.")
  12. def __init__(self, **kwargs):
  13. if kwargs.get('user').is_authenticated():
  14. self.available_actions = self.get_available_actions(kwargs)
  15. else:
  16. self.available_actions = []
  17. self.actions_names = [a['action'] for a in self.available_actions]
  18. self.selected_ids = []
  19. def __nonzero__(self):
  20. return bool(self.available_actions)
  21. def __contains__(self, item):
  22. return item in self.actions_names
  23. def get_available_actions(self, kwargs):
  24. raise NotImplementedError("get_available_actions has to return list "
  25. "of dicts with allowed actions")
  26. def resolve_action(self, request):
  27. action_name = request.POST.get(self.query_key)
  28. for action in self.available_actions:
  29. if action['action'] == action_name and not action.get('is_button'):
  30. if ':' in action_name:
  31. action_bits = action_name.split(':')
  32. action_name = action_bits[0]
  33. action_arg = action_bits[1]
  34. else:
  35. action_arg = None
  36. action_callable = 'action_%s' % action_name
  37. return getattr(self, action_callable), action_arg
  38. else:
  39. raise ModerationError(self.invalid_action_message)
  40. def clean_selection(self, data):
  41. filtered_data = []
  42. for pk in data[:50]: # a tiny fail-safe to avoid too big workloads
  43. try:
  44. filtered_data.append(int(pk))
  45. except ValueError:
  46. pass
  47. if not filtered_data:
  48. raise ModerationError(self.select_items_message)
  49. return filtered_data
  50. def handle_post(self, request, target):
  51. try:
  52. if self.is_mass_action:
  53. return self.handle_mass_action(request, target)
  54. else:
  55. return self.handle_single_action(request, target)
  56. except ModerationError as e:
  57. if request.is_ajax():
  58. raise AjaxError(e.message, 406)
  59. else:
  60. messages.error(request, e.message)
  61. return False
  62. def handle_mass_action(self, request, queryset):
  63. action, action_arg = self.resolve_action(request)
  64. self.selected_ids = self.clean_selection(
  65. request.POST.getlist('item', []))
  66. filtered_queryset = queryset.filter(pk__in=self.selected_ids)
  67. if filtered_queryset.exists():
  68. if action_arg:
  69. response = action(request, filtered_queryset, action_arg)
  70. else:
  71. response = action(request, filtered_queryset)
  72. if isinstance(response, ReloadAfterDelete):
  73. return self.redirect_after_deletion(request, queryset)
  74. if response:
  75. return response
  76. elif request.is_ajax():
  77. raise AjaxError(self.invalid_action_message, 406)
  78. else:
  79. # prepare default response: page reload
  80. return redirect(request.path)
  81. else:
  82. raise ModerationError(self.select_items_message)
  83. def redirect_after_deletion(self, request, queryset):
  84. raise NotImplementedError("action handlers should declare their own "
  85. "redirect_after_deletion methods")
  86. def handle_single_action(self, request, target):
  87. action, action_arg = self.resolve_action(request)
  88. if action_arg:
  89. response = action(request, target, action_arg)
  90. else:
  91. response = action(request, target)
  92. if response:
  93. return response
  94. elif request.is_ajax():
  95. raise AjaxError(self.invalid_action_message, 406)
  96. else:
  97. # prepare default response: page reload
  98. return redirect(request.path)
  99. def get_list(self):
  100. visible_actions = []
  101. for action in self.available_actions:
  102. if not action.get('is_hidden'):
  103. visible_actions.append(action)
  104. return visible_actions
  105. def get_selected_ids(self):
  106. return self.selected_ids