summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-10-30 02:42:45 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-10-30 02:42:45 -0400
commita8dd0f645539759187ba3e271078da4b6dcd3ba3 (patch)
tree35ba55f4674ae4e4c515c17d426fc72e57e630bd
parent75fade07a95c1e4cd34cf9675a326721d042e6db (diff)
downloadaskbot-a8dd0f645539759187ba3e271078da4b6dcd3ba3.tar.gz
askbot-a8dd0f645539759187ba3e271078da4b6dcd3ba3.tar.bz2
askbot-a8dd0f645539759187ba3e271078da4b6dcd3ba3.zip
improved retagging
-rw-r--r--askbot/skins/default/media/js/com.cnprog.post.js238
-rwxr-xr-xaskbot/skins/default/media/style/style.css27
-rw-r--r--askbot/skins/default/templates/question.html52
-rw-r--r--askbot/views/readers.py2
-rw-r--r--askbot/views/writers.py27
5 files changed, 318 insertions, 28 deletions
diff --git a/askbot/skins/default/media/js/com.cnprog.post.js b/askbot/skins/default/media/js/com.cnprog.post.js
index 477d4357..8273d6da 100644
--- a/askbot/skins/default/media/js/com.cnprog.post.js
+++ b/askbot/skins/default/media/js/com.cnprog.post.js
@@ -491,6 +491,240 @@ var Vote = function(){
};
} ();
+var questionRetagger = function(){
+
+ var oldTagsHTML = '';
+ var tagInput = null;
+ var tagsDiv = null;
+ var retagLink = null;
+
+ var restoreEventHandlers = function(){
+ $(document).unbind('click');
+ };
+
+ var cancelRetag = function(){
+ tagInput.unautocomplete();//removes dropdown if open
+ tagsDiv.html(oldTagsHTML);
+ tagsDiv.removeClass('post-retag');
+ tagsDiv.addClass('post-tags');
+ restoreEventHandlers();
+ initRetagger();
+ };
+
+ var render_tag = function(tag_name){
+ //copy-paste from live search!!!
+ var url = scriptUrl +
+ $.i18n._('questions/') +
+ '?tags=' + encodeURI(tag_name);
+ var tag_title = $.i18n._(
+ "see questions tagged '{tag}'"
+ ).replace(
+ '{tag}',
+ tag_name
+ );
+ return '<a ' +
+ 'href="' + url + '" ' +
+ 'title="' + tag_title + '" rel="tag"' +
+ '>' + tag_name + '</a>';
+ };
+
+ var drawNewTags = function(new_tags){
+ new_tags = new_tags.split(/\s+/);
+ var tags_html = ''
+ $.each(new_tags, function(index, name){
+ if (index === 0){
+ tags_html = render_tag(name);
+ }
+ else {
+ tags_html += ' ' + render_tag(name);
+ }
+ });
+ tagsDiv.html(tags_html);
+ };
+
+ var doRetag = function(){
+ $.ajax({
+ type: "POST",
+ url: retagUrl,
+ dataType: "json",
+ data: { tags: getUniqueTags().join(' ') },
+ success: function(json) {
+ if (json['success'] === true){
+ new_tags = getUniqueTags();
+ oldTagsHtml = '';
+ cancelRetag();
+ drawNewTags(new_tags.join(' '));
+ }
+ else {
+ cancelRetag();
+ showMessage(tagsDiv, json['message']);
+ }
+ },
+ error: function(xhr, textStatus, errorThrown) {
+ showMessage(tagsDiv, 'sorry, somethin is not right here');
+ cancelRetag();
+ }
+ });
+ return false;
+ }
+
+ var setupInputEventHandlers = function(input){
+ input.keydown(function(e){
+ if ((e.which && e.which == 27) || (e.keyCode && e.keyCode == 27)){
+ cancelRetag();
+ }
+ });
+ $(document).unbind('click').click(cancelRetag, false);
+ input.click(function(){return false});
+ };
+
+ var setupButtonEventHandlers = function(button){
+ button.keydown(function(e){
+ if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)){
+ doRetag();
+ return false;
+ }
+ });
+ button.click(doRetag);
+ };
+
+ var getUniqueTags = function(){
+ return $.unique($.trim(tagInput.val()).split(/\s+/));
+ };
+
+ //todo pull from django
+ var validateTagLength = function(){
+ var tags = getUniqueTags();
+ var are_tags_ok = true;
+ $.each(tags, function(index, value){
+ if (value.length > askbot['settings']['maxTagLength']){
+ are_tags_ok = false;
+ }
+ });
+ return are_tags_ok;
+ };
+
+ var validateTagCount = function(){
+ var tags = getUniqueTags();
+ return (tags.length <= askbot['settings']['maxTagsPerPost']);
+ };
+
+ var createRetagForm = function(old_tags_string){
+ var div = $('<form method="post"></form>');
+ tagInput = $('<input id="retag_tags" type="text" autocomplete="off" name="tags" size="30"/>');
+ //var tagLabel = $('<label for="retag_tags" class="error"></label>');
+ tagInput.val(old_tags_string);
+ //populate input
+ //todo: make autocomplete work
+ tagInput.autocomplete(tags_autocomplete, {
+ minChars: 1,
+ matchContains: true,
+ selectFirst: false,
+ max: 20,
+ multiple: true,
+ multipleSeparator: " ",
+ formatItem: function(row, i, max) {
+ return row.n + " ("+ row.c +")";
+ },
+ formatResult: function(row, i, max){
+ return row.n;
+ }
+ });
+
+ div.append(tagInput);
+ //div.append(tagLabel);
+ setupInputEventHandlers(tagInput);
+
+ //button = $('<input type="submit" />');
+ //button.val($.i18n._('save tags'));
+ //div.append(button);
+ //setupButtonEventHandlers(button);
+
+ $.validator.addMethod('limit_tag_count', validateTagCount);
+ $.validator.addMethod('limit_tag_length', validateTagLength);
+ div.validate({//copy-paste from utils.js
+ rules: {
+ tags: {
+ required: true,
+ maxlength: askbot['settings']['maxTagsPerPost'] * askbot['settings']['maxTagLength'],
+ limit_tag_count: true,
+ limit_tag_length: true,
+ }
+ },
+ messages: {
+ tags: {
+ required: $.i18n._('tags cannot be empty'),
+ maxlength: askbot['messages']['tagLimits'],
+ limit_tag_count: askbot['messages']['maxTagsPerPost'],
+ limit_tag_length: askbot['messages']['maxTagLength']
+ }
+ },
+ submitHandler: doRetag,
+ errorClass: "retag-error",
+ });
+
+ return div;
+ };
+
+ var getTagsAsString = function(tags_div){
+ var links = tags_div.find('a');
+ var tags_str = '';
+ links.each(function(index, element){
+ if (index === 0){
+ tags_str = $(element).html();
+ }
+ else {
+ tags_str += ' ' + $(element).html();
+ }
+ });
+ return tags_str;
+ };
+
+ var noopHandler = function(){
+ tagInput.focus();
+ return false;
+ };
+
+ var deactivateRetagLink = function(){
+ retagLink.unbind('click').click(noopHandler);
+ retagLink.unbind('keypress').keypress(noopHandler);
+ };
+
+ var startRetag = function(){
+ tagsDiv = $('#question-tags');
+ oldTagsHTML = tagsDiv.html();//save to restore on cancel
+ var old_tags_string = getTagsAsString(tagsDiv);
+ var retag_form = createRetagForm(old_tags_string);
+ tagsDiv.html('');
+ tagsDiv.append(retag_form);
+ tagsDiv.removeClass('post-tags');
+ tagsDiv.addClass('post-retag');
+ tagInput.focus();
+ deactivateRetagLink();
+ return false;
+ };
+
+ var setupClickAndEnterHandler = function(element, callback){
+ element.unbind('click').click(callback);
+ element.unbind('keypress').keypress(function(e){
+ if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)){
+ callback();
+ }
+ });
+ }
+
+ var initRetagger = function(){
+ setupClickAndEnterHandler(retagLink, startRetag);
+ };
+
+ return {
+ init: function(){
+ retagLink = $('#retag');
+ initRetagger();
+ }
+ };
+}();
+
var questionComments = createComments('question');
var answerComments = createComments('answer');
var commentsFactory = {'question' : questionComments, 'answer' : answerComments};
@@ -533,7 +767,8 @@ function createComments(type) {
setupFormValidation("#" + formId,
{ comment: { required: true, minlength: 10} }, '',
- function() { postComment(id, formId); });
+ function() { postComment(id, formId); })
+ ;
}
}
else {
@@ -741,6 +976,7 @@ function createComments(type) {
$(document).ready(function() {
questionComments.init();
+ questionRetagger.init();
answerComments.init();
});
diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css
index 19ca902a..7fc52e37 100755
--- a/askbot/skins/default/media/style/style.css
+++ b/askbot/skins/default/media/style/style.css
@@ -1281,19 +1281,21 @@ a:hover.medal {
color: #fff;
}
-.action-link a {
+.post-controls a {
color: #777;
padding: 3px;
cursor: pointer;
+ border: none;
+ background: none;
+ text-decoration: none;
}
-.action-link: a hover {
+.post-controls a:hover {
background-color: #777;
- text-decoration: none;
color: #fff;
}
-.action-link-separator {
+.post-controls .sep {
color: #ccc;
}
@@ -1544,6 +1546,12 @@ table.ab-tag-filter-form {
font-size: 10px;
}
+label.retag-error {
+ color: darkred;
+ padding-left: 5px;
+ font-size: 10px;
+}
+
.error-list li {
padding: 5px;
}
@@ -2516,7 +2524,8 @@ ul.form-horizontal-rows li input {
font-weight: bold;
}
-.post-controls {
+.post-controls, .post-tags {
+ clear: left;
float: left;
font-size: 11px;
line-height: 12px;
@@ -2524,6 +2533,14 @@ ul.form-horizontal-rows li input {
margin-bottom: 5px;
}
+.post-tags {
+ margin-bottom:8px;
+}
+
+.post-retag {
+ margin-bottom:0px;
+}
+
#question-controls .tags {
margin: 0 0 3px 0;
}
diff --git a/askbot/skins/default/templates/question.html b/askbot/skins/default/templates/question.html
index f1b80812..fdb573b6 100644
--- a/askbot/skins/default/templates/question.html
+++ b/askbot/skins/default/templates/question.html
@@ -129,32 +129,31 @@
<div class="question-body">
{{question.html}}
</div>
+ <div id="question-tags" class="post-tags tags">
+ {% for tag in question.get_tag_names() %}
+ <a href="{% url questions %}?tags={{tag|urlencode}}&amp;start_over=true"
+ class="post-tag"
+ title="{% trans %}see questions tagged '{{tag}}'{% endtrans %}" rel="tag">{{ tag }}</a>
+ {% endfor %}
+ </div>
<div id="question-controls" class="post-controls">
- {#todo: here we have important case to reset search state #}
- <div id="question-tags" class="tags">
- {% for tag in question.get_tag_names() %}
- <a href="{% url questions %}?tags={{tag|urlencode}}&amp;start_over=true"
- class="post-tag"
- title="{% trans %}see questions tagged '{{tag}}'{% endtrans %}" rel="tag">{{ tag }}</a>
- {% endfor %}
- </div>
- {% set pipe=joiner('<span class="action-link-separator">|</span>') %}
+ {% set pipe=joiner('<span class="sep">|</span>') %}
{% if request.user|can_edit_post(question) %}{{ pipe() }}
- <span class="action-link">
- <a href="{% url edit_question question.id %}">{% trans %}edit{% endtrans %}</a>
- </span>
- {% elif request.user|can_retag_question(question) %}
- <span class="action-link">
- <a href="{% url retag_question question.id %}">{% trans %}retag{% endtrans %}</a>
- </span>
+ <a href="{% url edit_question question.id %}">{% trans %}edit{% endtrans %}</a>
+ {% endif %}
+ {% if request.user|can_retag_question(question) %}{{ pipe() }}
+ <a id="retag" href="{% url retag_question question.id %}">{% trans %}retag{% endtrans %}</a>
+ <script type="text/javascript">
+ var retagUrl = "{% url retag_question question.id %}";
+ </script>
{% endif %}
{% if question.closed %}
{% if request.user|can_reopen_question(question) %}{{ pipe() }}
- <span class="action-link"><a href="{% url reopen question.id %}">{% trans %}reopen{% endtrans %}</a></span>
+ <a href="{% url reopen question.id %}">{% trans %}reopen{% endtrans %}</a>
{% endif %}
{% else %}
{% if request.user|can_close_question(question) %}{{ pipe() }}
- <span class="action-link"><a href="{% url close question.id %}">{% trans %}close{% endtrans %}</a></span>
+ <a href="{% url close question.id %}">{% trans %}close{% endtrans %}</a>
{% endif %}
{% endif %}
{% if request.user|can_flag_offensive(question) %}{{ pipe() }}
@@ -167,8 +166,9 @@
</span>
{% endif %}
{% if request.user|can_delete_post(question) %}{{ pipe() }}
- <span class="action-link"><a id="question-delete-link-{{question.id}}">{% trans %}delete{% endtrans %}</a></span>
+ <a id="question-delete-link-{{question.id}}">{% trans %}delete{% endtrans %}</a>
{% endif %}
+ </div>
</div>
<div class="post-update-info-container">
{{
@@ -455,5 +455,19 @@
{% endif %}
{% endblock %}
{% block endjs %}
+ {% if request.user|can_retag_question(question) %}
+ <script type="text/javascript">
+ var tags_autocomplete = {{tags_autocomplete|safe}};
+ var askbot = askbot || {};
+ askbot['settings'] = askbot['settings'] || {};
+ askbot['messages'] = askbot['messages'] || {};
+ askbot['settings']['maxTagLength'] = {{settings.MAX_TAG_LENGTH}};
+ 'each tag must be shorter than %(max_chars)d characters',
+ askbot['messages']['maxTagLength'] = '{% trans max_chars = settings.MAX_TAG_LENGTH %}each tag must be shorter that {{max_chars}} character{% pluralize %}each tag must be shorter than {{max_chars}} characters{% endtrans %}';
+ askbot['settings']['maxTagsPerPost'] = {{settings.MAX_TAGS_PER_POST}};
+ 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 %}';
+ </script>
+ {% endif %}
{% endblock %}
<!-- end question.html -->
diff --git a/askbot/views/readers.py b/askbot/views/readers.py
index fb22240c..25425659 100644
--- a/askbot/views/readers.py
+++ b/askbot/views/readers.py
@@ -524,6 +524,8 @@ def question(request, id):#refactor - long subroutine. display question body, an
'language_code': translation.get_language(),
'paginator_context' : paginator_context
}
+ if request.user.is_authenticated():
+ data['tags_autocomplete'] = _get_tags_cache_json()
context = RequestContext(request, data)
template = ENV.get_template('question.html')
return HttpResponse(template.render(context))
diff --git a/askbot/views/writers.py b/askbot/views/writers.py
index 22407b67..aca11b63 100644
--- a/askbot/views/writers.py
+++ b/askbot/views/writers.py
@@ -198,9 +198,22 @@ def retag_question(request, id):
question = question,
tags = form.cleaned_data['tags']
)
- return HttpResponseRedirect(question.get_absolute_url())
+ if request.is_ajax():
+ response_data = {'success': True}
+ data = simplejson.dumps(response_data)
+ return HttpResponse(data, mimetype="application/json")
+ else:
+ return HttpResponseRedirect(question.get_absolute_url())
+ elif request.is_ajax():
+ response_data = {
+ 'message': unicode(form.errors['tags']),
+ 'success': False
+ }
+ data = simplejson.dumps(response_data)
+ return HttpResponse(data, mimetype="application/json")
else:
form = forms.RetagQuestionForm(question)
+
data = {
'active_tab': 'questions',
'question': question,
@@ -211,8 +224,16 @@ def retag_question(request, id):
template = ENV.get_template('question_retag.html')
return HttpResponse(template.render(context))
except exceptions.PermissionDenied, e:
- request.user.message_set.create(message = unicode(e))
- return HttpResponseRedirect(question.get_absolute_url())
+ if request.is_ajax():
+ response_data = {
+ 'message': unicode(e),
+ 'success': False
+ }
+ data = simplejson.dumps(response_data)
+ return HttpResponse(data, mimetype="application/json")
+ else:
+ request.user.message_set.create(message = unicode(e))
+ return HttpResponseRedirect(question.get_absolute_url())
@login_required
def edit_question(request, id):