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