diff options
author | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-07-30 02:18:51 -0400 |
---|---|---|
committer | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-07-30 02:18:51 -0400 |
commit | 2aac3317ab03ebe1f27796b647ce87679d202c46 (patch) | |
tree | c75252c872f8ef94379f6d3c24a883476dd726f6 | |
parent | 5e3f78086073f73e8bf938a9ec196f2e897fb03b (diff) | |
parent | e9243cbfa7c8ee9724f8022b987feced43fbd824 (diff) | |
download | askbot-2aac3317ab03ebe1f27796b647ce87679d202c46.tar.gz askbot-2aac3317ab03ebe1f27796b647ce87679d202c46.tar.bz2 askbot-2aac3317ab03ebe1f27796b647ce87679d202c46.zip |
merged the "post under fake accounts" branch
-rw-r--r-- | askbot/doc/source/changelog.rst | 1 | ||||
-rw-r--r-- | askbot/doc/source/contributors.rst | 1 | ||||
-rw-r--r-- | askbot/forms.py | 99 | ||||
-rw-r--r-- | askbot/migrations/0126_add_field__auth_user__is_fake.py | 19 | ||||
-rw-r--r-- | askbot/models/__init__.py | 20 | ||||
-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 | 41 | ||||
-rw-r--r-- | askbot/skins/default/media/style/style.css | 4 | ||||
-rw-r--r-- | askbot/skins/default/media/style/style.less | 35 | ||||
-rw-r--r-- | askbot/skins/default/templates/answer_edit.html | 2 | ||||
-rw-r--r-- | askbot/skins/default/templates/macros.html | 3 | ||||
-rw-r--r-- | askbot/skins/default/templates/question/new_answer_form.html | 2 | ||||
-rw-r--r-- | askbot/skins/default/templates/question_edit.html | 3 | ||||
-rw-r--r-- | askbot/skins/default/templates/widgets/ask_form.html | 3 | ||||
-rw-r--r-- | askbot/tests/form_tests.py | 27 | ||||
-rw-r--r-- | askbot/urls.py | 5 | ||||
-rw-r--r-- | askbot/views/commands.py | 23 | ||||
-rw-r--r-- | askbot/views/writers.py | 17 |
19 files changed, 301 insertions, 32 deletions
diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst index c0540be9..48725c07 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -6,6 +6,7 @@ Development version * Added optional support for unicode slugs (Evgeny) * Optionally allow limiting one answer per question per person (Evgeny) * Added management command `build_livesettings_cache` (Adolfo) +* 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 1188ed59..ecf86717 100644 --- a/askbot/forms.py +++ b/askbot/forms.py @@ -3,6 +3,7 @@ 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 @@ -733,7 +734,80 @@ class FormWithHideableFields(object): 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_fake_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 @@ -757,14 +831,6 @@ class AskForm(forms.Form, FormWithHideableFields): required=False, max_length=255, widget=forms.TextInput(attrs={'size': 40, 'class': 'openid-input'}) ) - user = forms.CharField( - required=False, max_length=255, - widget=forms.TextInput(attrs={'size': 35}) - ) - email = forms.CharField( - required=False, max_length=255, - widget=forms.TextInput(attrs={'size': 35}) - ) def __init__(self, *args, **kwargs): super(AskForm, self).__init__(*args, **kwargs) @@ -867,21 +933,13 @@ class AskByEmailForm(forms.Form): return self.cleaned_data['subject'] -class AnswerForm(forms.Form): +class AnswerForm(PostAsSomeoneForm): 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, - 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) def __init__(self, *args, **kwargs): @@ -952,7 +1010,7 @@ class RevisionForm(forms.Form): self.fields['revision'].initial = latest_revision.revision -class EditQuestionForm(forms.Form, FormWithHideableFields): +class EditQuestionForm(PostAsSomeoneForm, FormWithHideableFields): title = TitleField() text = QuestionEditorField() tags = TagNamesField() @@ -1053,6 +1111,7 @@ 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 is False and self.can_stay_anonymous(): @@ -1061,7 +1120,7 @@ class EditQuestionForm(forms.Form, FormWithHideableFields): return self.cleaned_data -class EditAnswerForm(forms.Form): +class EditAnswerForm(PostAsSomeoneForm): text = AnswerEditorField() summary = SummaryField() wiki = WikiField() 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 914f1c11..686190b7 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,24 @@ 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_fake_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() + user.username = username + user.email = email + user.is_fake = True + user.set_unusable_password() + user.save() + return user + def _assert_user_can( user = None, post = None, #related post (may be parent) @@ -2537,6 +2556,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_fake_user', user_get_or_create_fake_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 81aff7c3..3f585435 100644 --- a/askbot/skins/common/media/js/utils.js +++ b/askbot/skins/common/media/js/utils.js @@ -313,6 +313,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 66f79237..f26e57f6 100644 --- a/askbot/skins/common/templates/widgets/edit_post.html +++ b/askbot/skins/common/templates/widgets/edit_post.html @@ -18,7 +18,7 @@ {% if post_type == 'question' %} <div class="form-item"> {% if tags_are_required %} - <label for=id_tags"> + <label for="id_tags"> {% if mandatory_tags %} <strong>{% trans %}tags{% endtrans %}</strong> {% trans %}, one of these is required{% endtrans %} @@ -54,6 +54,7 @@ <div class="form-error" >{{ post_form.summary.errors }}</div> </div> {% endif %} + <div class="preview-toggle"> <span id="pre-collapse" @@ -63,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.css b/askbot/skins/default/media/style/style.css index 7a695825..c8d92f60 100644 --- a/askbot/skins/default/media/style/style.css +++ b/askbot/skins/default/media/style/style.css @@ -1358,7 +1358,9 @@ ul#related-tags li { font-size: 13px; } .ask-page #id_tags, -.edit-question-page #id_tags { +.edit-question-page #id_tags, +#id_user, +#id_user_author { border: #cce6ec 3px solid; height: 25px; padding-left: 5px; diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less index 4e272eca..db5d14f8 100644 --- a/askbot/skins/default/media/style/style.less +++ b/askbot/skins/default/media/style/style.less @@ -1345,13 +1345,46 @@ 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; + } + } +} + +#id_user, +#id_user_author { + border:#cce6ec 3px solid; + height:25px; + padding-left:5px; + width:395px; + font-size:14px; } .title-desc { diff --git a/askbot/skins/default/templates/answer_edit.html b/askbot/skins/default/templates/answer_edit.html index bbc00420..a1dbb4f9 100644 --- a/askbot/skins/default/templates/answer_edit.html +++ b/askbot/skins/default/templates/answer_edit.html @@ -16,7 +16,7 @@ <div style="vertical-align:middle"> {{ revision_form.revision }} <input type="submit" style="display:none" id="select_revision" name="select_revision" value="{% trans %}select revision{% endtrans %}"> </div> - {{ macros.edit_post(form) }} + {{ macros.edit_post(form, user = request.user) }} {% if settings.WIKI_ON and answer.wiki == False %} {{ macros.checkbox_in_div(form.wiki) }} {% endif %} diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html index 261c860f..4cc4e081 100644 --- a/askbot/skins/default/templates/macros.html +++ b/askbot/skins/default/templates/macros.html @@ -479,7 +479,8 @@ for the purposes of the AJAX comment editor #} post_form, post_type = None, mandatory_tags = None, - edit_title = False + edit_title = False, + user = None ) -%} {%include "widgets/edit_post.html" %} diff --git a/askbot/skins/default/templates/question/new_answer_form.html b/askbot/skins/default/templates/question/new_answer_form.html index 68af8afb..9868d3ba 100644 --- a/askbot/skins/default/templates/question/new_answer_form.html +++ b/askbot/skins/default/templates/question/new_answer_form.html @@ -39,7 +39,7 @@ {% endif %} </p> {% endif %} - {{ macros.edit_post(answer) }} + {{ macros.edit_post(answer, user = request.user) }} <input id="add-answer-btn" type="submit" class="submit after-editor" style="float:left"/> <script type="text/javascript"> askbot['functions']['renderAddAnswerButton'](); diff --git a/askbot/skins/default/templates/question_edit.html b/askbot/skins/default/templates/question_edit.html index 47873e0e..adf2f35b 100644 --- a/askbot/skins/default/templates/question_edit.html +++ b/askbot/skins/default/templates/question_edit.html @@ -20,7 +20,8 @@ form, post_type='question', edit_title=True, - mandatory_tags = mandatory_tags + mandatory_tags = mandatory_tags, + user = request.user ) }} <div class="after-editor"> diff --git a/askbot/skins/default/templates/widgets/ask_form.html b/askbot/skins/default/templates/widgets/ask_form.html index b8a5ce2c..2ece84d5 100644 --- a/askbot/skins/default/templates/widgets/ask_form.html +++ b/askbot/skins/default/templates/widgets/ask_form.html @@ -26,7 +26,8 @@ form, post_type = 'question', edit_title = False, - mandatory_tags = mandatory_tags + mandatory_tags = mandatory_tags, + user = request.user ) }} <div class="question-options"> 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/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 9a2de128..6bda3782 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -15,6 +15,7 @@ import time import urlparse from django.shortcuts import get_object_or_404 from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404 from django.utils import simplejson from django.utils.html import strip_tags, escape @@ -218,8 +219,10 @@ def ask(request):#view used to ask a new question ask_anonymously = form.cleaned_data['ask_anonymously'] if request.user.is_authenticated(): + + user = form.get_post_user(request.user) try: - question = request.user.post_question( + question = user.post_question( title = title, body_text = text, tags = tagnames, @@ -376,7 +379,9 @@ def edit_question(request, id): is_anon_edit = form.cleaned_data['stay_anonymous'] is_wiki = form.cleaned_data.get('wiki', question.wiki) - request.user.edit_question( + user = form.get_post_user(request.user) + + user.edit_question( question = question, title = form.cleaned_data['title'], body_text = form.cleaned_data['text'], @@ -446,7 +451,8 @@ def edit_answer(request, id): if form.is_valid(): if form.has_changed(): - request.user.edit_answer( + user = form.get_post_user(request.user) + user.edit_answer( answer = answer, body_text = form.cleaned_data['text'], revision_comment = form.cleaned_data['summary'], @@ -492,7 +498,10 @@ def answer(request, id):#process a new answer if request.user.is_authenticated(): try: follow = form.cleaned_data['email_notify'] - answer = request.user.post_answer( + + user = form.get_post_user(request.user) + + answer = user.post_answer( question = question, body_text = text, follow = follow, |