forms.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. from django.db import models
  2. from django.core.validators import URLValidator
  3. from django.utils.html import conditional_escape, mark_safe
  4. from django.utils.translation import ugettext_lazy as _
  5. from mptt.forms import TreeNodeChoiceField as TreeNodeChoiceField
  6. from misago.core import forms
  7. from misago.core.validators import validate_sluggable
  8. from misago.forums.models import Forum
  9. class ForumChoiceField(TreeNodeChoiceField):
  10. def __init__(self, *args, **kwargs):
  11. self.base_level = kwargs.pop('base_level', 1)
  12. kwargs['level_indicator'] = kwargs.get('level_indicator', '- - ')
  13. super(ForumChoiceField, self).__init__(*args, **kwargs)
  14. def _get_level_indicator(self, obj):
  15. level = getattr(obj, obj._mptt_meta.level_attr) - self.base_level
  16. if level > 0:
  17. return mark_safe(conditional_escape(self.level_indicator) * level)
  18. else:
  19. return ''
  20. FORUM_ROLES = (
  21. ('category', _('Category')),
  22. ('forum', _('Forum')),
  23. ('redirect', _('Redirect')),
  24. )
  25. class ForumFormBase(forms.ModelForm):
  26. role = forms.ChoiceField(label=_("Type"), choices=FORUM_ROLES)
  27. name = forms.CharField(
  28. label=_("Name"),
  29. validators=[validate_sluggable()],
  30. help_text=_('Short and descriptive name of all users with this rank. '
  31. '"The Team" or "Game Masters" are good examples.'))
  32. description = forms.CharField(
  33. label=_("Description"), max_length=2048, required=False,
  34. widget=forms.Textarea(attrs={'rows': 3}),
  35. help_text=_("Optional description explaining forum's intented "
  36. "purpose."))
  37. redirect_url = forms.URLField(
  38. label=_("Redirect URL"),
  39. validators=[validate_sluggable()],
  40. help_text=_('If forum type is redirect, enter here its URL.'),
  41. required=False)
  42. css_class = forms.CharField(
  43. label=_("CSS class"), required=False,
  44. help_text=_("Optional CSS class used to customize this forum "
  45. "appearance from templates."))
  46. is_closed = forms.YesNoSwitch(
  47. label=_("Closed forum"), required=False,
  48. help_text=_("Only members with valid permissions can post in "
  49. "closed forums."))
  50. css_class = forms.CharField(
  51. label=_("CSS class"), required=False,
  52. help_text=_("Optional CSS class used to customize this forum "
  53. "appearance from templates."))
  54. prune_started_after = forms.IntegerField(
  55. label=_("Prune thread if number of days since its creation is "
  56. "greater than"), min_value=0,
  57. help_text=_("Enter 0 to disable this pruning criteria."))
  58. prune_replied_after = forms.IntegerField(
  59. label=_("Prune thread if number of days since last reply is greater "
  60. "than"), min_value=0,
  61. help_text=_("Enter 0 to disable this pruning criteria."))
  62. class Meta:
  63. model = Forum
  64. fields = [
  65. 'role',
  66. 'name',
  67. 'description',
  68. 'redirect_url',
  69. 'css_class',
  70. 'is_closed',
  71. 'prune_started_after',
  72. 'prune_replied_after',
  73. 'archive_pruned_in',
  74. ]
  75. def clean_copy_permissions(self):
  76. data = self.cleaned_data['copy_permissions']
  77. if data and data.pk == self.instance.pk:
  78. message = _("Permissions cannot be copied from forum into itself.")
  79. raise forms.ValidationError(message)
  80. return data
  81. def clean_archive_pruned_in(self):
  82. data = self.cleaned_data['archive_pruned_in']
  83. if data and data.pk == self.instance.pk:
  84. message = _("Forum cannot act as archive for itself.")
  85. raise forms.ValidationError(message)
  86. return data
  87. def clean(self):
  88. data = super(ForumFormBase, self).clean()
  89. self.instance.set_name(data.get('name'))
  90. self.instance.set_description(data.get('description'))
  91. if data['role'] != 'category':
  92. if not data['new_parent'].level:
  93. message = _("Only categories can have no parent category.")
  94. raise forms.ValidationError(message)
  95. if data['role'] == 'redirect':
  96. if not data.get('redirect'):
  97. message = _("This forum is redirect, yet you haven't "
  98. "specified URL to which it should redirect "
  99. "after click.")
  100. raise forms.ValidationError(message)
  101. return data
  102. def ForumFormFactory(instance):
  103. parent_queryset = Forum.objects.all_forums(True).order_by('lft')
  104. if instance.pk:
  105. not_siblings = models.Q(lft__lt=instance.lft)
  106. not_siblings = not_siblings | models.Q(rght__gt=instance.rght)
  107. parent_queryset = parent_queryset.filter(not_siblings)
  108. return type('TreeNodeChoiceField', (ForumFormBase,), {
  109. 'new_parent': ForumChoiceField(
  110. label=_("Parent forum"),
  111. queryset=parent_queryset,
  112. initial=instance.parent,
  113. empty_label=None),
  114. 'copy_permissions': ForumChoiceField(
  115. label=_("Copy permissions"),
  116. help_text=_("You can override this forum permissions with "
  117. "permissions of other forum selected here."),
  118. queryset=Forum.objects.all_forums(),
  119. empty_label=_("Don't copy permissions"),
  120. base_level=1,
  121. required=False),
  122. 'archive_pruned_in': ForumChoiceField(
  123. label=_("Pruned threads archive"),
  124. help_text=_("Instead of being deleted, pruned threads can be "
  125. "moved to designated forum."),
  126. queryset=Forum.objects.all_forums(),
  127. empty_label=_("Don't archive pruned threads"),
  128. base_level=1,
  129. required=False),
  130. })