forms.py 11 KB

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