From 980989b786c77ef2a7a23e23545c402e1b072591 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Sun, 28 Oct 2012 05:37:03 -0300 Subject: added reputation limit to insert links into questions and answers --- askbot/conf/minimum_reputation.py | 9 +++++++ askbot/forms.py | 54 +++++++++++++++++++++++++++++-------- askbot/mail/__init__.py | 4 +-- askbot/models/post.py | 11 -------- askbot/models/question.py | 5 +++- askbot/tests/form_tests.py | 56 ++++++++++++++++++++++++++++++--------- askbot/views/commands.py | 2 +- askbot/views/readers.py | 2 +- askbot/views/widgets.py | 12 ++++++--- askbot/views/writers.py | 45 +++++++++++++++++-------------- 10 files changed, 137 insertions(+), 63 deletions(-) diff --git a/askbot/conf/minimum_reputation.py b/askbot/conf/minimum_reputation.py index 152a2079..359855c9 100644 --- a/askbot/conf/minimum_reputation.py +++ b/askbot/conf/minimum_reputation.py @@ -104,6 +104,15 @@ settings.register( ) ) +settings.register( + livesettings.IntegerValue( + MIN_REP, + 'MIN_REP_TO_INSERT_LINK', + default=10, + description=_('Insert links') + ) +) + settings.register( livesettings.IntegerValue( MIN_REP, diff --git a/askbot/forms.py b/askbot/forms.py index 1c5bc3d6..0a0f4f33 100644 --- a/askbot/forms.py +++ b/askbot/forms.py @@ -11,6 +11,7 @@ from django.utils.text import get_text_list from django.contrib.auth.models import User from django_countries import countries from askbot.utils.forms import NextUrlField, UserNameField +from askbot.utils.markup import URL_RE from askbot.mail import extract_first_email_address from recaptcha_works.fields import RecaptchaField from askbot.conf import settings as askbot_settings @@ -273,6 +274,11 @@ class EditorField(forms.CharField): min_length = 10 # sentinel default value def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) + if user is None: + raise ValueError('user parameter is required') + self.user = user + editor_attrs = kwargs.pop('editor_attrs', {}) super(EditorField, self).__init__(*args, **kwargs) self.required = True @@ -295,6 +301,21 @@ class EditorField(forms.CharField): self.min_length ) % self.min_length raise forms.ValidationError(msg) + + if re.search(URL_RE, value): + min_rep = askbot_settings.MIN_REP_TO_INSERT_LINK + if self.user.is_anonymous(): + raise forms.ValidationError( + _('Links or images cannot be inserted anonymously') + ) + elif self.user.reputation < min_rep: + raise forms.ValidationError( + ungettext_lazy( + 'At at least %d karma point is required to insert links', + 'At at least %d karma points are required to insert links', + min_rep + ) % min_rep + ) return value @@ -302,7 +323,10 @@ class QuestionEditorField(EditorField): """Editor field for the questions""" def __init__(self, *args, **kwargs): - super(QuestionEditorField, self).__init__(*args, **kwargs) + user = kwargs.pop('user', None) + super(QuestionEditorField, self).__init__( + user=user, *args, **kwargs + ) self.length_error_template_singular = \ 'question body must be > %d character' self.length_error_template_plural = \ @@ -477,10 +501,12 @@ class EditorForm(forms.Form): the field must be created dynamically, so it's added in the __init__() function""" - def __init__(self, editor_attrs=None): + def __init__(self, user=None, editor_attrs=None): super(EditorForm, self).__init__() editor_attrs = editor_attrs or {} - self.fields['editor'] = EditorField(editor_attrs=editor_attrs) + self.fields['editor'] = EditorField( + user=user, editor_attrs=editor_attrs + ) class DumpUploadForm(forms.Form): @@ -897,9 +923,10 @@ class AskForm(PostAsSomeoneForm, PostPrivatelyForm): ) def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) super(AskForm, self).__init__(*args, **kwargs) #it's important that this field is set up dynamically - self.fields['text'] = QuestionEditorField() + self.fields['text'] = QuestionEditorField(user=user) #hide ask_anonymously field if askbot_settings.ALLOW_ASK_ANONYMOUSLY is False: self.hide_field('ask_anonymously') @@ -932,11 +959,12 @@ class AskWidgetForm(forms.Form, FormWithHideableFields): ) def __init__(self, include_text=True, *args, **kwargs): + user = kwargs.pop('user', None) super(AskWidgetForm, self).__init__(*args, **kwargs) #hide ask_anonymously field if not askbot_settings.ALLOW_ASK_ANONYMOUSLY: self.hide_field('ask_anonymously') - self.fields['text'] = QuestionEditorField() + self.fields['text'] = QuestionEditorField(user=user) if not include_text: self.hide_field('text') #hack to make it validate @@ -1020,7 +1048,11 @@ class AskByEmailForm(forms.Form): 'required': ASK_BY_EMAIL_SUBJECT_HELP } ) - body_text = QuestionEditorField() + + def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) + super(AskByEmailForm, self).__init__(*args, **kwargs) + self.fields['body_text'] = QuestionEditorField(user=user) def clean_sender(self): """Cleans the :attr:`~askbot.forms.AskByEmail.sender` attribute @@ -1074,7 +1106,6 @@ class AskByEmailForm(forms.Form): class AnswerForm(PostAsSomeoneForm, PostPrivatelyForm): - text = AnswerEditorField() wiki = WikiField() openid = forms.CharField( required=False, max_length=255, @@ -1084,7 +1115,7 @@ class AnswerForm(PostAsSomeoneForm, PostPrivatelyForm): def __init__(self, *args, **kwargs): super(AnswerForm, self).__init__(*args, **kwargs) - self.fields['text'] = AnswerEditorField() + self.fields['text'] = AnswerEditorField(user=kwargs['user']) self.fields['email_notify'].widget.attrs['id'] = \ 'question-subscribe-updates' @@ -1169,11 +1200,11 @@ class EditQuestionForm(PostAsSomeoneForm, PostPrivatelyForm): def __init__(self, *args, **kwargs): """populate EditQuestionForm with initial data""" self.question = kwargs.pop('question') - self.user = kwargs['user']#preserve for superclass + self.user = kwargs.pop('user')#preserve for superclass revision = kwargs.pop('revision') super(EditQuestionForm, self).__init__(*args, **kwargs) #it is important to add this field dynamically - self.fields['text'] = QuestionEditorField() + self.fields['text'] = QuestionEditorField(user=self.user) self.fields['title'].initial = revision.title self.fields['text'].initial = revision.text self.fields['tags'].initial = revision.tagnames @@ -1275,9 +1306,10 @@ class EditAnswerForm(PostAsSomeoneForm, PostPrivatelyForm): def __init__(self, answer, revision, *args, **kwargs): self.answer = answer + user = kwargs.pop('user', None) super(EditAnswerForm, self).__init__(*args, **kwargs) #it is important to add this field dynamically - self.fields['text'] = AnswerEditorField() + self.fields['text'] = AnswerEditorField(user=user) self.fields['text'].initial = revision.text self.fields['wiki'].initial = answer.wiki diff --git a/askbot/mail/__init__.py b/askbot/mail/__init__.py index 74aa27e9..c8ec130e 100644 --- a/askbot/mail/__init__.py +++ b/askbot/mail/__init__.py @@ -362,10 +362,10 @@ def process_emailed_question( 'subject': subject, 'body_text': body_text } - form = AskByEmailForm(data) + user = User.objects.get(email__iexact = email_address) + form = AskByEmailForm(data, user=user) if form.is_valid(): email_address = form.cleaned_data['email'] - user = User.objects.get(email__iexact = email_address) if user.can_post_by_email() is False: raise PermissionDenied(messages.insufficient_reputation(user)) diff --git a/askbot/models/post.py b/askbot/models/post.py index daf9c93f..62fdcc46 100644 --- a/askbot/models/post.py +++ b/askbot/models/post.py @@ -884,17 +884,6 @@ class Post(models.Model): if parent_post is None: break quote_level += 1 - """ - output += '

' - output += _( - 'In reply to %(user)s %(post)s of %(date)s' - ) % { - 'user': parent_post.author.username, - 'post': _(parent_post.post_type), - 'date': parent_post.added_at.strftime(const.DATETIME_FORMAT) - } - output += '

' - """ output += parent_post.format_for_email( quote_level = quote_level, format = 'parent_subthread' diff --git a/askbot/models/question.py b/askbot/models/question.py index 7faf589b..43844c82 100644 --- a/askbot/models/question.py +++ b/askbot/models/question.py @@ -702,7 +702,10 @@ class Thread(models.Model): def format_for_email(self, user=None): """experimental function: output entire thread for email""" - question, answers, junk, published_ans_ids = self.get_cached_post_data(user=user) + + question, answers, junk, published_ans_ids = \ + self.get_cached_post_data(user=user) + output = question.format_for_email_as_subthread() if answers: answer_heading = ungettext( diff --git a/askbot/tests/form_tests.py b/askbot/tests/form_tests.py index 90f4f4f2..21286b9b 100644 --- a/askbot/tests/form_tests.py +++ b/askbot/tests/form_tests.py @@ -1,5 +1,7 @@ from django import forms as django_forms +from django.contrib.auth.models import AnonymousUser from askbot.tests.utils import AskbotTestCase +from askbot.tests.utils import with_settings from askbot.conf import settings as askbot_settings from askbot import forms from askbot.utils import forms as util_forms @@ -53,9 +55,10 @@ class AskByEmailFormTests(AskbotTestCase): and makes sure that tags and title are parsed out""" setting_backup = askbot_settings.TAGS_ARE_REQUIRED askbot_settings.update('TAGS_ARE_REQUIRED', True) + user = AnonymousUser() for test_case in SUBJECT_LINE_CASES: self.data['subject'] = test_case[0] - form = forms.AskByEmailForm(self.data) + form = forms.AskByEmailForm(self.data, user=user) output = test_case[1] if output is None: self.assertFalse(form.is_valid()) @@ -75,10 +78,11 @@ class AskByEmailFormTests(AskbotTestCase): """loops through variants of the from field in the emails and tests the email address extractor""" + user = AnonymousUser() for test_case in EMAIL_CASES: self.data['sender'] = test_case[0] expected_result = test_case[1] - form = forms.AskByEmailForm(self.data) + form = forms.AskByEmailForm(self.data, user=user) if expected_result is None: self.assertFalse(form.is_valid()) else: @@ -196,9 +200,9 @@ class EditQuestionAnonymouslyFormTests(AskbotTestCase): data['reveal_identity'] = 'on' self.form = forms.EditQuestionForm( data, - question = question, - user = editor, - revision = question.get_latest_revision(), + question=question, + user=editor, + revision=question.get_latest_revision(), ) def test_reveal_identity_field(self): @@ -230,7 +234,7 @@ class AskFormTests(AskbotTestCase): } if ask_anonymously == True: data['ask_anonymously'] = 'on' - self.form = forms.AskForm(data) + self.form = forms.AskForm(data, user=AnonymousUser()) self.form.full_clean() def assert_anon_is(self, value): @@ -317,7 +321,7 @@ class AnswerEditorFieldTests(AskbotTestCase): def setUp(self): self.old_min_length = askbot_settings.MIN_ANSWER_BODY_LENGTH askbot_settings.update('MIN_ANSWER_BODY_LENGTH', 10) - self.field = forms.AnswerEditorField() + self.field = forms.AnswerEditorField(user=AnonymousUser()) def tearDown(self): askbot_settings.update('MIN_ANSWER_BODY_LENGTH', self.old_min_length) @@ -364,8 +368,6 @@ class PostAsSomeoneFormTests(AskbotTestCase): class AskWidgetFormTests(AskbotTestCase): - form = forms.AskWidgetForm - def setUp(self): self.good_data = {'title': "What's the price of a house in london?"} self.good_data_anon = {'title': "What's the price of a house in london?", @@ -374,12 +376,40 @@ class AskWidgetFormTests(AskbotTestCase): self.bad_data = {'title': ''} def test_valid_input(self): - form_object = self.form(include_text=False, data=self.good_data) - print form_object.errors + form_object = forms.AskWidgetForm( + include_text=False, data=self.good_data, user=AnonymousUser() + ) self.assertTrue(form_object.is_valid()) - form_object = self.form(include_text=False, data=self.good_data_anon) + form_object = forms.AskWidgetForm( + include_text=False, data=self.good_data_anon, user=AnonymousUser() + ) self.assertTrue(form_object.is_valid()) def test_invalid_input(self): - form_object = self.form(False, data=self.bad_data) + form_object = forms.AskWidgetForm( + include_text=False, data=self.bad_data, user=AnonymousUser() + ) self.assertFalse(form_object.is_valid()) + + +TEXT_WITH_LINK = 'blah blah http://example.com/url/image.png' +class EditorFieldTests(AskbotTestCase): + + def setUp(self): + self.user = self.create_user('user') + self.user.reputation = 5 + self.user.save() + + @with_settings(EDITOR_TYPE='markdown', MIN_REP_TO_INSERT_LINK=10) + def test_low_rep_user_cannot_post_links_markdown(self): + field = forms.EditorField(user=self.user) + self.assertRaises( + django_forms.ValidationError, field.clean, TEXT_WITH_LINK + ) + + @with_settings(EDITOR_TYPE='tinymce', MIN_REP_TO_INSERT_LINK=10) + def test_low_rep_user_cannot_post_links_tinymce(self): + field = forms.EditorField(user=self.user) + self.assertRaises( + django_forms.ValidationError, field.clean, TEXT_WITH_LINK + ) diff --git a/askbot/views/commands.py b/askbot/views/commands.py index 698bf2db..c072f7b8 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -1348,7 +1348,7 @@ def get_editor(request): if 'config' not in request.GET: return HttpResponseForbidden() config = simplejson.loads(request.GET['config']) - form = forms.EditorForm(editor_attrs=config) + form = forms.EditorForm(editor_attrs=config, user=request.user) editor_html = render_text_into_skin( '{{ form.media }} {{ form.editor }}', {'form': form}, diff --git a/askbot/views/readers.py b/askbot/views/readers.py index 5a3e378b..9801d5bc 100644 --- a/askbot/views/readers.py +++ b/askbot/views/readers.py @@ -549,7 +549,7 @@ def question(request, id):#refactor - long subroutine. display question body, an if drafts.count() > 0: initial['text'] = drafts[0].text - answer_form = AnswerForm(initial) + answer_form = AnswerForm(initial, user=request.user) user_can_post_comment = ( request.user.is_authenticated() and request.user.can_post_comment() diff --git a/askbot/views/widgets.py b/askbot/views/widgets.py index 8699cdf1..4401bbdc 100644 --- a/askbot/views/widgets.py +++ b/askbot/views/widgets.py @@ -61,8 +61,11 @@ def ask_widget(request, widget_id): widget = get_object_or_404(models.AskWidget, id=widget_id) if request.method == "POST": - form = forms.AskWidgetForm(include_text=widget.include_text_field, - data=request.POST) + form = forms.AskWidgetForm( + include_text=widget.include_text_field, + data=request.POST, + user=request.user + ) if form.is_valid(): ask_anonymously = form.cleaned_data['ask_anonymously'] title = form.cleaned_data['title'] @@ -116,7 +119,10 @@ def ask_widget(request, widget_id): next_url = '%s?next=%s' % (reverse('widget_signin'), reverse('ask_by_widget')) return redirect(next_url) - form = forms.AskWidgetForm(include_text=widget.include_text_field) + form = forms.AskWidgetForm( + include_text=widget.include_text_field, + user=request.user + ) data = { 'form': form, diff --git a/askbot/views/writers.py b/askbot/views/writers.py index db7a24d2..d1504d23 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -211,7 +211,7 @@ def ask(request):#view used to ask a new question user can start posting a question anonymously but then must login/register in order for the question go be shown """ - form = forms.AskForm(request.REQUEST) + form = forms.AskForm(request.REQUEST, user=request.user) if request.method == 'POST': if form.is_valid(): timestamp = datetime.datetime.now() @@ -264,7 +264,7 @@ def ask(request):#view used to ask a new question return HttpResponseRedirect(url_utils.get_login_url()) if request.method == 'GET': - form = forms.AskForm() + form = forms.AskForm(user=request.user) draft_title = '' draft_text = '' @@ -386,24 +386,24 @@ def edit_question(request, id): rev_id = revision_form.cleaned_data['revision'] revision = question.revisions.get(revision = rev_id) form = forms.EditQuestionForm( - question = question, - user = request.user, - revision = revision + question=question, + user=request.user, + revision=revision ) else: form = forms.EditQuestionForm( request.POST, - question = question, - user = request.user, - revision = revision + question=question, + user=quest.user, + revision=revision ) else:#new content edit # Always check modifications against the latest revision form = forms.EditQuestionForm( request.POST, - question = question, - revision = revision, - user = request.user, + question=question, + revision=revision, + user=request.user, ) revision_form = forms.RevisionForm(question, revision) if form.is_valid(): @@ -437,10 +437,10 @@ def edit_question(request, id): 'wiki': question.wiki } form = forms.EditQuestionForm( - question = question, - revision = revision, - user = request.user, - initial = initial + question=question, + revision=revision, + user=request.user, + initial=initial ) data = { @@ -481,15 +481,20 @@ def edit_answer(request, id): # Replace with those from the selected revision rev = revision_form.cleaned_data['revision'] revision = answer.revisions.get(revision = rev) - form = forms.EditAnswerForm(answer, revision) + form = forms.EditAnswerForm( + answer, revision, user=request.user + ) else: form = forms.EditAnswerForm( answer, revision, - request.POST + request.POST, + user=request.user ) else: - form = forms.EditAnswerForm(answer, revision, request.POST) + form = forms.EditAnswerForm( + answer, revision, request.POST, user=request.user + ) revision_form = forms.RevisionForm(answer, revision) if form.is_valid(): @@ -506,7 +511,7 @@ def edit_answer(request, id): return HttpResponseRedirect(answer.get_absolute_url()) else: revision_form = forms.RevisionForm(answer, revision) - form = forms.EditAnswerForm(answer, revision) + form = forms.EditAnswerForm(answer, revision, user=request.user) if request.user.can_make_group_private_posts(): form.initial['post_privately'] = answer.is_private() data = { @@ -536,7 +541,7 @@ def answer(request, id):#process a new answer """ question = get_object_or_404(models.Post, post_type='question', id=id) if request.method == "POST": - form = forms.AnswerForm(request.POST) + form = forms.AnswerForm(request.POST, user=request.user) if form.is_valid(): wiki = form.cleaned_data['wiki'] text = form.cleaned_data['text'] -- cgit v1.2.3-1-g7c22