]> git.openstreetmap.org Git - osqa.git/blob - forum/forms/qanda.py
merge notification-config -> trunk, previous commits 694, 695, 702
[osqa.git] / forum / forms / qanda.py
1 import re
2 from datetime import date
3 from django import forms
4 from forum.models import *
5 from django.utils.translation import ugettext as _
6 from django.contrib.humanize.templatetags.humanize import apnumber
7
8 from django.utils.safestring import mark_safe
9 from general import NextUrlField, UserNameField, SetPasswordForm
10 from forum import settings
11
12 from forum.modules import call_all_handlers
13
14 import logging
15
16 class TitleField(forms.CharField):
17     def __init__(self, *args, **kwargs):
18         super(TitleField, self).__init__(*args, **kwargs)
19         self.required = True
20         self.max_length = 255
21         self.widget = forms.TextInput(attrs={'size' : 70, 'autocomplete' : 'off', 'maxlength' : self.max_length})
22         self.label  = _('title')
23         self.help_text = _('please enter a descriptive title for your question')
24         self.initial = ''
25
26     def clean(self, value):
27         if len(value) < settings.FORM_MIN_QUESTION_TITLE:
28             raise forms.ValidationError(_('title must be must be at least %s characters') % settings.FORM_MIN_QUESTION_TITLE)
29
30         return value
31
32 class EditorField(forms.CharField):
33     def __init__(self, *args, **kwargs):
34         super(EditorField, self).__init__(*args, **kwargs)
35         self.widget = forms.Textarea(attrs={'id':'editor'})
36         self.label  = _('content')
37         self.help_text = u''
38         self.initial = ''
39
40
41 class QuestionEditorField(EditorField):
42     def __init__(self, *args, **kwargs):
43         super(QuestionEditorField, self).__init__(*args, **kwargs)
44         self.required = not bool(settings.FORM_EMPTY_QUESTION_BODY)
45
46
47     def clean(self, value):
48         if not bool(settings.FORM_EMPTY_QUESTION_BODY) and (len(re.sub('[ ]{2,}', ' ', value)) < settings.FORM_MIN_QUESTION_BODY):
49             raise forms.ValidationError(_('question content must be at least %s characters') % settings.FORM_MIN_QUESTION_BODY)
50
51         return value
52
53 class AnswerEditorField(EditorField):
54     def __init__(self, *args, **kwargs):
55         super(AnswerEditorField, self).__init__(*args, **kwargs)
56         self.required = True
57
58     def clean(self, value):
59         if len(re.sub('[ ]{2,}', ' ', value)) < settings.FORM_MIN_QUESTION_BODY:
60             raise forms.ValidationError(_('answer content must be at least %s characters') % settings.FORM_MIN_QUESTION_BODY)
61
62         return value
63
64
65 class TagNamesField(forms.CharField):
66     def __init__(self, user=None, *args, **kwargs):
67         super(TagNamesField, self).__init__(*args, **kwargs)
68         self.required = True
69         self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
70         self.max_length = 255
71         self.label  = _('tags')
72         #self.help_text = _('please use space to separate tags (this enables autocomplete feature)')
73         self.help_text = _('Tags are short keywords, with no spaces within. At least %(min)s and up to %(max)s tags can be used.') % {
74             'min': settings.FORM_MIN_NUMBER_OF_TAGS, 'max': settings.FORM_MAX_NUMBER_OF_TAGS    
75         }
76         self.initial = ''
77         self.user = user
78
79     def clean(self, value):
80         value = super(TagNamesField, self).clean(value)
81         data = value.strip().lower()
82
83         split_re = re.compile(r'[ ,]+')
84         list = {}
85         for tag in split_re.split(data):
86             list[tag] = tag
87
88         if len(list) > settings.FORM_MAX_NUMBER_OF_TAGS or len(list) < settings.FORM_MIN_NUMBER_OF_TAGS:
89             raise forms.ValidationError(_('please use between %(min)s and %(max)s tags') % { 'min': settings.FORM_MIN_NUMBER_OF_TAGS, 'max': settings.FORM_MAX_NUMBER_OF_TAGS})
90
91         list_temp = []
92         tagname_re = re.compile(r'^[\w+\.-]+$', re.UNICODE)
93         for key,tag in list.items():
94             if len(tag) > settings.FORM_MAX_LENGTH_OF_TAG or len(tag) < settings.FORM_MIN_LENGTH_OF_TAG:
95                 raise forms.ValidationError(_('please use between %(min)s and %(max)s characters in you tags') % { 'min': settings.FORM_MIN_LENGTH_OF_TAG, 'max': settings.FORM_MAX_LENGTH_OF_TAG})
96             if not tagname_re.match(tag):
97                 raise forms.ValidationError(_('please use following characters in tags: letters , numbers, and characters \'.-_\''))
98             # only keep one same tag
99             if tag not in list_temp and len(tag.strip()) > 0:
100                 list_temp.append(tag)
101
102         if settings.LIMIT_TAG_CREATION and not self.user.can_create_tags():
103             existent = Tag.objects.filter(name__in=list_temp).values_list('name', flat=True)
104
105             if len(existent) < len(list_temp):
106                 unexistent = [n for n in list_temp if not n in existent]
107                 raise forms.ValidationError(_("You don't have enough reputation to create new tags. The following tags do not exist yet: %s") %
108                         ', '.join(unexistent))
109
110
111         return u' '.join(list_temp)
112
113 class WikiField(forms.BooleanField):
114     def __init__(self, disabled=False, *args, **kwargs):
115         super(WikiField, self).__init__(*args, **kwargs)
116         self.required = False
117         self.label  = _('community wiki')
118         self.help_text = _('if you choose community wiki option, the question and answer do not generate points and name of author will not be shown')
119         if disabled:
120             self.widget=forms.CheckboxInput(attrs={'disabled': "disabled"})
121     def clean(self,value):
122         return value
123
124 class EmailNotifyField(forms.BooleanField):
125     def __init__(self, *args, **kwargs):
126         super(EmailNotifyField, self).__init__(*args, **kwargs)
127         self.required = False
128         self.widget.attrs['class'] = 'nomargin'
129
130 class SummaryField(forms.CharField):
131     def __init__(self, *args, **kwargs):
132         super(SummaryField, self).__init__(*args, **kwargs)
133         self.required = False
134         self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
135         self.max_length = 300
136         self.label  = _('update summary:')
137         self.help_text = _('enter a brief summary of your revision (e.g. fixed spelling, grammar, improved style, this field is optional)')
138
139
140 class FeedbackForm(forms.Form):
141     message = forms.CharField(label=_('Your message:'), max_length=800,widget=forms.Textarea(attrs={'cols':60}))
142     next = NextUrlField()
143
144     def __init__(self, user, *args, **kwargs):
145         super(FeedbackForm, self).__init__(*args, **kwargs)
146         if not user.is_authenticated():
147             self.fields['name'] = forms.CharField(label=_('Your name:'), required=False)
148             self.fields['email'] = forms.EmailField(label=_('Email (not shared with anyone):'), required=True)
149
150
151
152 class AskForm(forms.Form):
153     title  = TitleField()
154     text   = QuestionEditorField()
155
156     def __init__(self, data=None, user=None, *args, **kwargs):
157         super(AskForm, self).__init__(data, *args, **kwargs)
158
159         self.fields['tags']   = TagNamesField(user)
160         
161         if int(user.reputation) < settings.CAPTCHA_IF_REP_LESS_THAN and not (user.is_superuser or user.is_staff):
162             spam_fields = call_all_handlers('create_anti_spam_field')
163             if spam_fields:
164                 spam_fields = dict(spam_fields)
165                 for name, field in spam_fields.items():
166                     self.fields[name] = field
167
168                 self._anti_spam_fields = spam_fields.keys()
169             else:
170                 self._anti_spam_fields = []
171
172         if settings.WIKI_ON:
173             self.fields['wiki'] = WikiField()
174
175 class AnswerForm(forms.Form):
176     text   = AnswerEditorField()
177     wiki   = WikiField()
178
179     def __init__(self, data=None, user=None, *args, **kwargs):
180         super(AnswerForm, self).__init__(data, *args, **kwargs)
181         
182         if int(user.reputation) < settings.CAPTCHA_IF_REP_LESS_THAN and not (user.is_superuser or user.is_staff):
183             spam_fields = call_all_handlers('create_anti_spam_field')
184             if spam_fields:
185                 spam_fields = dict(spam_fields)
186                 for name, field in spam_fields.items():
187                     self.fields[name] = field
188
189                 self._anti_spam_fields = spam_fields.keys()
190             else:
191                 self._anti_spam_fields = []
192
193         if settings.WIKI_ON:
194             self.fields['wiki'] = WikiField()
195
196 class RetagQuestionForm(forms.Form):
197     tags   = TagNamesField()
198     # initialize the default values
199     def __init__(self, question, *args, **kwargs):
200         super(RetagQuestionForm, self).__init__(*args, **kwargs)
201         self.fields['tags'].initial = question.tagnames
202
203 class RevisionForm(forms.Form):
204     """
205     Lists revisions of a Question or Answer
206     """
207     revision = forms.ChoiceField(widget=forms.Select(attrs={'style' : 'width:520px'}))
208
209     def __init__(self, post, *args, **kwargs):
210         super(RevisionForm, self).__init__(*args, **kwargs)
211
212         revisions = post.revisions.all().values_list('revision', 'author__username', 'revised_at', 'summary').order_by('-revised_at')
213
214         date_format = '%c'
215         self.fields['revision'].choices = [
216             (r[0], u'%s - %s (%s) %s' % (r[0], r[1], r[2].strftime(date_format), r[3]))
217             for r in revisions]
218
219         self.fields['revision'].initial = post.active_revision.revision
220
221 class EditQuestionForm(forms.Form):
222     title  = TitleField()
223     text   = QuestionEditorField()
224     summary = SummaryField()
225
226     def __init__(self, question, user, revision=None, *args, **kwargs):
227         super(EditQuestionForm, self).__init__(*args, **kwargs)
228
229         if revision is None:
230             revision = question.active_revision
231
232         self.fields['title'].initial = revision.title
233         self.fields['text'].initial = revision.body
234
235         self.fields['tags'] = TagNamesField(user)
236         self.fields['tags'].initial = revision.tagnames
237
238         if int(user.reputation) < settings.CAPTCHA_IF_REP_LESS_THAN and not (user.is_superuser or user.is_staff):
239             spam_fields = call_all_handlers('create_anti_spam_field')
240             if spam_fields:
241                 spam_fields = dict(spam_fields)
242                 for name, field in spam_fields.items():
243                     self.fields[name] = field
244
245                 self._anti_spam_fields = spam_fields.keys()
246             else:
247                 self._anti_spam_fields = []
248
249         if settings.WIKI_ON:
250             self.fields['wiki'] = WikiField(disabled=(question.nis.wiki and not user.can_cancel_wiki(question)), initial=question.nis.wiki)
251
252 class EditAnswerForm(forms.Form):
253     text = AnswerEditorField()
254     summary = SummaryField()
255
256     def __init__(self, answer, user, revision=None, *args, **kwargs):
257         super(EditAnswerForm, self).__init__(*args, **kwargs)
258
259         if revision is None:
260             revision = answer.active_revision
261
262         self.fields['text'].initial = revision.body
263
264         if int(user.reputation) < settings.CAPTCHA_IF_REP_LESS_THAN and not (user.is_superuser or user.is_staff):
265             spam_fields = call_all_handlers('create_anti_spam_field')
266             if spam_fields:
267                 spam_fields = dict(spam_fields)
268                 for name, field in spam_fields.items():
269                     self.fields[name] = field
270
271                 self._anti_spam_fields = spam_fields.keys()
272             else:
273                 self._anti_spam_fields = []
274         
275         if settings.WIKI_ON:
276             self.fields['wiki'] = WikiField(disabled=(answer.nis.wiki and not user.can_cancel_wiki(answer)), initial=answer.nis.wiki)
277
278 class EditUserForm(forms.Form):
279     email = forms.EmailField(label=u'Email', help_text=_('this email does not have to be linked to gravatar'), required=True, max_length=75, widget=forms.TextInput(attrs={'size' : 35}))
280     realname = forms.CharField(label=_('Real name'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
281     website = forms.URLField(label=_('Website'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
282     city = forms.CharField(label=_('Location'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
283     birthday = forms.DateField(label=_('Date of birth'), help_text=_('will not be shown, used to calculate age, format: YYYY-MM-DD'), required=False, widget=forms.TextInput(attrs={'size' : 35}))
284     about = forms.CharField(label=_('Profile'), required=False, widget=forms.Textarea(attrs={'cols' : 60}))
285
286     def __init__(self, user, *args, **kwargs):
287         super(EditUserForm, self).__init__(*args, **kwargs)
288         if settings.EDITABLE_SCREEN_NAME:
289             self.fields['username'] = UserNameField(label=_('Screen name'))
290             self.fields['username'].initial = user.username
291             self.fields['username'].user_instance = user
292         self.fields['email'].initial = user.email
293         self.fields['realname'].initial = user.real_name
294         self.fields['website'].initial = user.website
295         self.fields['city'].initial = user.location
296
297         if user.date_of_birth is not None:
298             self.fields['birthday'].initial = user.date_of_birth
299         else:
300             self.fields['birthday'].initial = '1990-01-01'
301         self.fields['about'].initial = user.about
302         self.user = user
303
304     def clean_email(self):
305         if self.user.email != self.cleaned_data['email']:
306             if settings.EMAIL_UNIQUE == True:
307                 if 'email' in self.cleaned_data:
308                     from forum.models import User
309                     try:
310                         User.objects.get(email = self.cleaned_data['email'])
311                     except User.DoesNotExist:
312                         return self.cleaned_data['email']
313                     except User.MultipleObjectsReturned:
314                         logging.error("Found multiple users sharing the same email: %s" % self.cleaned_data['email'])
315                         
316                     raise forms.ValidationError(_('this email has already been registered, please use another one'))
317         return self.cleaned_data['email']
318         
319
320 NOTIFICATION_CHOICES = (
321     ('i', _('Instantly')),
322     #('d', _('Daily')),
323     #('w', _('Weekly')),
324     ('n', _('No notifications')),
325 )
326
327 class SubscriptionSettingsForm(forms.ModelForm):
328     enable_notifications = forms.BooleanField(widget=forms.HiddenInput, required=False)
329     member_joins = forms.ChoiceField(widget=forms.RadioSelect, choices=NOTIFICATION_CHOICES)
330     new_question = forms.ChoiceField(widget=forms.RadioSelect, choices=NOTIFICATION_CHOICES)
331     new_question_watched_tags = forms.ChoiceField(widget=forms.RadioSelect, choices=NOTIFICATION_CHOICES)
332     subscribed_questions = forms.ChoiceField(widget=forms.RadioSelect, choices=NOTIFICATION_CHOICES)
333
334     class Meta:
335         model = SubscriptionSettings
336
337 class UserPreferencesForm(forms.Form):
338     sticky_sorts = forms.BooleanField(required=False, initial=False)
339
340
341