views.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. import copy
  2. import floppyforms as forms
  3. from urlparse import urlparse
  4. from django.core.urlresolvers import resolve, reverse as django_reverse
  5. from django.db.models import Q
  6. from django.http import Http404
  7. from django.shortcuts import redirect
  8. from django.utils.translation import ugettext as _
  9. from mptt.forms import TreeNodeChoiceField
  10. from misago.admin import site
  11. from misago.apps.admin.widgets import *
  12. from misago.models import Forum
  13. from misago.monitor import monitor, UpdatingMonitor
  14. from misago.shortcuts import render_to_response
  15. from misago.utils.strings import slugify
  16. from misago.apps.admin.forums.forms import NewNodeForm, CategoryForm, ForumForm, RedirectForm, DeleteForm
  17. def reverse(route, target=None):
  18. if target:
  19. return django_reverse(route, kwargs={'target': target.pk, 'slug': target.slug})
  20. return django_reverse(route)
  21. """
  22. Views
  23. """
  24. class List(ListWidget):
  25. admin = site.get_action('forums')
  26. id = 'list'
  27. columns = (
  28. ('forum', _("Forum")),
  29. )
  30. nothing_checked_message = _('You have to select at least one forum.')
  31. actions = (
  32. ('resync_fast', _("Resynchronize forums (fast)")),
  33. ('resync', _("Resynchronize forums")),
  34. )
  35. empty_message = _('No forums are currently defined.')
  36. def get_items(self):
  37. return self.admin.model.objects.get(special='root').get_descendants()
  38. def sort_items(self, page_items, sorting_method):
  39. return page_items.order_by('lft')
  40. def get_item_actions(self, item):
  41. if item.type == 'category':
  42. return (
  43. self.action('chevron-up', _("Move Category Up"), reverse('admin_forums_up', item), post=True),
  44. self.action('chevron-down', _("Move Category Down"), reverse('admin_forums_down', item), post=True),
  45. self.action('pencil', _("Edit Category"), reverse('admin_forums_edit', item)),
  46. self.action('remove', _("Delete Category"), reverse('admin_forums_delete', item)),
  47. )
  48. if item.type == 'forum':
  49. return (
  50. self.action('chevron-up', _("Move Forum Up"), reverse('admin_forums_up', item), post=True),
  51. self.action('chevron-down', _("Move Forum Down"), reverse('admin_forums_down', item), post=True),
  52. self.action('pencil', _("Edit Forum"), reverse('admin_forums_edit', item)),
  53. self.action('remove', _("Delete Forum"), reverse('admin_forums_delete', item)),
  54. )
  55. return (
  56. self.action('chevron-up', _("Move Redirect Up"), reverse('admin_forums_up', item), post=True),
  57. self.action('chevron-down', _("Move Redirect Down"), reverse('admin_forums_down', item), post=True),
  58. self.action('pencil', _("Edit Redirect"), reverse('admin_forums_edit', item)),
  59. self.action('remove', _("Delete Redirect"), reverse('admin_forums_delete', item)),
  60. )
  61. def action_resync_fast(self, items, checked):
  62. for forum in items:
  63. if forum.pk in checked:
  64. forum.sync()
  65. forum.save(force_update=True)
  66. return Message(_('Selected forums have been resynchronized successfully.'), 'success'), reverse('admin_forums')
  67. def action_resync(self, items, checked):
  68. clean_checked = []
  69. for item in items:
  70. if item.pk in checked and item.type == 'forum':
  71. clean_checked.append(item.pk)
  72. if not clean_checked:
  73. return Message(_('Only forums can be resynchronized.'), 'error'), reverse('admin_forums')
  74. self.request.session['sync_forums'] = clean_checked
  75. return Message('Meh', 'success'), django_reverse('admin_forums_resync')
  76. def resync_forums(request, forum=0, progress=0):
  77. progress = int(progress)
  78. forums = request.session.get('sync_forums')
  79. if not forums:
  80. request.messages.set_flash(Message(_('No forums to resynchronize.')), 'info', 'forums')
  81. return redirect(reverse('admin_forums'))
  82. try:
  83. if not forum:
  84. forum = request.session['sync_forums'].pop()
  85. forum = Forum.objects.get(id=forum)
  86. except Forum.DoesNotExist:
  87. del request.session['sync_forums']
  88. request.messages.set_flash(Message(_('Forum for resynchronization does not exist.')), 'error', 'forums')
  89. return redirect(reverse('admin_forums'))
  90. # Sync 50 threads
  91. threads_total = forum.thread_set.count()
  92. for thread in forum.thread_set.all()[progress:(progress+1)]:
  93. thread.sync()
  94. thread.save(force_update=True)
  95. progress += 1
  96. if not threads_total:
  97. return redirect(django_reverse('admin_forums_resync'))
  98. # Render Progress
  99. response = render_to_response('processing.html',
  100. {
  101. 'task_name': _('Resynchronizing Forums'),
  102. 'target_name': forum.name,
  103. 'message': _('Resynchronized %(progress)s from %(total)s threads') % {'progress': progress, 'total': threads_total},
  104. 'progress': progress * 100 / threads_total,
  105. 'cancel_link': reverse('admin_forums'),
  106. },
  107. context_instance=RequestContext(request));
  108. # Redirect where to?
  109. if progress >= threads_total:
  110. forum.sync()
  111. forum.save(force_update=True)
  112. response['refresh'] = '2;url=%s' % django_reverse('admin_forums_resync')
  113. else:
  114. response['refresh'] = '2;url=%s' % django_reverse('admin_forums_resync', kwargs={'forum': forum.pk, 'progress': progress})
  115. return response
  116. class NewNode(FormWidget):
  117. admin = site.get_action('forums')
  118. id = 'new'
  119. fallback = 'admin_forums'
  120. form = NewNodeForm
  121. submit_button = _("Save Node")
  122. def get_new_link(self, model):
  123. return reverse('admin_forums_new')
  124. def get_edit_link(self, model):
  125. return reverse('admin_forums_edit', model)
  126. def get_initial_data(self, model):
  127. print 'CALL!'
  128. if not self.request.session.get('forums_admin_preffs'):
  129. print 'NO PATTERN!'
  130. return {}
  131. ref = self.request.META.get('HTTP_REFERER')
  132. if ref:
  133. parsed = urlparse(self.request.META.get('HTTP_REFERER'));
  134. try:
  135. link = resolve(parsed.path)
  136. if not link.url_name == 'admin_forums_new':
  137. return {}
  138. except Http404:
  139. return {}
  140. try:
  141. init = self.request.session.get('forums_admin_preffs')
  142. del self.request.session['forums_admin_preffs']
  143. return {
  144. 'parent': Forum.objects.get(id=init['parent']),
  145. 'perms': Forum.objects.get(id=init['perms']) if init['perms'] else None,
  146. 'role': init['role'],
  147. }
  148. except (KeyError, Forum.DoesNotExist):
  149. return {}
  150. def submit_form(self, form, target):
  151. new_forum = Forum(
  152. name=form.cleaned_data['name'],
  153. slug=slugify(form.cleaned_data['name']),
  154. type=form.cleaned_data['role'],
  155. attrs=form.cleaned_data['attrs'],
  156. style=form.cleaned_data['style'],
  157. )
  158. new_forum.set_description(form.cleaned_data['description'])
  159. if form.cleaned_data['role'] == 'redirect':
  160. new_forum.redirect = form.cleaned_data['redirect']
  161. else:
  162. new_forum.closed = form.cleaned_data['closed']
  163. new_forum.show_details = form.cleaned_data['show_details']
  164. new_forum.insert_at(form.cleaned_data['parent'], position='last-child', save=True)
  165. Forum.objects.populate_tree(True)
  166. if form.cleaned_data['perms']:
  167. new_forum.copy_permissions(form.cleaned_data['perms'])
  168. with UpdatingMonitor() as cm:
  169. monitor.increase('acl_version')
  170. self.request.session['forums_admin_preffs'] = {
  171. 'parent': form.cleaned_data['parent'].pk,
  172. 'perms': form.cleaned_data['perms'].pk if form.cleaned_data['perms'] else None,
  173. 'role': form.cleaned_data['role'],
  174. }
  175. if form.cleaned_data['role'] == 'category':
  176. return new_forum, Message(_('New Category has been created.'), 'success')
  177. if form.cleaned_data['role'] == 'forum':
  178. return new_forum, Message(_('New Forum has been created.'), 'success')
  179. if form.cleaned_data['role'] == 'redirect':
  180. return new_forum, Message(_('New Redirect has been created.'), 'success')
  181. class Up(ButtonWidget):
  182. admin = site.get_action('forums')
  183. id = 'up'
  184. fallback = 'admin_forums'
  185. notfound_message = _('Requested Forum could not be found.')
  186. def action(self, target):
  187. previous_sibling = target.get_previous_sibling()
  188. if previous_sibling:
  189. target.move_to(previous_sibling, 'left')
  190. return Message(_('Forum "%(name)s" has been moved up.') % {'name': target.name}, 'success'), False
  191. return Message(_('Forum "%(name)s" is first child of its parent node and cannot be moved up.') % {'name': target.name}, 'info'), False
  192. class Down(ButtonWidget):
  193. admin = site.get_action('forums')
  194. id = 'down'
  195. fallback = 'admin_forums'
  196. notfound_message = _('Requested Forum could not be found.')
  197. def action(self, target):
  198. next_sibling = target.get_next_sibling()
  199. if next_sibling:
  200. target.move_to(next_sibling, 'right')
  201. return Message(_('Forum "%(name)s" has been moved down.') % {'name': target.name}, 'success'), False
  202. return Message(_('Forum "%(name)s" is last child of its parent node and cannot be moved down.') % {'name': target.name}, 'info'), False
  203. class Edit(FormWidget):
  204. admin = site.get_action('forums')
  205. id = 'edit'
  206. name = _("Edit Forum")
  207. fallback = 'admin_forums'
  208. form = ForumForm
  209. target_name = 'name'
  210. notfound_message = _('Requested Forum could not be found.')
  211. submit_fallback = True
  212. def get_link(self, model):
  213. return reverse('admin_forums_edit', model)
  214. def get_edit_link(self, model):
  215. return self.get_link(model)
  216. def get_form(self, target):
  217. if target.type == 'category':
  218. self.name = _("Edit Category")
  219. self.form = CategoryForm
  220. if target.type == 'redirect':
  221. self.name = _("Edit Redirect")
  222. self.form = RedirectForm
  223. return self.form
  224. def get_form_instance(self, form, target, initial, post=False):
  225. form_inst = super(Edit, self).get_form_instance(form, target, initial, post)
  226. valid_targets = Forum.objects.get(special='root').get_descendants(include_self=target.type == 'category').exclude(Q(lft__gte=target.lft) & Q(rght__lte=target.rght))
  227. if target.role == 'category':
  228. label = _("Category Parent")
  229. if target.role == 'forum':
  230. label = _("Forum Parent")
  231. if target.role == 'redirect':
  232. label = _("Redirect Parent")
  233. form_inst.fields['parent'] = TreeNodeChoiceField(label=label, widget=forms.Select,
  234. queryset=valid_targets, level_indicator=u'- - ')
  235. form_inst.target_forum = target
  236. return form_inst
  237. def get_initial_data(self, model):
  238. initial = {
  239. 'parent': model.parent,
  240. 'name': model.name,
  241. 'description': model.description,
  242. }
  243. if model.type == 'redirect':
  244. initial['redirect'] = model.redirect
  245. else:
  246. initial['attrs'] = model.attrs
  247. initial['show_details'] = model.show_details
  248. initial['style'] = model.style
  249. initial['closed'] = model.closed
  250. if model.type == 'forum':
  251. initial['prune_start'] = model.prune_start
  252. initial['prune_last'] = model.prune_last
  253. initial['pruned_archive'] = model.pruned_archive
  254. return initial
  255. def submit_form(self, form, target):
  256. target.name = form.cleaned_data['name']
  257. target.slug = slugify(form.cleaned_data['name'])
  258. target.set_description(form.cleaned_data['description'])
  259. if target.type == 'redirect':
  260. target.redirect = form.cleaned_data['redirect']
  261. else:
  262. target.attrs = form.cleaned_data['attrs']
  263. target.show_details = form.cleaned_data['show_details']
  264. target.style = form.cleaned_data['style']
  265. target.closed = form.cleaned_data['closed']
  266. if target.type == 'forum':
  267. target.prune_start = form.cleaned_data['prune_start']
  268. target.prune_last = form.cleaned_data['prune_last']
  269. target.pruned_archive = form.cleaned_data['pruned_archive']
  270. target.save(force_update=True)
  271. Forum.objects.populate_tree(True)
  272. if form.cleaned_data['perms']:
  273. target.copy_permissions(form.cleaned_data['perms'])
  274. with UpdatingMonitor() as cm:
  275. if form.cleaned_data['parent'].pk != target.parent.pk:
  276. target.move_to(form.cleaned_data['parent'], 'last-child')
  277. monitor.increase('acl_version')
  278. if form.cleaned_data['parent'].pk != target.parent.pk or form.cleaned_data['perms']:
  279. monitor.increase('acl_version')
  280. if self.original_name != target.name:
  281. target.sync_name()
  282. return target, Message(_('Changes in forum "%(name)s" have been saved.') % {'name': self.original_name}, 'success')
  283. class Delete(FormWidget):
  284. admin = site.get_action('forums')
  285. id = 'delete'
  286. name = _("Delete Forum")
  287. fallback = 'admin_forums'
  288. template = 'delete'
  289. form = DeleteForm
  290. target_name = 'name'
  291. notfound_message = _('Requested Forum could not be found.')
  292. submit_fallback = True
  293. def get_link(self, model):
  294. return reverse('admin_forums_delete', model)
  295. def get_form(self, target):
  296. if target.type == 'category':
  297. self.name = _("Delete Category")
  298. if target.type == 'redirect':
  299. self.name = _("Delete Redirect")
  300. return self.form
  301. def get_form_instance(self, form, target, initial, post=False):
  302. if post:
  303. form_inst = form(self.request.POST, forum=target, request=self.request, initial=self.get_initial_data(target))
  304. else:
  305. form_inst = form(forum=target, request=self.request, initial=self.get_initial_data(target))
  306. if target.type != 'forum':
  307. del form_inst.fields['contents']
  308. valid_targets = Forum.objects.get(special='root').get_descendants().exclude(Q(lft__gte=target.lft) & Q(rght__lte=target.rght))
  309. form_inst.fields['subforums'] = TreeNodeChoiceField(label=_("Move subforums to"), widget=forms.Select,
  310. queryset=valid_targets, required=False, empty_label=_("Remove with forum"), level_indicator=u'- - ')
  311. return form_inst
  312. def submit_form(self, form, target):
  313. if target.type == 'forum':
  314. new_forum = form.cleaned_data['contents']
  315. if new_forum:
  316. target.move_content(new_forum)
  317. new_forum.sync()
  318. new_forum.save(force_update=True)
  319. new_parent = form.cleaned_data['subforums']
  320. if new_parent:
  321. for child in target.get_descendants():
  322. if child.parent_id == target.pk:
  323. child.move_to(new_parent, 'last-child')
  324. child.save(force_update=True)
  325. else:
  326. for child in target.get_descendants().order_by('-lft'):
  327. Forum.objects.get(id=child.pk).delete()
  328. Forum.objects.get(id=target.pk).delete()
  329. Forum.objects.populate_tree(True)
  330. with UpdatingMonitor() as cm:
  331. monitor.increase('acl_version')
  332. return target, Message(_('Forum "%(name)s" has been deleted.') % {'name': self.original_name}, 'success')