forms.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. from django import forms
  2. from django.conf import settings
  3. from django.utils.translation import ungettext, ugettext_lazy as _
  4. from misago.acl.exceptions import ACLError403, ACLError404
  5. from misago.forms import Form, ForumChoiceField
  6. from misago.models import Forum, Thread
  7. from misago.utils.strings import slugify
  8. from misago.validators import validate_sluggable
  9. class ThreadNameMixin(object):
  10. def clean_thread_name(self):
  11. data = self.cleaned_data['thread_name']
  12. slug = slugify(data)
  13. if len(slug) < self.request.settings['thread_name_min']:
  14. raise forms.ValidationError(ungettext(
  15. "Thread name must contain at least one alpha-numeric character.",
  16. "Thread name must contain at least %(count)d alpha-numeric characters.",
  17. self.request.settings['thread_name_min']
  18. ) % {'count': self.request.settings['thread_name_min']})
  19. if len(data) > self.request.settings['thread_name_max']:
  20. raise forms.ValidationError(ungettext(
  21. "Thread name cannot be longer than %(count)d character.",
  22. "Thread name cannot be longer than %(count)d characters.",
  23. self.request.settings['thread_name_max']
  24. ) % {'count': self.request.settings['thread_name_max']})
  25. return data
  26. class PostForm(Form, ThreadNameMixin):
  27. post = forms.CharField(widget=forms.Textarea)
  28. def __init__(self, data=None, file=None, request=None, mode=None, *args, **kwargs):
  29. self.mode = mode
  30. super(PostForm, self).__init__(data, file, request=request, *args, **kwargs)
  31. def finalize_form(self):
  32. self.layout = [
  33. [
  34. None,
  35. [
  36. ('thread_name', {'label': _("Thread Name")}),
  37. ('edit_reason', {'label': _("Edit Reason")}),
  38. ('post', {'label': _("Post Content")}),
  39. ],
  40. ],
  41. ]
  42. if self.mode in ['edit_thread', 'edit_post']:
  43. self.fields['edit_reason'] = forms.CharField(max_length=255, required=False, help_text=_("Optional reason for changing this post."))
  44. else:
  45. del self.layout[0][1][1]
  46. if self.mode not in ['edit_thread', 'new_thread']:
  47. del self.layout[0][1][0]
  48. else:
  49. self.fields['thread_name'] = forms.CharField(
  50. max_length=self.request.settings['thread_name_max'],
  51. validators=[validate_sluggable(
  52. _("Thread name must contain at least one alpha-numeric character."),
  53. _("Thread name is too long. Try shorter name.")
  54. )])
  55. def clean_post(self):
  56. data = self.cleaned_data['post']
  57. if len(data) < self.request.settings['post_length_min']:
  58. raise forms.ValidationError(ungettext(
  59. "Post content cannot be empty.",
  60. "Post content cannot be shorter than %(count)d characters.",
  61. self.request.settings['post_length_min']
  62. ) % {'count': self.request.settings['post_length_min']})
  63. return data
  64. class SplitThreadForm(Form, ThreadNameMixin):
  65. def finalize_form(self):
  66. self.layout = [
  67. [
  68. None,
  69. [
  70. ('thread_name', {'label': _("New Thread Name")}),
  71. ('thread_forum', {'label': _("New Thread Forum")}),
  72. ],
  73. ],
  74. ]
  75. self.fields['thread_name'] = forms.CharField(
  76. max_length=self.request.settings['thread_name_max'],
  77. validators=[validate_sluggable(
  78. _("Thread name must contain at least one alpha-numeric character."),
  79. _("Thread name is too long. Try shorter name.")
  80. )])
  81. self.fields['thread_forum'] = ForumChoiceField(queryset=Forum.tree.get(token='root').get_descendants().filter(pk__in=self.request.acl.forums.acl['can_browse']))
  82. def clean_thread_forum(self):
  83. new_forum = self.cleaned_data['thread_forum']
  84. # Assert its forum and its not current forum
  85. if new_forum.type != 'forum':
  86. raise forms.ValidationError(_("This is not a forum."))
  87. return new_forum
  88. class MovePostsForm(Form, ThreadNameMixin):
  89. error_source = 'thread_url'
  90. def __init__(self, data=None, request=None, thread=None, *args, **kwargs):
  91. self.thread = thread
  92. super(MovePostsForm, self).__init__(data, request=request, *args, **kwargs)
  93. def finalize_form(self):
  94. self.layout = [
  95. [
  96. None,
  97. [
  98. ('thread_url', {'label': _("New Thread Link"), 'help_text': _("To select new thread, simply copy and paste here its link.")}),
  99. ],
  100. ],
  101. ]
  102. self.fields['thread_url'] = forms.CharField()
  103. def clean_thread_url(self):
  104. from django.core.urlresolvers import resolve
  105. from django.http import Http404
  106. thread_url = self.cleaned_data['thread_url']
  107. try:
  108. thread_url = thread_url[len(settings.BOARD_ADDRESS):]
  109. match = resolve(thread_url)
  110. thread = Thread.objects.get(pk=match.kwargs['thread'])
  111. self.request.acl.threads.allow_thread_view(self.request.user, thread)
  112. if thread.pk == self.thread.pk:
  113. raise forms.ValidationError(_("New thread is same as current one."))
  114. return thread
  115. except (Http404, KeyError):
  116. raise forms.ValidationError(_("This is not a correct thread URL."))
  117. except (Thread.DoesNotExist, ACLError403, ACLError404):
  118. raise forms.ValidationError(_("Thread could not be found."))
  119. class QuickReplyForm(Form):
  120. post = forms.CharField(widget=forms.Textarea)
  121. class MoveThreadsForm(Form):
  122. error_source = 'new_forum'
  123. def __init__(self, data=None, request=None, forum=None, *args, **kwargs):
  124. self.forum = forum
  125. super(MoveThreadsForm, self).__init__(data, request=request, *args, **kwargs)
  126. def finalize_form(self):
  127. self.fields['new_forum'] = ForumChoiceField(queryset=Forum.tree.get(token='root').get_descendants().filter(pk__in=self.request.acl.forums.acl['can_browse']))
  128. self.layout = [
  129. [
  130. None,
  131. [
  132. ('new_forum', {'label': _("Move Threads to"), 'help_text': _("Select forum you want to move threads to.")}),
  133. ],
  134. ],
  135. ]
  136. def clean_new_forum(self):
  137. new_forum = self.cleaned_data['new_forum']
  138. # Assert its forum and its not current forum
  139. if new_forum.type != 'forum':
  140. raise forms.ValidationError(_("This is not forum."))
  141. if new_forum.pk == self.forum.pk:
  142. raise forms.ValidationError(_("New forum is same as current one."))
  143. return new_forum
  144. class MergeThreadsForm(Form, ThreadNameMixin):
  145. def __init__(self, data=None, request=None, threads=[], *args, **kwargs):
  146. self.threads = threads
  147. super(MergeThreadsForm, self).__init__(data, request=request, *args, **kwargs)
  148. def finalize_form(self):
  149. self.fields['new_forum'] = ForumChoiceField(queryset=Forum.tree.get(token='root').get_descendants().filter(pk__in=self.request.acl.forums.acl['can_browse']), initial=self.threads[0].forum)
  150. self.fields['thread_name'] = forms.CharField(
  151. max_length=self.request.settings['thread_name_max'],
  152. initial=self.threads[0].name,
  153. validators=[validate_sluggable(
  154. _("Thread name must contain at least one alpha-numeric character."),
  155. _("Thread name is too long. Try shorter name.")
  156. )])
  157. self.layout = [
  158. [
  159. None,
  160. [
  161. ('thread_name', {'label': _("Thread Name"), 'help_text': _("Name of new thread that will be created as result of merge.")}),
  162. ('new_forum', {'label': _("Thread Forum"), 'help_text': _("Select forum you want to put new thread in.")}),
  163. ],
  164. ],
  165. [
  166. _("Merge Order"),
  167. [
  168. ],
  169. ],
  170. ]
  171. choices = []
  172. for i, thread in enumerate(self.threads):
  173. choices.append((str(i), i + 1))
  174. for i, thread in enumerate(self.threads):
  175. self.fields['thread_%s' % thread.pk] = forms.ChoiceField(choices=choices, initial=str(i))
  176. self.layout[1][1].append(('thread_%s' % thread.pk, {'label': thread.name}))
  177. def clean_new_forum(self):
  178. new_forum = self.cleaned_data['new_forum']
  179. # Assert its forum
  180. if new_forum.type != 'forum':
  181. raise forms.ValidationError(_("This is not forum."))
  182. return new_forum
  183. def clean(self):
  184. cleaned_data = super(MergeThreadsForm, self).clean()
  185. self.merge_order = {}
  186. lookback = []
  187. for thread in self.threads:
  188. order = int(cleaned_data['thread_%s' % thread.pk])
  189. if order in lookback:
  190. raise forms.ValidationError(_("One or more threads have same position in merge order."))
  191. lookback.append(order)
  192. self.merge_order[order] = thread
  193. return cleaned_data