summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x.gitignore1
-rw-r--r--MANIFEST.in1
-rw-r--r--askbot/conf/forum_data_rules.py14
-rw-r--r--askbot/forms.py30
-rw-r--r--askbot/models/tag.py13
-rw-r--r--askbot/skins/default/templates/ask.html3
-rw-r--r--askbot/skins/default/templates/blocks/ask_form.html9
-rw-r--r--askbot/skins/default/templates/blocks/mandatory_tags_js.html25
-rw-r--r--askbot/skins/default/templates/macros.html30
-rw-r--r--askbot/skins/default/templates/question_edit.html14
-rw-r--r--askbot/tests/form_tests.py24
-rw-r--r--askbot/views/writers.py2
12 files changed, 158 insertions, 8 deletions
diff --git a/.gitignore b/.gitignore
index 98ff79a4..207896f6 100755
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ settings_local.py
settings.py
.idea
*.iml
+lint
env
django
django/*
diff --git a/MANIFEST.in b/MANIFEST.in
index 0f0423d0..4788b4eb 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -6,6 +6,7 @@ recursive-exclude .git
prune dist
prune tmp
prune build
+exclude lint
exclude settings.py
exclude manage.py
exclude __init__.py
diff --git a/askbot/conf/forum_data_rules.py b/askbot/conf/forum_data_rules.py
index dc801d41..570b8b9e 100644
--- a/askbot/conf/forum_data_rules.py
+++ b/askbot/conf/forum_data_rules.py
@@ -87,6 +87,20 @@ settings.register(
)
settings.register(
+ livesettings.StringValue(
+ FORUM_DATA_RULES,
+ 'MANDATORY_TAGS',
+ description = _('Mandatory tags'),
+ default = '',
+ help_text = _(
+ 'At least one of these tags will be required for any new '
+ 'or newly edited question. A mandatory tag may be wildcard, '
+ 'if the wildcard tags are active.'
+ )
+ )
+)
+
+settings.register(
livesettings.BooleanValue(
FORUM_DATA_RULES,
'FORCE_LOWERCASE_TAGS',
diff --git a/askbot/forms.py b/askbot/forms.py
index 2ab67213..57775ed0 100644
--- a/askbot/forms.py
+++ b/askbot/forms.py
@@ -4,6 +4,7 @@ from askbot import models
from askbot import const
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
+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
@@ -135,6 +136,27 @@ class TagNamesField(forms.CharField):
self.help_text = _('Tags are short keywords, with no spaces within. Up to five tags can be used.')
self.initial = ''
+ def need_mandatory_tags(self):
+ """true, if list of mandatory tags is not empty"""
+ return 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``"""
+ 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):
value = super(TagNamesField, self).clean(value)
data = value.strip()
@@ -152,6 +174,14 @@ class TagNamesField(forms.CharField):
'please use %(tag_count)d tags or less',
tag_count) % {'tag_count':max_tags}
raise forms.ValidationError(msg)
+
+ if self.need_mandatory_tags():
+ if self.mandatory_tag_missing(tag_strings):
+ msg = _(
+ 'At least one of the following tags is required : %(tags)s'
+ ) % {'tags': get_text_list(models.tag.get_mandatory_tags())}
+ raise forms.ValidationError(msg)
+
for tag in tag_strings:
tag_length = len(tag)
if tag_length > askbot_settings.MAX_TAG_LENGTH:
diff --git a/askbot/models/tag.py b/askbot/models/tag.py
index 73aa328f..e114cef7 100644
--- a/askbot/models/tag.py
+++ b/askbot/models/tag.py
@@ -1,9 +1,11 @@
+import re
from django.db import models
from django.db import connection, transaction
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from askbot.models.base import DeletableContent
from askbot.models.base import BaseQuerySetManager
+from askbot import const
def tags_match_some_wildcard(tag_names, wildcard_tags):
"""Same as
@@ -16,6 +18,17 @@ def tags_match_some_wildcard(tag_names, wildcard_tags):
return True
return False
+def get_mandatory_tags():
+ """returns list of mandatory tags,
+ or an empty list, if there aren't any"""
+ from askbot.conf import settings as askbot_settings
+ raw_mandatory_tags = askbot_settings.MANDATORY_TAGS.strip()
+ if len(raw_mandatory_tags) == 0:
+ return []
+ else:
+ split_re = re.compile(const.TAG_SPLIT_REGEX)
+ return split_re.split(raw_mandatory_tags)
+
class TagQuerySet(models.query.QuerySet):
UPDATE_USED_COUNTS_QUERY = """
UPDATE tag
diff --git a/askbot/skins/default/templates/ask.html b/askbot/skins/default/templates/ask.html
index 508cc16e..9aaa7e8c 100644
--- a/askbot/skins/default/templates/ask.html
+++ b/askbot/skins/default/templates/ask.html
@@ -21,6 +21,9 @@
</script>
<script type='text/javascript' src='{{"/js/live_search.js"|media}}'></script>
{% include "blocks/editor_data.html" %}
+ {% if mandatory_tags %}
+ {% include "blocks/mandatory_tags_js.html" %}
+ {% endif %}
<script type='text/javascript'>
askbot['urls']['api_get_questions'] = '{% url api_get_questions %}';
{% if settings.ENABLE_MATHJAX or settings.MARKUP_CODE_FRIENDLY %}
diff --git a/askbot/skins/default/templates/blocks/ask_form.html b/askbot/skins/default/templates/blocks/ask_form.html
index 24196bb6..606aef78 100644
--- a/askbot/skins/default/templates/blocks/ask_form.html
+++ b/askbot/skins/default/templates/blocks/ask_form.html
@@ -22,7 +22,14 @@
</div>
</div>
<div id='question-list'></div>
- {{macros.edit_post(form, post_type='question', edit_title=False)}}
+ {{
+ macros.edit_post(
+ form,
+ post_type = 'question',
+ edit_title = False,
+ mandatory_tags = mandatory_tags
+ )
+ }}
{% if not request.user.is_authenticated() %}
<input type="submit" name="post_anon" value="{% trans %}Login/signup to post your question{% endtrans %}" class="submit" />
{% else %}
diff --git a/askbot/skins/default/templates/blocks/mandatory_tags_js.html b/askbot/skins/default/templates/blocks/mandatory_tags_js.html
new file mode 100644
index 00000000..f04a6345
--- /dev/null
+++ b/askbot/skins/default/templates/blocks/mandatory_tags_js.html
@@ -0,0 +1,25 @@
+<script type="text/javascript">
+ $(document).ready(function(){
+ var fake_tag = new Tag();
+ $('ul.tags .tag').each(function(idx, elem){
+ var tag_input = $('input#id_tags');
+ var callback = function(){
+ var new_tag = fake_tag.decodeTagName($(elem).html());
+ if (/\*$/.test(new_tag)){//strip the asterisk
+ new_tag = new_tag.substring(0, new_tag.length - 1);
+ }
+ if ($.trim(tag_input.val()) !== ''){
+ var entered_tags = tag_input.val().split(/\s+/);
+ } else {
+ var entered_tags = [];
+ }
+ if ($.inArray(new_tag, entered_tags) === -1){
+ entered_tags.push(new_tag);
+ tag_input.val(entered_tags.join(' '));
+ tag_input.focus();
+ }
+ };
+ setupButtonEventHandlers($(elem), callback);
+ });
+ });
+</script>
diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html
index 23177c0b..987b3332 100644
--- a/askbot/skins/default/templates/macros.html
+++ b/askbot/skins/default/templates/macros.html
@@ -353,6 +353,7 @@ poor design of the data or methods on data objects #}
<{{ html_tag }} class="tag-left{% if deletable %} deletable-tag{% endif %}">
<{% if not is_link or tag[-1] == '*' %}span{% else %}a{% endif %}
class="tag tag-right{% if css_class %} {{ css_class }}{% endif %}"
+ {% if is_link %}
href="{% url questions %}?tags={{tag|urlencode}}{{
if_else(
url_params != None,
@@ -360,7 +361,9 @@ poor design of the data or methods on data objects #}
''
)|escape
}}"
- title="{% trans %}see questions tagged '{{ tag }}'{% endtrans %}" rel="tag"
+ title="{% trans %}see questions tagged '{{ tag }}'{% endtrans %}"
+ {% endif %}
+ rel="tag"
>{{ tag|replace('*', '&#10045;') }}</{% if not is_link or tag[-1] == '*' %}span{% else %}a{% endif %}>
{% if deletable %}
<span class="delete-icon"
@@ -612,8 +615,9 @@ poor design of the data or methods on data objects #}
{%- macro edit_post(
post_form,
- post_type=None,
- edit_title=False
+ post_type = None,
+ mandatory_tags = None,
+ edit_title = False
)
-%}
{% if edit_title %}
@@ -633,8 +637,24 @@ poor design of the data or methods on data objects #}
{# need label element for resizable input, b/c form validation won't find span #}
{% if post_type == 'question' %}
<div class="form-item">
- <strong>{{ post_form.tags.label_tag() }}:</strong>
- {% trans %}(required){% endtrans %}
+ {% if mandatory_tags %}
+ <label for="id_tags">
+ <strong>{% trans %}tags{% endtrans %},</strong>
+ {% trans %}one of these is required{% endtrans %}
+ </label>
+ {{
+ tag_list_widget(
+ mandatory_tags,
+ make_links = False,
+ css_class = 'clearfix'
+ )
+ }}
+ {% else %}
+ <label for="id_tags">
+ <strong>{% trans %}tags{% endtrans %}:</strong>
+ {% trans %}(required){% endtrans %}
+ </label>
+ {% endif %}
<span class="form-error">{{ post_form.tags.errors }}</span><br/>
{{ post_form.tags }}
<div class="title-desc">
diff --git a/askbot/skins/default/templates/question_edit.html b/askbot/skins/default/templates/question_edit.html
index 6a55e58c..feee2521 100644
--- a/askbot/skins/default/templates/question_edit.html
+++ b/askbot/skins/default/templates/question_edit.html
@@ -8,14 +8,21 @@
{% block content %}
<h1>{% trans %}Edit question{% endtrans %} [<a href="{{ question.get_absolute_url() }}">{% trans %}back{% endtrans %}</a>]</h1>
<form id="fmedit" action="{% url edit_question question.id %}" method="post" >{% csrf_token %}
- <label for="id_revision" ><strong>{% trans %}revision{% endtrans %}:</strong></label> <br/>
+
{% if revision_form.revision.errors %}{{ revision_form.revision.errors.as_ul() }}{% endif %}
<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, post_type='question', edit_title=True,) }}
+ {{
+ macros.edit_post(
+ form,
+ post_type='question',
+ edit_title=True,
+ mandatory_tags = mandatory_tags
+ )
+ }}
<div class="after-editor">
<input type="submit" value="{% trans %}Save edit{% endtrans %}" class="submit" />&nbsp;
<input type="button" value="{% trans %}Cancel{% endtrans %}" class="submit" onclick="history.back(-1);" />
@@ -38,6 +45,9 @@
{% block endjs %}
{% include "blocks/editor_data.html" %}
<script type='text/javascript' src='{{"/js/editor.js"|media }}'></script>
+ {% if mandatory_tags %}
+ {% include "blocks/mandatory_tags_js.html" %}
+ {% endif %}
<script type='text/javascript' src='{{"/js/jquery.validate.min.js"|media}}'></script>
<script type='text/javascript' src='{{"/js/post.js"|media}}'></script>
<script type='text/javascript' src='{{"/js/wmd/showdown.js"|media}}'></script>
diff --git a/askbot/tests/form_tests.py b/askbot/tests/form_tests.py
index 68fb7010..519d81c0 100644
--- a/askbot/tests/form_tests.py
+++ b/askbot/tests/form_tests.py
@@ -1,3 +1,4 @@
+from django import forms as django_forms
from askbot.tests.utils import AskbotTestCase
from askbot.conf import settings as askbot_settings
from askbot import forms
@@ -88,6 +89,9 @@ class TagNamesFieldTests(AskbotTestCase):
self.field = forms.TagNamesField()
self.user = self.create_user('user1')
+ def tearDown(self):
+ askbot_settings.update('MANDATORY_TAGS', '')
+
def clean(self, value):
return self.field.clean(value).strip().split(' ')
@@ -110,6 +114,26 @@ class TagNamesFieldTests(AskbotTestCase):
cleaned_tags = self.clean('tag1 taG2 TAG1 tag3 tag3')
self.assert_tags_equal(cleaned_tags, ['TAG1', 'Tag2', 'tag3'])
+ def test_catch_missing_mandatory_tag(self):
+ askbot_settings.update('MANDATORY_TAGS', 'one two')
+ self.assertRaises(
+ django_forms.ValidationError,
+ self.clean,
+ ('three',)
+ )
+
+ def test_pass_with_entered_mandatory_tag(self):
+ askbot_settings.update('MANDATORY_TAGS', 'one two')
+ cleaned_tags = self.clean('one')
+ self.assert_tags_equal(cleaned_tags, ['one',])
+
+ def test_pass_with_entered_wk_mandatory_tag(self):
+ askbot_settings.update('MANDATORY_TAGS', 'one* two')
+ askbot_settings.update('USE_WILDCARD_TAGS', True)
+ cleaned_tags = self.clean('oneness')
+ self.assert_tags_equal(cleaned_tags, ['oneness',])
+
+
class EditQuestionAnonymouslyFormTests(AskbotTestCase):
"""setup the following truth table
on reveal_identity field:
diff --git a/askbot/views/writers.py b/askbot/views/writers.py
index c5a69c1d..0cfae751 100644
--- a/askbot/views/writers.py
+++ b/askbot/views/writers.py
@@ -258,6 +258,7 @@ def ask(request):#view used to ask a new question
'active_tab': 'ask',
'page_class': 'ask-page',
'form' : form,
+ 'mandatory_tags': models.tag.get_mandatory_tags(),
'email_validation_faq_url':reverse('faq') + '#validate',
}
return render_into_skin('ask.html', data, request)
@@ -394,6 +395,7 @@ def edit_question(request, id):
'active_tab': 'questions',
'question': question,
'revision_form': revision_form,
+ 'mandatory_tags': models.tag.get_mandatory_tags(),
'form' : form,
}
return render_into_skin('question_edit.html', data, request)