]> git.openstreetmap.org Git - osqa.git/blob - forum/forms/qanda.py
Work on issue #OSQA-510. Added support for ReCaptcha for users with low reputation...
[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.recaptcha.formfield import ReCaptchaField
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.widget = forms.TextInput(attrs={'size' : 70, 'autocomplete' : 'off'})
21         self.max_length = 255
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             self.fields['captcha'] = ReCaptchaField()
163
164         if settings.WIKI_ON:
165             self.fields['wiki'] = WikiField()
166
167 class AnswerForm(forms.Form):
168     text   = AnswerEditorField()
169     wiki   = WikiField()
170
171     def __init__(self, data=None, user=None, *args, **kwargs):
172         super(AnswerForm, self).__init__(data, *args, **kwargs)
173         
174         if int(user.reputation) < settings.CAPTCHA_IF_REP_LESS_THAN and not (user.is_superuser or user.is_staff):
175             self.fields['captcha'] = ReCaptchaField()
176
177         if settings.WIKI_ON:
178             self.fields['wiki'] = WikiField()
179
180 class RetagQuestionForm(forms.Form):
181     tags   = TagNamesField()
182     # initialize the default values
183     def __init__(self, question, *args, **kwargs):
184         super(RetagQuestionForm, self).__init__(*args, **kwargs)
185         self.fields['tags'].initial = question.tagnames
186
187 class RevisionForm(forms.Form):
188     """
189     Lists revisions of a Question or Answer
190     """
191     revision = forms.ChoiceField(widget=forms.Select(attrs={'style' : 'width:520px'}))
192
193     def __init__(self, post, *args, **kwargs):
194         super(RevisionForm, self).__init__(*args, **kwargs)
195
196         revisions = post.revisions.all().values_list('revision', 'author__username', 'revised_at', 'summary').order_by('-revised_at')
197
198         date_format = '%c'
199         self.fields['revision'].choices = [
200             (r[0], u'%s - %s (%s) %s' % (r[0], r[1], r[2].strftime(date_format), r[3]))
201             for r in revisions]
202
203         self.fields['revision'].initial = post.active_revision.revision
204
205 class EditQuestionForm(forms.Form):
206     title  = TitleField()
207     text   = QuestionEditorField()
208     summary = SummaryField()
209
210     def __init__(self, question, user, revision=None, *args, **kwargs):
211         super(EditQuestionForm, self).__init__(*args, **kwargs)
212
213         if revision is None:
214             revision = question.active_revision
215
216         self.fields['title'].initial = revision.title
217         self.fields['text'].initial = revision.body
218
219         self.fields['tags'] = TagNamesField(user)
220         self.fields['tags'].initial = revision.tagnames
221
222         if int(user.reputation) < settings.CAPTCHA_IF_REP_LESS_THAN and not (user.is_superuser or user.is_staff):
223             self.fields['captcha'] = ReCaptchaField()
224
225         if settings.WIKI_ON:
226             self.fields['wiki'] = WikiField(disabled=(question.nis.wiki and not user.can_cancel_wiki(question)), initial=question.nis.wiki)
227
228 class EditAnswerForm(forms.Form):
229     text = AnswerEditorField()
230     summary = SummaryField()
231
232     def __init__(self, answer, user, revision=None, *args, **kwargs):
233         super(EditAnswerForm, self).__init__(*args, **kwargs)
234
235         if revision is None:
236             revision = answer.active_revision
237
238         self.fields['text'].initial = revision.body
239
240         if int(user.reputation) < settings.CAPTCHA_IF_REP_LESS_THAN and not (user.is_superuser or user.is_staff):
241             self.fields['captcha'] = ReCaptchaField()
242         
243         if settings.WIKI_ON:
244             self.fields['wiki'] = WikiField(disabled=(answer.nis.wiki and not user.can_cancel_wiki(answer)), initial=answer.nis.wiki)
245
246 class EditUserForm(forms.Form):
247     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}))
248     realname = forms.CharField(label=_('Real name'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
249     website = forms.URLField(label=_('Website'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
250     city = forms.CharField(label=_('Location'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
251     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}))
252     about = forms.CharField(label=_('Profile'), required=False, widget=forms.Textarea(attrs={'cols' : 60}))
253
254     def __init__(self, user, *args, **kwargs):
255         super(EditUserForm, self).__init__(*args, **kwargs)
256         if settings.EDITABLE_SCREEN_NAME:
257             self.fields['username'] = UserNameField(label=_('Screen name'))
258             self.fields['username'].initial = user.username
259             self.fields['username'].user_instance = user
260         self.fields['email'].initial = user.email
261         self.fields['realname'].initial = user.real_name
262         self.fields['website'].initial = user.website
263         self.fields['city'].initial = user.location
264
265         if user.date_of_birth is not None:
266             self.fields['birthday'].initial = user.date_of_birth
267         else:
268             self.fields['birthday'].initial = '1990-01-01'
269         self.fields['about'].initial = user.about
270         self.user = user
271
272     def clean_email(self):
273         if self.user.email != self.cleaned_data['email']:
274             if settings.EMAIL_UNIQUE == True:
275                 if 'email' in self.cleaned_data:
276                     from forum.models import User
277                     try:
278                         User.objects.get(email = self.cleaned_data['email'])
279                     except User.DoesNotExist:
280                         return self.cleaned_data['email']
281                     except User.MultipleObjectsReturned:
282                         logging.error("Found multiple users sharing the same email: %s" % self.cleaned_data['email'])
283                         
284                     raise forms.ValidationError(_('this email has already been registered, please use another one'))
285         return self.cleaned_data['email']
286         
287
288 NOTIFICATION_CHOICES = (
289     ('i', _('Instantly')),
290     #('d', _('Daily')),
291     #('w', _('Weekly')),
292     ('n', _('No notifications')),
293 )
294
295 class SubscriptionSettingsForm(forms.ModelForm):
296     enable_notifications = forms.BooleanField(widget=forms.HiddenInput, required=False)
297     member_joins = forms.ChoiceField(widget=forms.RadioSelect, choices=NOTIFICATION_CHOICES)
298     new_question = forms.ChoiceField(widget=forms.RadioSelect, choices=NOTIFICATION_CHOICES)
299     new_question_watched_tags = forms.ChoiceField(widget=forms.RadioSelect, choices=NOTIFICATION_CHOICES)
300     subscribed_questions = forms.ChoiceField(widget=forms.RadioSelect, choices=NOTIFICATION_CHOICES)
301
302     class Meta:
303         model = SubscriptionSettings
304
305 class UserPreferencesForm(forms.Form):
306     sticky_sorts = forms.BooleanField(required=False, initial=False)
307
308
309