diff options
-rw-r--r-- | askbot/conf/forum_data_rules.py | 41 | ||||
-rw-r--r-- | askbot/doc/source/changelog.rst | 3 | ||||
-rw-r--r-- | askbot/forms.py | 49 | ||||
-rw-r--r-- | askbot/models/__init__.py | 8 | ||||
-rw-r--r-- | askbot/models/content.py | 2 | ||||
-rw-r--r-- | askbot/skins/common/media/js/post.js | 51 | ||||
-rw-r--r-- | askbot/skins/default/media/style/style.less | 6 | ||||
-rw-r--r-- | askbot/skins/default/templates/answer_edit.html | 6 | ||||
-rw-r--r-- | askbot/skins/default/templates/meta/editor_data.html | 3 | ||||
-rw-r--r-- | askbot/skins/default/templates/question/javascript.html | 6 | ||||
-rw-r--r-- | askbot/tests/db_api_tests.py | 9 | ||||
-rw-r--r-- | askbot/tests/form_tests.py | 24 | ||||
-rw-r--r-- | askbot/urls.py | 9 | ||||
-rw-r--r-- | askbot/views/commands.py | 2 |
14 files changed, 182 insertions, 37 deletions
diff --git a/askbot/conf/forum_data_rules.py b/askbot/conf/forum_data_rules.py index 80af2f5b..d6d2efd3 100644 --- a/askbot/conf/forum_data_rules.py +++ b/askbot/conf/forum_data_rules.py @@ -89,6 +89,37 @@ settings.register( ) settings.register( + livesettings.IntegerValue( + FORUM_DATA_RULES, + 'MIN_TITLE_LENGTH', + default=10, + description=_('Minimum length of title (number of characters)') + ) +) + +settings.register( + livesettings.IntegerValue( + FORUM_DATA_RULES, + 'MIN_QUESTION_BODY_LENGTH', + default=10, + description=_( + 'Minimum length of question body (number of characters)' + ) + ) +) + +settings.register( + livesettings.IntegerValue( + FORUM_DATA_RULES, + 'MIN_ANSWER_BODY_LENGTH', + default=10, + description=_( + 'Minimum length of answer body (number of characters)' + ) + ) +) + +settings.register( livesettings.StringValue( FORUM_DATA_RULES, 'MANDATORY_TAGS', @@ -109,11 +140,11 @@ settings.register( default = False, description = _('Force lowercase the tags'), help_text = _( - 'Attention: after checking this, please back up the database, ' - 'and run a management command: ' - '<code>python manage.py fix_question_tags</code> to globally ' - 'rename the tags' - ) + 'Attention: after checking this, please back up the database, ' + 'and run a management command: ' + '<code>python manage.py fix_question_tags</code> to globally ' + 'rename the tags' + ) ) ) diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst index a3e4ef05..7ef46ffe 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -10,6 +10,9 @@ Development version (not released yet) * made askbot compatible with django's `CSRFViewMiddleware` that may be used for other projects (Evgeny) * added more rigorous test for the user name to make it slug safe (Evgeny) +* made setting `ASKBOT_FILE_UPLOAD_DIR` work (Radim Řehůřek) +* added minimal length of question title ond body + text to live settings and allowed body-less questions (Radim Řehůřek, Evgeny) 0.7.36 (Dec 20, 2011) --------------------- diff --git a/askbot/forms.py b/askbot/forms.py index d2215189..00735b72 100644 --- a/askbot/forms.py +++ b/askbot/forms.py @@ -106,12 +106,24 @@ class TitleField(forms.CharField): self.initial = '' def clean(self, value): - if len(value) < 10: - raise forms.ValidationError(_('title must be > 10 characters')) + if len(value) < askbot_settings.MIN_TITLE_LENGTH: + msg = ungettext_lazy( + 'title must be > %d character', + 'title must be > %d characters', + askbot_settings.MIN_TITLE_LENGTH + ) % askbot_settings.MIN_TITLE_LENGTH + raise forms.ValidationError(msg) return value class EditorField(forms.CharField): + """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 + def __init__(self, *args, **kwargs): super(EditorField, self).__init__(*args, **kwargs) self.required = True @@ -121,10 +133,29 @@ class EditorField(forms.CharField): self.initial = '' def clean(self, value): - if len(value) < 10: - raise forms.ValidationError(_('question content must be > 10 characters')) + if len(value) < self.min_length: + msg = ungettext_lazy( + self.length_error_template_singular, + self.length_error_template_plural, + self.min_length + ) % self.min_length + raise forms.ValidationError(msg) return value +class QuestionEditorField(EditorField): + 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.min_length = askbot_settings.MIN_QUESTION_BODY_LENGTH + +class AnswerEditorField(EditorField): + 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): def __init__(self, *args, **kwargs): super(TagNamesField, self).__init__(*args, **kwargs) @@ -610,7 +641,7 @@ class AskForm(forms.Form, FormWithHideableFields): settings forbids anonymous asking """ title = TitleField() - text = EditorField() + text = QuestionEditorField() tags = TagNamesField() wiki = WikiField() ask_anonymously = forms.BooleanField( @@ -661,7 +692,7 @@ class AskByEmailForm(forms.Form): """ sender = forms.CharField(max_length = 255) subject = forms.CharField(max_length = 255) - body_text = EditorField() + body_text = QuestionEditorField() def clean_sender(self): """Cleans the :attr:`~askbot.forms.AskByEmail.sender` attribute @@ -706,7 +737,7 @@ class AskByEmailForm(forms.Form): return self.cleaned_data['subject'] class AnswerForm(forms.Form): - text = EditorField() + 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})) @@ -770,7 +801,7 @@ class RevisionForm(forms.Form): class EditQuestionForm(forms.Form, FormWithHideableFields): title = TitleField() - text = EditorField() + text = QuestionEditorField() tags = TagNamesField() summary = SummaryField() wiki = WikiField() @@ -877,7 +908,7 @@ class EditQuestionForm(forms.Form, FormWithHideableFields): return self.cleaned_data class EditAnswerForm(forms.Form): - text = EditorField() + text = AnswerEditorField() summary = SummaryField() wiki = WikiField() diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index d43b0e53..0ba2e4c7 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -1264,7 +1264,7 @@ def user_restore_post( def user_post_question( self, title = None, - body_text = None, + body_text = '', tags = None, wiki = False, is_anonymous = False, @@ -1275,10 +1275,11 @@ def user_post_question( self.assert_can_post_question() + if body_text == '':#a hack to allow bodyless question + body_text = ' ' + if title is None: raise ValueError('Title is required to post question') - if body_text is None: - raise ValueError('Text body is required to post question') if tags is None: raise ValueError('Tags are required to post question') if timestamp is None: @@ -1371,6 +1372,7 @@ def user_post_answer( timestamp = None ): + #todo: move this to assertion - user_assert_can_post_answer if self == question.author and not self.is_administrator(): # check date and rep required to post answer to own question diff --git a/askbot/models/content.py b/askbot/models/content.py index f2645cc2..ca747cc8 100644 --- a/askbot/models/content.py +++ b/askbot/models/content.py @@ -691,6 +691,8 @@ class Content(models.Model): self.parse_and_save(author = edited_by) def apply_edit(self, *kargs, **kwargs): + if kwargs['text'] == '': + kwargs['text'] = ' '#a hack allowing empty body text in posts if self.is_answer(): return self._answer__apply_edit(*kargs, **kwargs) elif self.is_question(): diff --git a/askbot/skins/common/media/js/post.js b/askbot/skins/common/media/js/post.js index 22e2b6c4..2f2fbd75 100644 --- a/askbot/skins/common/media/js/post.js +++ b/askbot/skins/common/media/js/post.js @@ -112,17 +112,14 @@ var CPValidator = function(){ limit_tag_length: true }, text: { - required: true, - minlength: 10 + minlength: askbot['settings']['minQuestionBodyLength'] + }, + title: { + minlength: askbot['settings']['minTitleLength'] } - /*title: { - required: true, - minlength: 10 - }*/ }; }, getQuestionFormMessages: function(){ - var chars = 10 return { tags: { required: " " + gettext('tags cannot be empty'), @@ -132,13 +129,49 @@ var CPValidator = function(){ }, text: { required: " " + gettext('content cannot be empty'), - minlength: interpolate(gettext('%s content minchars'), [chars]) + minlength: interpolate( + ngettext( + 'question body must be > %s character', + 'question body must be > %s characters', + askbot['settings']['minQuestionBodyLength'] + ), + [askbot['settings']['minQuestionBodyLength'], ] + ) }, title: { required: " " + gettext('please enter title'), - minlength: interpolate(gettext('%s title minchars'), [chars]) + minlength: interpolate( + ngettext( + 'title must be > %s character', + 'title must be > %s characters', + askbot['settings']['minTitleLength'] + ), + [askbot['settings']['minTitleLength'], ] + ) } }; + }, + getAnswerFormRules : function(){ + return { + text: { + minlength: askbot['settings']['minAnswerBodyLength'] + }, + }; + }, + getAnswerFormMessages: function(){ + return { + text: { + required: " " + gettext('content cannot be empty'), + minlength: interpolate( + ngettext( + 'answer must be > %s character', + 'answer must be > %s characters', + askbot['settings']['minAnswerBodyLength'] + ), + [askbot['settings']['minAnswerBodyLength'], ] + ) + }, + } } }; }(); diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less index d1ca6283..389a0acc 100644 --- a/askbot/skins/default/media/style/style.less +++ b/askbot/skins/default/media/style/style.less @@ -1383,7 +1383,7 @@ ul#related-tags li { border-top:0; padding:10px; margin-bottom:10px; - width:710px; + width:717px; } #id_title { @@ -2667,6 +2667,10 @@ span.form-error { margin-left: 5px; } +ul.errorlist { + margin-bottom: 0; +} + p.form-item { margin: 0px; } diff --git a/askbot/skins/default/templates/answer_edit.html b/askbot/skins/default/templates/answer_edit.html index bbf3edef..28824968 100644 --- a/askbot/skins/default/templates/answer_edit.html +++ b/askbot/skins/default/templates/answer_edit.html @@ -67,7 +67,11 @@ $('#pre-collapse').text(txt); }); - setupFormValidation($("#fmedit"), CPValidator.getQuestionFormRules(), CPValidator.getQuestionFormMessages()); + setupFormValidation( + $("#fmedit"), + CPValidator.getAnswerFormRules(), + CPValidator.getAnswerFormMessages() + ); $('#id_revision').unbind().change(function(){ $("#select_revision").click(); diff --git a/askbot/skins/default/templates/meta/editor_data.html b/askbot/skins/default/templates/meta/editor_data.html index 79ed96fb..025be8a0 100644 --- a/askbot/skins/default/templates/meta/editor_data.html +++ b/askbot/skins/default/templates/meta/editor_data.html @@ -7,4 +7,7 @@ askbot['messages']['maxTagsPerPost'] = '{% trans tag_count = settings.MAX_TAGS_PER_POST %}please use {{tag_count}} tag{% pluralize %}please use {{tag_count}} tags or less{% endtrans %}'; askbot['messages']['tagLimits'] = '{% trans tag_count=settings.MAX_TAGS_PER_POST, max_chars=settings.MAX_TAG_LENGTH %}please use up to {{tag_count}} tags, less than {{max_chars}} characters each{% endtrans %}'; askbot['urls']['upload'] = '{% url "upload" %}'; + askbot['settings']['minTitleLength'] = {{settings.MIN_TITLE_LENGTH}} + askbot['settings']['minQuestionBodyLength'] = {{settings.MIN_QUESTION_BODY_LENGTH}} + askbot['settings']['minAnswerBodyLength'] = {{settings.MIN_ANSWER_BODY_LENGTH}} </script> diff --git a/askbot/skins/default/templates/question/javascript.html b/askbot/skins/default/templates/question/javascript.html index af1c7056..7f90627b 100644 --- a/askbot/skins/default/templates/question/javascript.html +++ b/askbot/skins/default/templates/question/javascript.html @@ -94,7 +94,11 @@ $('#previewer').toggle(); $('#pre-collapse').text(txt); }); - setupFormValidation($("#fmanswer"), CPValidator.getQuestionFormRules(), CPValidator.getQuestionFormMessages()); + setupFormValidation( + $("#fmanswer"), + CPValidator.getAnswerFormRules(), + CPValidator.getAnswerFormMessages() + ); } </script> {% include "meta/editor_data.html" %} diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py index b54bb2e9..585784aa 100644 --- a/askbot/tests/db_api_tests.py +++ b/askbot/tests/db_api_tests.py @@ -7,6 +7,7 @@ from django.core import exceptions from django.core.urlresolvers import reverse from django.test.client import Client from django.conf import settings +from django import forms from askbot.tests.utils import AskbotTestCase from askbot import models from askbot import const @@ -74,6 +75,14 @@ class DBApiTests(AskbotTestCase): rev = q.revisions.all()[0] self.assertTrue(rev.is_anonymous) + def test_post_bodyless_question(self): + q = self.user.post_question( + body_text = '', + title = 'aeuaouaousaotuhao', + tags = 'test' + ) + self.assertEquals(q.text.strip(), '') + def test_reveal_asker_identity(self): q = self.ask_anonymous_question() self.other_user.set_status('m') diff --git a/askbot/tests/form_tests.py b/askbot/tests/form_tests.py index 5235b13a..22f2a77c 100644 --- a/askbot/tests/form_tests.py +++ b/askbot/tests/form_tests.py @@ -306,3 +306,27 @@ class UserNameFieldTest(AskbotTestCase): self.assertRaises(django_forms.ValidationError, self.username_field.clean, '......') #TODO: test more things + +class AnswerEditorFieldTests(AskbotTestCase): + """don't need to test the QuestionEditorFieldTests, b/c the + class is identical""" + 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() + + def tearDown(self): + askbot_settings.update('MIN_ANSWER_BODY_LENGTH', self.old_min_length) + + def test_fail_short_body(self): + self.assertRaises( + django_forms.ValidationError, + self.field.clean, + 'a' + ) + + def test_pass_long_body(self): + self.assertEquals( + self.field.clean(10*'a'), + 10*'a' + ) diff --git a/askbot/urls.py b/askbot/urls.py index 2c3d143d..4bfab225 100644 --- a/askbot/urls.py +++ b/askbot/urls.py @@ -41,15 +41,10 @@ urlpatterns = patterns('', views.meta.media, name='askbot_media', ), - url( + url( # TODO: replace with django.conf.urls.static ? r'^%s(?P<path>.*)$' % settings.ASKBOT_UPLOADED_FILES_URL, 'django.views.static.serve', - {'document_root': os.path.join( - settings.PROJECT_ROOT, - 'askbot', - 'upfiles' - ).replace('\\','/') - }, + {'document_root': settings.ASKBOT_FILE_UPLOAD_DIR.replace('\\','/')}, name='uploaded_file', ), #no translation for this url!! diff --git a/askbot/views/commands.py b/askbot/views/commands.py index 3aeba161..7e219572 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -458,7 +458,7 @@ def api_get_questions(request): questions = models.Question.objects.get_by_text_query(query) if should_show_sort_by_relevance(): questions = questions.extra(order_by = ['-relevance']) - questions = questions.distinct() + questions = questions.filter(deleted = False).distinct() page_size = form.cleaned_data.get('page_size', 30) questions = questions[:page_size] |