diff options
-rw-r--r-- | askbot/doc/source/changelog.rst | 1 | ||||
-rw-r--r-- | askbot/doc/source/contributors.rst | 1 | ||||
-rw-r--r-- | askbot/forms.py | 613 | ||||
-rw-r--r-- | askbot/migrations/0126_add_field__auth_user__is_fake.py | 19 | ||||
-rw-r--r-- | askbot/models/__init__.py | 19 | ||||
-rw-r--r-- | askbot/skins/common/media/js/post.js | 27 | ||||
-rw-r--r-- | askbot/skins/common/media/js/utils.js | 1 | ||||
-rw-r--r-- | askbot/skins/common/templates/widgets/edit_post.html | 62 | ||||
-rw-r--r-- | askbot/skins/default/media/style/style.less | 26 | ||||
-rw-r--r-- | askbot/tests/form_tests.py | 27 | ||||
-rw-r--r-- | askbot/urls.py | 5 | ||||
-rw-r--r-- | askbot/utils/forms.py | 22 | ||||
-rw-r--r-- | askbot/views/commands.py | 23 | ||||
-rw-r--r-- | askbot/views/writers.py | 13 |
14 files changed, 592 insertions, 267 deletions
diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst index 430d98de..cbef16c3 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -3,6 +3,7 @@ Changes in Askbot Development version ------------------- +* Administrators can post under fictional user accounts without logging out (jtrain, Evgeny) * Welcome email for the case when replying by email is enabled (Evgeny) * Detection of email signature based on the response to the welcome email (Evgeny) * Hide "website" and "about" section of the blocked user profiles diff --git a/askbot/doc/source/contributors.rst b/askbot/doc/source/contributors.rst index 69348b84..ce8656ea 100644 --- a/askbot/doc/source/contributors.rst +++ b/askbot/doc/source/contributors.rst @@ -41,6 +41,7 @@ Programming and documentation * Silvio Heuberger * `Alexandros <https://github.com/alexandros-z>`_ * `Paul Backhouse <https://github.com/powlo>`_ +* `jtrain <https://github.com/jtrain>`_ Translations ------------ diff --git a/askbot/forms.py b/askbot/forms.py index 18fe7537..64337883 100644 --- a/askbot/forms.py +++ b/askbot/forms.py @@ -1,11 +1,13 @@ +"""Forms, custom form fields and related utility functions +used in AskBot""" import re from django import forms from askbot import const +from django.forms.util import ErrorList from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy, string_concat from django.utils.text import get_text_list from django.contrib.auth.models import User -from django.contrib.contenttypes.models import ContentType from django_countries import countries from askbot.utils.forms import NextUrlField, UserNameField from askbot.mail import extract_first_email_address @@ -13,6 +15,7 @@ from recaptcha_works.fields import RecaptchaField from askbot.conf import settings as askbot_settings import logging + def cleanup_dict(dictionary, key, empty_value): """deletes key from dictionary if it exists and the corresponding value equals the empty_value @@ -20,13 +23,27 @@ def cleanup_dict(dictionary, key, empty_value): if key in dictionary and dictionary[key] == empty_value: del dictionary[key] + def format_form_errors(form): + """Formats form errors in HTML + if there is only one error - returns a plain string + if more than one, returns an unordered list of errors + in HTML format. + If there are no errors, returns empty string + """ if form.errors: errors = form.errors.values() if len(errors) == 1: return errors[0] else: - return 'hahahahah' + result = '<ul>' + for error in errors: + result += '<li>%s</li>' % error + result += '</ul>' + return result + else: + return '' + def clean_marked_tagnames(tagnames): """return two strings - one containing tagnames @@ -35,7 +52,7 @@ def clean_marked_tagnames(tagnames): wildcard tags are those that have an asterisk at the end the function does not verify that the tag names are valid """ - if askbot_settings.USE_WILDCARD_TAGS == False: + if askbot_settings.USE_WILDCARD_TAGS is False: return tagnames, list() pure_tags = list() @@ -53,7 +70,8 @@ def clean_marked_tagnames(tagnames): return pure_tags, wildcards -def filter_choices(remove_choices = None, from_choices = None): + +def filter_choices(remove_choices=None, from_choices=None): """a utility function that will remove choice tuples usable for the forms.ChoicesField from ``from_choices``, the removed ones will be those given @@ -73,12 +91,47 @@ def filter_choices(remove_choices = None, from_choices = None): if choice == choice_to_test[0]: remove = True break - if remove == False: - filtered_choices += ( choice_to_test, ) + if remove is False: + filtered_choices += (choice_to_test, ) return filtered_choices -COUNTRY_CHOICES = (('unknown',_('select country')),) + countries.COUNTRIES + +def need_mandatory_tags(): + """true, if list of mandatory tags is not empty""" + from askbot import models + return ( + askbot_settings.TAGS_ARE_REQUIRED + and len(models.tag.get_mandatory_tags()) > 0 + ) + + +def mandatory_tag_missing_in_list(tag_strings): + """true, if mandatory tag is not present in the list + of ``tag_strings``""" + from askbot import models + mandatory_tags = models.tag.get_mandatory_tags() + for mandatory_tag in mandatory_tags: + for tag_string in tag_strings: + if tag_strings_match(tag_string, mandatory_tag): + return False + return True + + +def tag_strings_match(tag_string, mandatory_tag): + """true if tag string matches the mandatory tag, + the comparison is not symmetric if tag_string ends with a + wildcard (asterisk) + """ + if mandatory_tag.endswith('*'): + return tag_string.startswith(mandatory_tag[:-1]) + else: + return tag_string == mandatory_tag + + + +COUNTRY_CHOICES = (('unknown', _('select country')),) + countries.COUNTRIES + class CountryField(forms.ChoiceField): """this is better placed into the django_coutries app""" @@ -100,10 +153,13 @@ class CountryField(forms.ChoiceField): return None return value + class CountedWordsField(forms.CharField): - + """a field where a number of words is expected + to be in a certain range""" + def __init__( - self, min_words = 0, max_words = 9999, field_name = None, + self, min_words=0, max_words=9999, field_name=None, *args, **kwargs ): self.min_words = min_words @@ -144,28 +200,38 @@ class CountedWordsField(forms.CharField): class DomainNameField(forms.CharField): + """Field for Internet Domain Names + todo: maybe there is a standard field for this? + """ def clean(self, value): #find a better regex, taking into account tlds domain_re = re.compile(r'[a-zA-Z\d]+(\.[a-zA-Z\d]+)+') if domain_re.match(value): return value else: - raise forms.ValidationError('%s is not a valid domain name' % value) + raise forms.ValidationError( + '%s is not a valid domain name' % value + ) class TitleField(forms.CharField): + """Fild receiving question title""" def __init__(self, *args, **kwargs): super(TitleField, self).__init__(*args, **kwargs) self.required = True self.widget = forms.TextInput( - attrs={'size' : 70, 'autocomplete' : 'off'} - ) + attrs={'size': 70, 'autocomplete': 'off'} + ) self.max_length = 255 - self.label = _('title') - self.help_text = _('please enter a descriptive title for your question') + self.label = _('title') + self.help_text = _( + 'please enter a descriptive title for your question' + ) self.initial = '' def clean(self, value): + """cleans the field for minimum and maximum length + also is supposed to work for unicode non-ascii characters""" if value is None: value = '' if len(value) < askbot_settings.MIN_TITLE_LENGTH: @@ -192,21 +258,22 @@ class TitleField(forms.CharField): ) % self.max_length ) - return value.strip() # TODO: test me + return value.strip() # TODO: test me + class EditorField(forms.CharField): - """EditorField is subclassed by the + """EditorField is subclassed by the :class:`QuestionEditorField` and :class:`AnswerEditorField` """ length_error_template_singular = 'post content must be > %d character', length_error_template_plural = 'post content must be > %d characters', - min_length = 10#sentinel default value + min_length = 10 # sentinel default value def __init__(self, *args, **kwargs): super(EditorField, self).__init__(*args, **kwargs) self.required = True - self.widget = forms.Textarea(attrs={'id':'editor'}) - self.label = _('content') + self.widget = forms.Textarea(attrs={'id': 'editor'}) + self.label = _('content') self.help_text = u'' self.initial = '' @@ -218,32 +285,44 @@ class EditorField(forms.CharField): self.length_error_template_singular, self.length_error_template_plural, self.min_length - ) % self.min_length + ) % self.min_length raise forms.ValidationError(msg) return value + class QuestionEditorField(EditorField): + """Editor field for the questions""" + def __init__(self, *args, **kwargs): super(QuestionEditorField, self).__init__(*args, **kwargs) - self.length_error_template_singular = 'question body must be > %d character' - self.length_error_template_plural = 'question body must be > %d characters' + self.length_error_template_singular = \ + 'question body must be > %d character' + self.length_error_template_plural = \ + 'question body must be > %d characters' self.min_length = askbot_settings.MIN_QUESTION_BODY_LENGTH + class AnswerEditorField(EditorField): + """Editor field for answers""" + def __init__(self, *args, **kwargs): super(AnswerEditorField, self).__init__(*args, **kwargs) self.length_error_template_singular = 'answer must be > %d character' self.length_error_template_plural = 'answer must be > %d characters' self.min_length = askbot_settings.MIN_ANSWER_BODY_LENGTH + class TagNamesField(forms.CharField): + """field that receives AskBot tag names""" + def __init__(self, *args, **kwargs): super(TagNamesField, self).__init__(*args, **kwargs) self.required = askbot_settings.TAGS_ARE_REQUIRED - self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'}) + self.widget = forms.TextInput( + attrs={'size': 50, 'autocomplete': 'off'} + ) self.max_length = 255 self.label = _('tags') - #self.help_text = _('please use space to separate tags (this enables autocomplete feature)') self.help_text = ungettext_lazy( 'Tags are short keywords, with no spaces within. ' 'Up to %(max_tags)d tag can be used.', @@ -253,38 +332,16 @@ class TagNamesField(forms.CharField): ) % {'max_tags': askbot_settings.MAX_TAGS_PER_POST} self.initial = '' - def need_mandatory_tags(self): - """true, if list of mandatory tags is not empty""" - from askbot import models - return askbot_settings.TAGS_ARE_REQUIRED and len(models.tag.get_mandatory_tags()) > 0 - - def tag_string_matches(self, tag_string, mandatory_tag): - """true if tag string matches the mandatory tag""" - if mandatory_tag.endswith('*'): - return tag_string.startswith(mandatory_tag[:-1]) - else: - return tag_string == mandatory_tag - - def mandatory_tag_missing(self, tag_strings): - """true, if mandatory tag is not present in the list - of ``tag_strings``""" - from askbot import models - mandatory_tags = models.tag.get_mandatory_tags() - for mandatory_tag in mandatory_tags: - for tag_string in tag_strings: - if self.tag_string_matches(tag_string, mandatory_tag): - return False - return True - def clean(self, value): from askbot import models value = super(TagNamesField, self).clean(value) data = value.strip() if len(data) < 1: if askbot_settings.TAGS_ARE_REQUIRED: - raise forms.ValidationError(_('tags are required')) + raise forms.ValidationError(_('tags are required')) else: - return ''#don't test for required characters when tags is '' + #don't test for required characters when tags is '' + return '' split_re = re.compile(const.TAG_SPLIT_REGEX) tag_strings = split_re.split(data) entered_tags = [] @@ -294,11 +351,11 @@ class TagNamesField(forms.CharField): msg = ungettext_lazy( 'please use %(tag_count)d tag or less', 'please use %(tag_count)d tags or less', - tag_count) % {'tag_count':max_tags} + tag_count) % {'tag_count': max_tags} raise forms.ValidationError(msg) - if self.need_mandatory_tags(): - if self.mandatory_tag_missing(tag_strings): + if need_mandatory_tags(): + if mandatory_tag_missing_in_list(tag_strings): msg = _( 'At least one of the following tags is required : %(tags)s' ) % {'tags': get_text_list(models.tag.get_mandatory_tags())} @@ -307,18 +364,22 @@ class TagNamesField(forms.CharField): for tag in tag_strings: tag_length = len(tag) if tag_length > askbot_settings.MAX_TAG_LENGTH: - #singular form is odd in english, but required for pluralization - #in other languages - msg = ungettext_lazy('each tag must be shorter than %(max_chars)d character',#odd but added for completeness - 'each tag must be shorter than %(max_chars)d characters', - tag_length) % {'max_chars':tag_length} + #singular form is odd in english, but + #required for pluralization + #in other languages, odd but added for completeness + msg = ungettext_lazy( + 'each tag must be shorter than %(max_chars)d character', + 'each tag must be shorter than %(max_chars)d characters', + tag_length + ) % {'max_chars': tag_length} raise forms.ValidationError(msg) #todo - this needs to come from settings tagname_re = re.compile(const.TAG_REGEX, re.UNICODE) if not tagname_re.search(tag): raise forms.ValidationError(_( - 'In tags, please use letters, numbers and characters "-+.#"' + 'In tags, please use letters, ' + 'numbers and characters "-+.#"' )) #only keep unique tags if tag not in entered_tags: @@ -340,7 +401,7 @@ class TagNamesField(forms.CharField): #because we need tag name cases to be the same #as those stored in the database stored_tag = models.Tag.objects.get( - name__iexact = entered_tag + name__iexact=entered_tag ) if stored_tag.name not in cleaned_entered_tags: cleaned_entered_tags.append(stored_tag.name) @@ -349,30 +410,54 @@ class TagNamesField(forms.CharField): return u' '.join(cleaned_entered_tags) + class WikiField(forms.BooleanField): + """Rendered as checkbox turning post into + "community wiki" + """ + def __init__(self, *args, **kwargs): super(WikiField, self).__init__(*args, **kwargs) self.required = False self.initial = False - self.label = _('community wiki (karma is not awarded & many others can edit wiki post)') - 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') + self.label = _( + 'community wiki (karma is not awarded & ' + 'many others can edit wiki post)' + ) + 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' + ) + def clean(self, value): return value and askbot_settings.WIKI_ON + class EmailNotifyField(forms.BooleanField): + """Rendered as checkbox which turns on + email notifications on the post""" def __init__(self, *args, **kwargs): super(EmailNotifyField, self).__init__(*args, **kwargs) self.required = False self.widget.attrs['class'] = 'nomargin' + class SummaryField(forms.CharField): + def __init__(self, *args, **kwargs): super(SummaryField, self).__init__(*args, **kwargs) self.required = False - self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'}) + self.widget = forms.TextInput( + attrs={'size': 50, 'autocomplete': 'off'} + ) self.max_length = 300 - self.label = _('update summary:') - self.help_text = _('enter a brief summary of your revision (e.g. fixed spelling, grammar, improved style, this field is optional)') + self.label = _('update summary:') + self.help_text = _( + 'enter a brief summary of your revision (e.g. ' + 'fixed spelling, grammar, improved style, this ' + 'field is optional)' + ) class DumpUploadForm(forms.Form): @@ -382,6 +467,7 @@ class DumpUploadForm(forms.Form): """ dump_file = forms.FileField() + class ShowQuestionForm(forms.Form): """Cleans data necessary to access answers and comments by the respective comment or answer id - necessary @@ -389,10 +475,10 @@ class ShowQuestionForm(forms.Form): on the page other than the first page of answers to a question. Same for the answers that are shown on the later pages. """ - answer = forms.IntegerField(required = False) - comment = forms.IntegerField(required = False) - page = forms.IntegerField(required = False) - sort = forms.CharField(required = False) + answer = forms.IntegerField(required=False) + comment = forms.IntegerField(required=False) + page = forms.IntegerField(required=False) + sort = forms.CharField(required=False) def __init__(self, data, default_sort_method): super(ShowQuestionForm, self).__init__(data) @@ -436,6 +522,7 @@ class ShowQuestionForm(forms.Form): self.cleaned_data = out_data return out_data + class ChangeUserReputationForm(forms.Form): """Form that allows moderators and site administrators to adjust reputation of users. @@ -445,13 +532,15 @@ class ChangeUserReputationForm(forms.Form): """ user_reputation_delta = forms.IntegerField( - min_value = 1, - label = _('Enter number of points to add or subtract') - ) - comment = forms.CharField(max_length = 128) + min_value=1, + label=_( + 'Enter number of points to add or subtract' + ) + ) + comment = forms.CharField(max_length=128) def clean_comment(self): - if 'comment' in self.cleaned_data: + if 'comment' in self.cleaned_data: comment = self.cleaned_data['comment'].strip() if comment == '': del self.cleaned_data['comment'] @@ -485,9 +574,7 @@ class ChangeUserStatusForm(forms.Form): "moderation" tab """ - user_status = forms.ChoiceField( - label = _('Change status to'), - ) + user_status = forms.ChoiceField(label=_('Change status to')) def __init__(self, *arg, **kwarg): @@ -508,12 +595,12 @@ class ChangeUserStatusForm(forms.Form): #remove current status of the "subject" user from choices user_status_choices = filter_choices( - remove_choices = [subject.status, ], - from_choices = user_status_choices + remove_choices=[subject.status, ], + from_choices=user_status_choices ) #add prompt option - user_status_choices = ( ('select', _('which one?')), ) \ + user_status_choices = (('select', _('which one?')), ) \ + user_status_choices self.fields['user_status'].choices = user_status_choices @@ -561,41 +648,39 @@ class ChangeUserStatusForm(forms.Form): msg = _( 'If you wish to change %(username)s\'s status, ' 'please make a meaningful selection.' - ) % {'username': self.subject.username } + ) % {'username': self.subject.username} raise forms.ValidationError(msg) return self.cleaned_data + class SendMessageForm(forms.Form): subject_line = forms.CharField( - label = _('Subject line'), - max_length = 64, - widget = forms.TextInput( - attrs = {'size':64}, - ) - ) + label=_('Subject line'), + max_length=64, + widget=forms.TextInput(attrs={'size': 64}, ) + ) body_text = forms.CharField( - label = _('Message text'), - max_length = 1600, - widget = forms.Textarea( - attrs = {'cols':64} - ) + label=_('Message text'), + max_length=1600, + widget=forms.Textarea(attrs={'cols': 64}) ) class NotARobotForm(forms.Form): recaptcha = RecaptchaField( - private_key = askbot_settings.RECAPTCHA_SECRET, - public_key = askbot_settings.RECAPTCHA_KEY + private_key=askbot_settings.RECAPTCHA_SECRET, + public_key=askbot_settings.RECAPTCHA_KEY ) + class FeedbackForm(forms.Form): name = forms.CharField(label=_('Your name (optional):'), required=False) email = forms.EmailField(label=_('Email:'), required=False) message = forms.CharField( label=_('Your message:'), max_length=800, - widget=forms.Textarea(attrs={'cols':60}) + widget=forms.Textarea(attrs={'cols': 60}) ) no_email = forms.BooleanField( label=_("I don't want to give my email or receive a response:"), @@ -612,19 +697,21 @@ class FeedbackForm(forms.Form): def _add_recaptcha_field(self): self.fields['recaptcha'] = RecaptchaField( - private_key = askbot_settings.RECAPTCHA_SECRET, - public_key = askbot_settings.RECAPTCHA_KEY - ) + private_key=askbot_settings.RECAPTCHA_SECRET, + public_key=askbot_settings.RECAPTCHA_KEY + ) def clean(self): super(FeedbackForm, self).clean() if not self.is_auth: - if not self.cleaned_data['no_email'] and not self.cleaned_data['email']: + if not self.cleaned_data['no_email'] \ + and not self.cleaned_data['email']: msg = _('Please mark "I dont want to give my mail" field.') self._errors['email'] = self.error_class([msg]) return self.cleaned_data + class FormWithHideableFields(object): """allows to swap a field widget to HiddenInput() and back""" @@ -646,7 +733,81 @@ class FormWithHideableFields(object): if name in self.__hidden_fields: self.fields[name] = self.__hidden_fields.pop(name) -class AskForm(forms.Form, FormWithHideableFields): + +class PostAsSomeoneForm(forms.Form): + post_author_username = forms.CharField( + initial=_('User name:'), + help_text=_( + 'Enter name to post on behalf of someone else. ' + 'Can create new accounts.' + ), + required=False, + widget=forms.TextInput(attrs={'class': 'tipped-input'}) + ) + post_author_email = forms.CharField( + initial=_('Email address:'), + required=False, + widget=forms.TextInput(attrs={'class': 'tipped-input'}) + ) + + def get_post_user(self, user): + """returns user on whose behalf the post or a revision + is being made + """ + username = self.cleaned_data['post_author_username'] + email= self.cleaned_data['post_author_email'] + if user.is_administrator() and username and email: + post_user = user.get_or_create_user(username, email) + else: + post_user = user + return post_user + + def clean_post_author_username(self): + """if value is the same as initial, it is reset to + empty string + todo: maybe better to have field where initial value is invalid, + then we would not have to have two almost identical clean functions? + """ + username = self.cleaned_data.get('post_author_username', '') + initial_username = unicode(self.fields['post_author_username'].initial) + if username == initial_username: + self.cleaned_data['post_author_username'] = '' + return self.cleaned_data['post_author_username'] + + def clean_post_author_email(self): + """if value is the same as initial, it is reset to + empty string""" + email = self.cleaned_data.get('post_author_email', '') + initial_email = unicode(self.fields['post_author_email'].initial) + if email == initial_email: + email = '' + if email != '': + email = forms.EmailField().clean(email) + self.cleaned_data['post_author_email'] = email + return email + + def clean(self): + """requires email address if user name is given""" + username = self.cleaned_data.get('post_author_username', '') + email = self.cleaned_data.get('post_author_email', '') + if username == '' and email: + username_errors = self._errors.get( + 'post_author_username', + ErrorList() + ) + username_errors.append(_('User name is required with the email')) + self._errors['post_author_username'] = username_errors + raise forms.ValidationError('missing user name') + elif email == '' and username: + email_errors = self._errors.get('post_author_email', ErrorList()) + email_errors.append(_('Email is required if user name is added')) + self._errors['post_author_email'] = email_errors + raise forms.ValidationError('missing email') + + return self.cleaned_data + + +class AskForm(PostAsSomeoneForm, FormWithHideableFields): """the form used to askbot questions field ask_anonymously is shown to the user if the if ALLOW_ASK_ANONYMOUSLY live setting is True @@ -654,45 +815,43 @@ class AskForm(forms.Form, FormWithHideableFields): in the cleaned data, and will evaluate to False if the settings forbids anonymous asking """ - title = TitleField() - text = QuestionEditorField() - tags = TagNamesField() + title = TitleField() + text = QuestionEditorField() + tags = TagNamesField() wiki = WikiField() ask_anonymously = forms.BooleanField( - label = _('ask anonymously'), - help_text = _( + label=_('ask anonymously'), + help_text=_( 'Check if you do not want to reveal your name ' 'when asking this question' ), - required = False, - ) - openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'})) - user = forms.CharField( required=False, - max_length=255, - label="User", - help_text=_("Enter the username to post this as. New users are automatically created."), - widget=forms.TextInput(attrs={'size' : 35})) - email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35})) + ) + openid = forms.CharField( + required=False, max_length=255, + widget=forms.TextInput(attrs={'size': 40, 'class': 'openid-input'}) + ) def __init__(self, *args, **kwargs): super(AskForm, self).__init__(*args, **kwargs) #hide ask_anonymously field - if askbot_settings.ALLOW_ASK_ANONYMOUSLY == False: + if askbot_settings.ALLOW_ASK_ANONYMOUSLY is False: self.hide_field('ask_anonymously') def clean_ask_anonymously(self): """returns false if anonymous asking is not allowed """ - if askbot_settings.ALLOW_ASK_ANONYMOUSLY == False: + if askbot_settings.ALLOW_ASK_ANONYMOUSLY is False: self.cleaned_data['ask_anonymously'] = False return self.cleaned_data['ask_anonymously'] + ASK_BY_EMAIL_SUBJECT_HELP = _( 'Subject line is expected in the format: ' '[tag1, tag2, tag3,...] question title' ) + class AskByEmailForm(forms.Form): """:class:`~askbot.forms.AskByEmailForm` validates question data, where question was posted @@ -711,12 +870,13 @@ class AskByEmailForm(forms.Form): * ``email`` - email address * ``title`` - question title * ``tagnames`` - tag names all in one string - * ``body_text`` - body of question text - a pass-through, no extra validation + * ``body_text`` - body of question text - + a pass-through, no extra validation """ - sender = forms.CharField(max_length = 255) + sender = forms.CharField(max_length=255) subject = forms.CharField( - max_length = 255, - error_messages = { + max_length=255, + error_messages={ 'required': ASK_BY_EMAIL_SUBJECT_HELP } ) @@ -748,17 +908,19 @@ class AskByEmailForm(forms.Form): match = subject_re.match(raw_subject) if match: #make raw tags comma-separated - if match.group(1) is None:#no tags + if match.group(1) is None: # no tags self.cleaned_data['tagnames'] = '' else: - tagnames = match.group(1).replace(';',',') + tagnames = match.group(1).replace(';', ',') #pre-process tags tag_list = [tag.strip() for tag in tagnames.split(',')] tag_list = [re.sub(r'\s+', ' ', tag) for tag in tag_list] + if askbot_settings.REPLACE_SPACE_WITH_DASH_IN_EMAILED_TAGS: tag_list = [tag.replace(' ', '-') for tag in tag_list] - tagnames = ' '.join(tag_list)#todo: use tag separator char here + #todo: use tag separator char here + tagnames = ' '.join(tag_list) #clean tags - may raise ValidationError self.cleaned_data['tagnames'] = TagNamesField().clean(tagnames) @@ -770,27 +932,28 @@ class AskByEmailForm(forms.Form): raise forms.ValidationError(ASK_BY_EMAIL_SUBJECT_HELP) return self.cleaned_data['subject'] -class AnswerForm(forms.Form): - text = AnswerEditorField() - wiki = WikiField() - openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'})) - user = forms.CharField( - required=False, - max_length=255, - label="User", - help_text=_("Enter the username to post this as. New users are automatically created."), - widget=forms.TextInput(attrs={'size' : 35})) - email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35})) - email_notify = EmailNotifyField(initial = False) + +class AnswerForm(PostAsSomeoneForm): + text = AnswerEditorField() + wiki = WikiField() + openid = forms.CharField( + required=False, max_length=255, + widget=forms.TextInput(attrs={'size': 40, 'class': 'openid-input'}) + ) + email_notify = EmailNotifyField(initial=False) + def __init__(self, *args, **kwargs): super(AnswerForm, self).__init__(*args, **kwargs) - self.fields['email_notify'].widget.attrs['id'] = 'question-subscribe-updates' + self.fields['email_notify'].widget.attrs['id'] = \ + 'question-subscribe-updates' + class VoteForm(forms.Form): """form used in ajax vote view (only comment_upvote so far) """ post_id = forms.IntegerField() - cancel_vote = forms.CharField()#char because it is 'true' or 'false' as string + # char because it is 'true' or 'false' as string + cancel_vote = forms.CharField() def clean_cancel_vote(self): val = self.cleaned_data['cancel_vote'] @@ -800,7 +963,9 @@ class VoteForm(forms.Form): result = False else: del self.cleaned_data['cancel_vote'] - raise forms.ValidationError('either "true" or "false" strings expected') + raise forms.ValidationError( + 'either "true" or "false" strings expected' + ) self.cleaned_data['cancel_vote'] = result return self.cleaned_data['cancel_vote'] @@ -808,50 +973,58 @@ class VoteForm(forms.Form): class CloseForm(forms.Form): reason = forms.ChoiceField(choices=const.CLOSE_REASONS) + class RetagQuestionForm(forms.Form): tags = TagNamesField() - # initialize the default values + def __init__(self, question, *args, **kwargs): + """initialize the default values""" super(RetagQuestionForm, self).__init__(*args, **kwargs) self.fields['tags'].initial = question.thread.tagnames + class RevisionForm(forms.Form): """ Lists revisions of a Question or Answer """ - revision = forms.ChoiceField(widget=forms.Select(attrs={'style' : 'width:520px'})) + revision = forms.ChoiceField( + widget=forms.Select( + attrs={'style': 'width:520px'} + ) + ) def __init__(self, post, latest_revision, *args, **kwargs): super(RevisionForm, self).__init__(*args, **kwargs) revisions = post.revisions.values_list( - 'revision', 'author__username', 'revised_at', 'summary') + 'revision', 'author__username', 'revised_at', 'summary' + ) date_format = '%c' - self.fields['revision'].choices = [ - (r[0], u'%s - %s (%s) %s' % (r[0], r[1], r[2].strftime(date_format), r[3])) - for r in revisions] + rev_choices = list() + for r in revisions: + rev_details = u'%s - %s (%s) %s' % ( + r[0], r[1], r[2].strftime(date_format), r[3] + ) + rev_choices.append((r[0], rev_details)) + + self.fields['revision'].choices = rev_choices self.fields['revision'].initial = latest_revision.revision -class EditQuestionForm(forms.Form, FormWithHideableFields): - title = TitleField() - text = QuestionEditorField() - tags = TagNamesField() + +class EditQuestionForm(PostAsSomeoneForm, FormWithHideableFields): + title = TitleField() + text = QuestionEditorField() + tags = TagNamesField() summary = SummaryField() wiki = WikiField() reveal_identity = forms.BooleanField( - help_text = _( + help_text=_( 'You have asked this question anonymously, ' 'if you decide to reveal your identity, please check ' 'this box.' ), - label = _('reveal identity'), - required = False, - ) - user_author = forms.CharField( + label=_('reveal identity'), required=False, - max_length=255, - label="User", - help_text=_("Enter the username to post this as. New users are automatically created."), - widget=forms.TextInput(attrs={'size' : 35})) + ) #todo: this is odd that this form takes question as an argument def __init__(self, *args, **kwargs): @@ -871,8 +1044,8 @@ class EditQuestionForm(forms.Form, FormWithHideableFields): def can_stay_anonymous(self): """determines if the user cat keep editing the question anonymously""" - return (askbot_settings.ALLOW_ASK_ANONYMOUSLY \ - and self.question.is_anonymous \ + return (askbot_settings.ALLOW_ASK_ANONYMOUSLY + and self.question.is_anonymous and self.user.is_owner_of(self.question) ) @@ -898,7 +1071,7 @@ class EditQuestionForm(forms.Form, FormWithHideableFields): """ value = self.cleaned_data['reveal_identity'] if self.question.is_anonymous: - if value == True: + if value is True: if self.user.is_owner_of(self.question): #regardless of the ALLOW_ASK_ANONYMOUSLY return True @@ -916,7 +1089,7 @@ class EditQuestionForm(forms.Form, FormWithHideableFields): else: can_ask_anon = askbot_settings.ALLOW_ASK_ANONYMOUSLY is_owner = self.user.is_owner_of(self.question) - if can_ask_anon == False and is_owner: + if can_ask_anon is False and is_owner: self.show_field('reveal_identity') raise forms.ValidationError( _( @@ -938,63 +1111,61 @@ class EditQuestionForm(forms.Form, FormWithHideableFields): field edit_anonymously. It relies on correct cleaning if the "reveal_identity" field """ + super(EditQuestionForm, self).clean() reveal_identity = self.cleaned_data.get('reveal_identity', False) stay_anonymous = False - if reveal_identity == False and self.can_stay_anonymous(): + if reveal_identity is False and self.can_stay_anonymous(): stay_anonymous = True self.cleaned_data['stay_anonymous'] = stay_anonymous return self.cleaned_data -class EditAnswerForm(forms.Form): + +class EditAnswerForm(PostAsSomeoneForm): text = AnswerEditorField() summary = SummaryField() wiki = WikiField() - user = forms.CharField( - required=False, - max_length=255, - label="User", - help_text=_("Enter the username to post this as. New users are automatically created."), - widget=forms.TextInput(attrs={'size' : 35})) def __init__(self, answer, revision, *args, **kwargs): super(EditAnswerForm, self).__init__(*args, **kwargs) self.fields['text'].initial = revision.text self.fields['wiki'].initial = answer.wiki + class EditTagWikiForm(forms.Form): - text = forms.CharField(required = False) + text = forms.CharField(required=False) tag_id = forms.IntegerField() + class EditUserForm(forms.Form): email = forms.EmailField( label=u'Email', required=True, max_length=255, - widget=forms.TextInput(attrs={'size' : 35}) + widget=forms.TextInput(attrs={'size': 35}) ) realname = forms.CharField( label=_('Real name'), required=False, max_length=255, - widget=forms.TextInput(attrs={'size' : 35}) + widget=forms.TextInput(attrs={'size': 35}) ) website = forms.URLField( label=_('Website'), required=False, max_length=255, - widget=forms.TextInput(attrs={'size' : 35}) + widget=forms.TextInput(attrs={'size': 35}) ) city = forms.CharField( label=_('City'), required=False, max_length=255, - widget=forms.TextInput(attrs={'size' : 35}) + widget=forms.TextInput(attrs={'size': 35}) ) - country = CountryField(required = False) + country = CountryField(required=False) show_country = forms.BooleanField( label=_('Show country'), @@ -1008,15 +1179,18 @@ class EditUserForm(forms.Form): birthday = forms.DateField( label=_('Date of birth'), - help_text=_('will not be shown, used to calculate age, format: YYYY-MM-DD'), + help_text=_( + 'will not be shown, used to calculate ' + 'age, format: YYYY-MM-DD' + ), required=False, - widget=forms.TextInput(attrs={'size' : 35}) + widget=forms.TextInput(attrs={'size': 35}) ) about = forms.CharField( label=_('Profile'), required=False, - widget=forms.Textarea(attrs={'cols' : 60}) + widget=forms.Textarea(attrs={'cols': 60}) ) def __init__(self, user, *args, **kwargs): @@ -1030,7 +1204,7 @@ class EditUserForm(forms.Form): self.fields['realname'].initial = user.real_name self.fields['website'].initial = user.website self.fields['city'].initial = user.location - if user.country == None: + if user.country is None: country = 'unknown' else: country = user.country @@ -1048,24 +1222,32 @@ class EditUserForm(forms.Form): """For security reason one unique email in database""" if self.user.email != self.cleaned_data['email']: #todo dry it, there is a similar thing in openidauth - if askbot_settings.EMAIL_UNIQUE == True: + if askbot_settings.EMAIL_UNIQUE is True: if 'email' in self.cleaned_data: try: - User.objects.get(email = self.cleaned_data['email']) + User.objects.get(email=self.cleaned_data['email']) except User.DoesNotExist: return self.cleaned_data['email'] except User.MultipleObjectsReturned: - raise forms.ValidationError(_('this email has already been registered, please use another one')) - raise forms.ValidationError(_('this email has already been registered, please use another one')) + raise forms.ValidationError(_( + 'this email has already been registered, ' + 'please use another one') + ) + raise forms.ValidationError(_( + 'this email has already been registered, ' + 'please use another one') + ) return self.cleaned_data['email'] + class TagFilterSelectionForm(forms.ModelForm): email_tag_filter_strategy = forms.ChoiceField( - choices = const.TAG_DISPLAY_FILTER_STRATEGY_CHOICES, - initial = const.EXCLUDE_IGNORED, - label = _('Choose email tag filter'), - widget = forms.RadioSelect + choices=const.TAG_DISPLAY_FILTER_STRATEGY_CHOICES, + initial=const.EXCLUDE_IGNORED, + label=_('Choose email tag filter'), + widget=forms.RadioSelect ) + class Meta: model = User fields = ('email_tag_filter_strategy',) @@ -1082,31 +1264,31 @@ class TagFilterSelectionForm(forms.ModelForm): class EmailFeedSettingField(forms.ChoiceField): def __init__(self, *arg, **kwarg): kwarg['choices'] = const.NOTIFICATION_DELIVERY_SCHEDULE_CHOICES - #kwarg['initial'] = askbot_settings.DEFAULT_NOTIFICATION_DELIVERY_SCHEDULE kwarg['widget'] = forms.RadioSelect super(EmailFeedSettingField, self).__init__(*arg, **kwarg) + class EditUserEmailFeedsForm(forms.Form): FORM_TO_MODEL_MAP = { - 'all_questions':'q_all', - 'asked_by_me':'q_ask', - 'answered_by_me':'q_ans', - 'individually_selected':'q_sel', - 'mentions_and_comments':'m_and_c', + 'all_questions': 'q_all', + 'asked_by_me': 'q_ask', + 'answered_by_me': 'q_ans', + 'individually_selected': 'q_sel', + 'mentions_and_comments': 'm_and_c', } NO_EMAIL_INITIAL = { - 'all_questions':'n', - 'asked_by_me':'n', - 'answered_by_me':'n', - 'individually_selected':'n', - 'mentions_and_comments':'n', + 'all_questions': 'n', + 'asked_by_me': 'n', + 'answered_by_me': 'n', + 'individually_selected': 'n', + 'mentions_and_comments': 'n', } INSTANT_EMAIL_INITIAL = { - 'all_questions':'i', - 'asked_by_me':'i', - 'answered_by_me':'i', - 'individually_selected':'i', - 'mentions_and_comments':'i', + 'all_questions': 'i', + 'asked_by_me': 'i', + 'answered_by_me': 'i', + 'individually_selected': 'i', + 'mentions_and_comments': 'i', } asked_by_me = EmailFeedSettingField( @@ -1129,7 +1311,7 @@ class EditUserEmailFeedsForm(forms.Form): def set_initial_values(self, user=None): from askbot import models KEY_MAP = dict([(v, k) for k, v in self.FORM_TO_MODEL_MAP.iteritems()]) - if user != None: + if user is not None: settings = models.EmailFeedSetting.objects.filter(subscriber=user) initial_values = {} for setting in settings: @@ -1159,7 +1341,7 @@ class EditUserEmailFeedsForm(forms.Form): """ return self.FORM_TO_MODEL_MAP.values() - def set_frequency(self, frequency = 'n'): + def set_frequency(self, frequency='n'): data = { 'all_questions': frequency, 'asked_by_me': frequency, @@ -1171,9 +1353,9 @@ class EditUserEmailFeedsForm(forms.Form): self.cleaned_data = data self.initial = data - def save(self,user,save_unbound=False): - """ - with save_unbound==True will bypass form validation and save initial values + def save(self, user, save_unbound=False): + """with save_unbound==True will bypass form + validation and save initial values """ from askbot import models changed = False @@ -1201,23 +1383,25 @@ class EditUserEmailFeedsForm(forms.Form): user.followed_threads.clear() return changed + class SubscribeForEmailUpdatesField(forms.ChoiceField): """a simple yes or no field to subscribe for email or not""" def __init__(self, **kwargs): kwargs['widget'] = forms.widgets.RadioSelect kwargs['error_messages'] = { - 'required':_('please choose one of the options above') + 'required': _('please choose one of the options above') } kwargs['choices'] = ( - ('y',_('okay, let\'s try!')), + ('y', _('okay, let\'s try!')), ( 'n', - _('no %(sitename)s email please, thanks') \ + _('no %(sitename)s email please, thanks') % {'sitename': askbot_settings.APP_SHORT_NAME} ) ) super(SubscribeForEmailUpdatesField, self).__init__(**kwargs) + class SimpleEmailSubscribeForm(forms.Form): subscribe = SubscribeForEmailUpdatesField() @@ -1233,11 +1417,13 @@ class SimpleEmailSubscribeForm(forms.Form): email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL) email_settings_form.save(user, save_unbound=True) + class GroupLogoURLForm(forms.Form): """form for saving group logo url""" group_id = forms.IntegerField() image_url = forms.CharField() + class EditGroupMembershipForm(forms.Form): """a form for adding or removing users to and from user groups""" @@ -1253,11 +1439,12 @@ class EditGroupMembershipForm(forms.Form): raise forms.ValidationError('invalid action') return action + class EditRejectReasonForm(forms.Form): - reason_id = forms.IntegerField(required = False) + reason_id = forms.IntegerField(required=False) title = CountedWordsField( - min_words = 1, max_words = 4, field_name = _('Title') + min_words=1, max_words=4, field_name=_('Title') ) details = CountedWordsField( - min_words = 6, field_name = _('Description') + min_words=6, field_name=_('Description') ) diff --git a/askbot/migrations/0126_add_field__auth_user__is_fake.py b/askbot/migrations/0126_add_field__auth_user__is_fake.py new file mode 100644 index 00000000..e0928ed7 --- /dev/null +++ b/askbot/migrations/0126_add_field__auth_user__is_fake.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from south.db import db +from south.v2 import SchemaMigration + +class Migration(SchemaMigration): + + def forwards(self, orm): + try: + # Adding field 'User.is_fake' + db.add_column( + u'auth_user', 'is_fake', + self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False) + except: + pass + + def backwards(self, orm): + db.delete_column('auth_user', 'is_fake') + + complete_apps = ['askbot'] diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index c4c11cb4..3fd78ee9 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -83,6 +83,7 @@ User.add_to_class( choices = const.USER_STATUS_CHOICES ) ) +User.add_to_class('is_fake', models.BooleanField(default=False)) User.add_to_class('email_isvalid', models.BooleanField(default=False)) #@UndefinedVariable User.add_to_class('email_key', models.CharField(max_length=32, null=True)) @@ -343,6 +344,23 @@ def user_can_post_by_email(self): return askbot_settings.REPLY_BY_EMAIL and \ self.reputation > askbot_settings.MIN_REP_TO_POST_BY_EMAIL +def user_get_or_create_user(self, username, email): + """ + Get's or creates a user, most likely with the purpose + of posting under that account. + """ + assert(self.is_administrator()) + + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + user = User.objects.create_user( + username=username, + email=email, + is_fake=True + ) + return user + def _assert_user_can( user = None, post = None, #related post (may be parent) @@ -2516,6 +2534,7 @@ User.add_to_class('get_absolute_url', user_get_absolute_url) User.add_to_class('get_avatar_url', user_get_avatar_url) User.add_to_class('get_default_avatar_url', user_get_default_avatar_url) User.add_to_class('get_gravatar_url', user_get_gravatar_url) +User.add_to_class('get_or_create_user', user_get_or_create_user) User.add_to_class('get_marked_tags', user_get_marked_tags) User.add_to_class('get_marked_tag_names', user_get_marked_tag_names) User.add_to_class('strip_email_signature', user_strip_email_signature) diff --git a/askbot/skins/common/media/js/post.js b/askbot/skins/common/media/js/post.js index 58f2436d..32453212 100644 --- a/askbot/skins/common/media/js/post.js +++ b/askbot/skins/common/media/js/post.js @@ -2408,6 +2408,33 @@ $(document).ready(function() { }); questionRetagger.init(); socialSharing.init(); + + var proxyUserNameInput = $('#id_post_author_username'); + var proxyUserEmailInput = $('#id_post_author_email'); + if (proxyUserNameInput.length === 1) { + var tip = new TippedInput(); + tip.decorate(proxyUserNameInput); + + var userSelectHandler = function(data) { + proxyUserEmailInput.val(data['data'][0]); + }; + + var fakeUserAc = new AutoCompleter({ + url: '/get-users-info/',//askbot['urls']['get_users_info'], + preloadData: true, + minChars: 1, + useCache: true, + matchInside: true, + maxCacheLength: 100, + delay: 10, + onItemSelect: userSelectHandler + }); + fakeUserAc.decorate(proxyUserNameInput); + } + if (proxyUserEmailInput.length === 1) { + var tip = new TippedInput(); + tip.decorate(proxyUserEmailInput); + } }); diff --git a/askbot/skins/common/media/js/utils.js b/askbot/skins/common/media/js/utils.js index d04a3cdb..3bc64b65 100644 --- a/askbot/skins/common/media/js/utils.js +++ b/askbot/skins/common/media/js/utils.js @@ -309,6 +309,7 @@ TippedInput.prototype.decorate = function(element){ this._element = element; var instruction_text = this.getVal(); this._instruction = instruction_text; + this.reset(); var me = this; $(element).focus(function(){ if (me.isBlank()){ diff --git a/askbot/skins/common/templates/widgets/edit_post.html b/askbot/skins/common/templates/widgets/edit_post.html index 4e695012..f26e57f6 100644 --- a/askbot/skins/common/templates/widgets/edit_post.html +++ b/askbot/skins/common/templates/widgets/edit_post.html @@ -55,30 +55,6 @@ </div> {% endif %} -{% if user and user.is_staff and user.is_active %} - {# admin can post answers or questions on behalf of anyone. #} - {% if post_type == 'question' and edit_title %} - {# edit question already has 'user' field set #} - <div class="form-item"> - <strong>{{ post_form.user_author.label_tag() }}</strong> <br /> - {{ post_form.user_author }} - <div class="title-desc"> - {{ post_form.user_author.help_text }} - </div> - <div class="form-error">{{ post_form.user_author.errors }}</div> - </div> - {% else %} - <div class="form-item"> - <strong>{{ post_form.user.label_tag() }}</strong> <br /> - {{ post_form.user }} - <div class="title-desc"> - {{ post_form.user.help_text }} - </div> - <div class="form-error">{{ post_form.user.errors }}</div> - </div> - {% endif %} -{% endif %} - <div class="preview-toggle"> <span id="pre-collapse" @@ -88,3 +64,41 @@ </span> </div> <div id="previewer" class="wmd-preview"></div> + +{% if user and user.is_authenticated() and user.is_administrator() %} + {# admin can post answers or questions on behalf of anyone. #} + <table class="proxy-user-info"> + <tbody> + <tr><td colspan="2"> + <label> + {% trans %}To post on behalf of someone else, enter user name <strong>and</strong> email below.{% endtrans %} + </label> + </td></tr> + <tr> + <td> + <div class="form-item"> + {{ post_form.post_author_username }} + <!--input + id="id_post_author_username" + name="post_author_username" + value="{% trans %}User name:{% endtrans %}" + /--> + </div> + <div class="form-item"> + {{ post_form.post_author_email }} + <!--input + id="id_post_author_email" + name="post_author_email" + value="{% trans %}Email address:{% endtrans %}" + /--> + </div> + </td> + </tr> + <tr> + <td colspan="2"> + <span class="form-error">{{ post_form.post_author_username.errors }}</span> + <span class="form-error">{{ post_form.post_author_email.errors }}</span> + </td> + </tbody> + </table> +{% endif %} diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less index 98914a28..3639fcc9 100644 --- a/askbot/skins/default/media/style/style.less +++ b/askbot/skins/default/media/style/style.less @@ -1340,12 +1340,36 @@ ul#related-tags li { font-size:13px; } - #id_tags{ + #id_tags { border:#cce6ec 3px solid; height:25px; padding-left:5px; + font-size:14px; width:395px; + } +} + +.ask-page, +.question-page, +.edit-question-page, +.edit-answer-page { + #id_post_author_username, + #id_post_author_email { + border:#cce6ec 3px solid; + height:25px; + padding-left:5px; font-size:14px; + width:186px; + } + #id_post_author_email { + margin-left: 10px; + } + table.proxy-user-info { + border-spacing: 0px; + + .form-item { + float: left; + } } } diff --git a/askbot/tests/form_tests.py b/askbot/tests/form_tests.py index 654272b3..a37f380c 100644 --- a/askbot/tests/form_tests.py +++ b/askbot/tests/form_tests.py @@ -334,3 +334,30 @@ class AnswerEditorFieldTests(AskbotTestCase): self.field.clean(10*'a'), 10*'a' ) + + +class PostAsSomeoneFormTests(AskbotTestCase): + + form = forms.PostAsSomeoneForm + + def setUp(self): + self.good_data = { + 'username': 'me', + 'email': 'me@example.com' + } + + def test_blank_form_validates(self): + form = forms.PostAsSomeoneForm({}) + self.assertEqual(form.is_valid(), True) + + def test_complete_form_validates(self): + form = forms.PostAsSomeoneForm(self.good_data) + self.assertEqual(form.is_valid(), True) + + def test_missing_email_fails(self): + form = forms.PostAsSomeoneForm({'post_author_username': 'me'}) + self.assertEqual(form.is_valid(), False) + + def test_missing_username_fails(self): + form = forms.PostAsSomeoneForm({'post_author_email': 'me@example.com'}) + self.assertEqual(form.is_valid(), False) diff --git a/askbot/urls.py b/askbot/urls.py index 869f7d69..f39371e5 100644 --- a/askbot/urls.py +++ b/askbot/urls.py @@ -79,6 +79,11 @@ urlpatterns = patterns('', name = 'api_get_questions' ), url( + r'^get-users-info/', + views.commands.get_users_info, + name='get_users_info' + ), + url( r'^%s%s$' % (_('questions/'), _('ask/')), views.writers.ask, name='ask' diff --git a/askbot/utils/forms.py b/askbot/utils/forms.py index 1420b58a..b8ed253b 100644 --- a/askbot/utils/forms.py +++ b/askbot/utils/forms.py @@ -26,28 +26,6 @@ def clean_next(next, default = None): def get_next_url(request, default = None): return clean_next(request.REQUEST.get('next'), default) -def post_as_user(user, form, userfield='user'): - """ - Allows admin staff to post as any user. - - If the `user` object is_staff and is_active: - - Gets or creates a new user with the username provided in the 'user' - field on the form. - - otherwise returns the `user` object. - """ - - if user.is_staff and user.is_active: - # allow admin user to post as anyone. - username = form.cleaned_data[userfield] - try: - user = User.objects.get(username=username) - except User.DoesNotExist: - user = User.objects.create_user(username=username, email='') - - return user - class StrippedNonEmptyCharField(forms.CharField): def clean(self, value): value = value.strip() diff --git a/askbot/views/commands.py b/askbot/views/commands.py index e343c85e..12305f46 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -913,3 +913,26 @@ def save_post_reject_reason(request): } else: raise Exception(forms.format_form_errors(form)) + + +@decorators.get_only +@decorators.admins_only +def get_users_info(request): + """retuns list of user names and email addresses + of "fake" users - so that admins can post on their + behalf""" + user_info_list = models.User.objects.filter( + is_fake=True + ).values_list( + 'username', + 'email' + ) + + result_list = list() + for user_info in user_info_list: + username = user_info[0] + email = user_info[1] + result_list.append('%s|%s' % (username, email)) + + output = '\n'.join(result_list) + return HttpResponse(output, mimetype = 'text/plain') diff --git a/askbot/views/writers.py b/askbot/views/writers.py index 6ac54991..9d86eb88 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -32,7 +32,6 @@ from askbot.utils import decorators from askbot.utils.functions import diff_date from askbot.utils import url_utils from askbot.utils.file_utils import store_file -from askbot.utils.forms import post_as_user from askbot.templatetags import extra_filters_jinja as template_filters from askbot.importers.stackexchange import management as stackexchange#todo: may change @@ -219,9 +218,8 @@ def ask(request):#view used to ask a new question ask_anonymously = form.cleaned_data['ask_anonymously'] if request.user.is_authenticated(): - - user = post_as_user(request.user, form) - + + user = form.get_post_user(request.user) try: question = user.post_question( title = title, @@ -380,7 +378,8 @@ def edit_question(request, id): is_anon_edit = form.cleaned_data['stay_anonymous'] is_wiki = form.cleaned_data.get('wiki', question.wiki) - user = post_as_user(request.user, form, userfield='user_author') + user = form.get_post_user(request.user) + user.edit_question( question = question, title = form.cleaned_data['title'], @@ -451,7 +450,7 @@ def edit_answer(request, id): if form.is_valid(): if form.has_changed(): - user = post_as_user(request.user, form) + user = form.get_post_user(request.user) user.edit_answer( answer = answer, body_text = form.cleaned_data['text'], @@ -499,7 +498,7 @@ def answer(request, id):#process a new answer try: follow = form.cleaned_data['email_notify'] - user = post_as_user(request.user, form) + user = form.get_post_user(request.user) answer = user.post_answer( question = question, |