summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-05-14 12:37:15 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-05-14 12:37:15 -0400
commit2225b8828f8b2b3bab55b6ff235a768c2a6a0f8c (patch)
treea5fa66d61d3ed3bc3347151e79c0a1207131db7a
parent521a574aa6c029649e18f4592f04990ab69c896c (diff)
parent81d64e494eeaf184d3dc7e6105b2815486e93053 (diff)
downloadaskbot-2225b8828f8b2b3bab55b6ff235a768c2a6a0f8c.tar.gz
askbot-2225b8828f8b2b3bab55b6ff235a768c2a6a0f8c.tar.bz2
askbot-2225b8828f8b2b3bab55b6ff235a768c2a6a0f8c.zip
Merge branch 'tmp' into user-groups
-rw-r--r--askbot/conf/forum_data_rules.py9
-rw-r--r--askbot/lamson_handlers.py2
-rw-r--r--askbot/models/__init__.py1
-rw-r--r--askbot/models/post.py12
-rw-r--r--askbot/models/question.py27
-rw-r--r--askbot/search/state_manager.py9
-rw-r--r--askbot/skins/common/media/jquery-openid/openid.css3
-rw-r--r--askbot/skins/common/media/js/live_search.js125
-rw-r--r--askbot/skins/common/media/js/post.js11
-rw-r--r--askbot/skins/common/media/js/utils.js36
-rw-r--r--askbot/skins/common/media/js/wmd/wmd.js2
-rw-r--r--askbot/skins/common/templates/authopenid/signup_with_password.html6
-rw-r--r--askbot/skins/default/media/style/style.less36
-rw-r--r--askbot/skins/default/templates/main_page/sidebar.html4
-rw-r--r--askbot/skins/default/templates/main_page/tag_search.html7
-rw-r--r--askbot/skins/default/templates/users.html4
-rw-r--r--askbot/tests/page_load_tests.py31
-rw-r--r--askbot/tests/post_model_tests.py10
-rw-r--r--askbot/urls.py2
-rw-r--r--askbot/utils/decorators.py2
-rw-r--r--askbot/utils/mail.py2
-rw-r--r--askbot/views/commands.py4
-rw-r--r--askbot/views/readers.py13
-rw-r--r--askbot/views/writers.py6
24 files changed, 279 insertions, 85 deletions
diff --git a/askbot/conf/forum_data_rules.py b/askbot/conf/forum_data_rules.py
index 491ebfa8..7d98c9e8 100644
--- a/askbot/conf/forum_data_rules.py
+++ b/askbot/conf/forum_data_rules.py
@@ -201,6 +201,15 @@ settings.register(
)
settings.register(
+ livesettings.BooleanValue(
+ FORUM_DATA_RULES,
+ 'TAG_SEARCH_INPUT_ENABLED',
+ default = False,
+ description = _('Enable separate tag search box on main page')
+ )
+)
+
+settings.register(
livesettings.IntegerValue(
FORUM_DATA_RULES,
'MAX_COMMENTS_TO_SHOW',
diff --git a/askbot/lamson_handlers.py b/askbot/lamson_handlers.py
index 34f93a7b..9062594c 100644
--- a/askbot/lamson_handlers.py
+++ b/askbot/lamson_handlers.py
@@ -130,7 +130,7 @@ def ASK(message, host = None, addr = None):
try:
group_tag = Tag.group_tags.get(
deleted = False,
- name_iexact = addr
+ name__iexact = addr
)
mail.process_emailed_question(
from_address, subject, parts, tags = [group_tag.name, ]
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index bfab105a..88c8f7b1 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -1440,6 +1440,7 @@ def user_edit_comment(
edited_by = self,
by_email = by_email
)
+ comment_post.thread.invalidate_cached_data()
def user_edit_post(self,
post = None,
diff --git a/askbot/models/post.py b/askbot/models/post.py
index c1fc33d6..a0fdc4ff 100644
--- a/askbot/models/post.py
+++ b/askbot/models/post.py
@@ -518,12 +518,14 @@ class Post(models.Model):
def get_absolute_url(self, no_slug = False, question_post=None, thread=None):
from askbot.utils.slug import slugify
+ #todo: the url generation function is pretty bad -
+ #the trailing slash is entered in three places here + in urls.py
if not hasattr(self, '_thread_cache') and thread:
self._thread_cache = thread
if self.is_answer():
if not question_post:
question_post = self.thread._question_post()
- return u'%(base)s%(slug)s?answer=%(id)d#post-id-%(id)d' % {
+ return u'%(base)s%(slug)s/?answer=%(id)d#post-id-%(id)d' % {
'base': urlresolvers.reverse('question', args=[question_post.id]),
'slug': django_urlquote(slugify(self.thread.title)),
'id': self.id
@@ -531,9 +533,9 @@ class Post(models.Model):
elif self.is_question():
url = urlresolvers.reverse('question', args=[self.id])
if thread:
- url += django_urlquote(slugify(thread.title))
+ url += django_urlquote(slugify(thread.title)) + '/'
elif no_slug is False:
- url += django_urlquote(self.slug)
+ url += django_urlquote(self.slug) + '/'
return url
elif self.is_comment():
origin_post = self.get_origin_post()
@@ -1741,8 +1743,8 @@ class PostRevision(models.Model):
return False
#if sent by email to group and group does not want moderation
- if self.by_email and self.email:
- group_name = self.email.split('@')[0]
+ if self.by_email and self.email_address:
+ group_name = self.email_address.split('@')[0]
try:
group = Tag.objects.get(name = group_name, deleted = False)
return group.group.profile.moderate_email
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 58325ccb..a18e719b 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -182,8 +182,31 @@ class ThreadManager(models.Manager):
qs = qs.filter(posts__post_type='question', posts__author__in=query_users) # TODO: unify with search_state.author ?
tags = search_state.unified_tags()
- for tag in tags:
- qs = qs.filter(tags__name=tag) # Tags or AND-ed here, not OR-ed (i.e. we fetch only threads with all tags)
+ if len(tags) > 0:
+
+ if askbot_settings.TAG_SEARCH_INPUT_ENABLED:
+ #todo: this may be gone or disabled per option
+ #"tag_search_box_enabled"
+ existing_tags = set(
+ Tag.objects.filter(
+ name__in = tags
+ ).values_list(
+ 'name',
+ flat = True
+ )
+ )
+
+ non_existing_tags = set(tags) - existing_tags
+ meta_data['non_existing_tags'] = list(non_existing_tags)
+ tags = existing_tags
+ else:
+ meta_data['non_existing_tags'] = list()
+
+ #construct filter for the tag search
+ for tag in tags:
+ qs = qs.filter(tags__name=tag) # Tags or AND-ed here, not OR-ed (i.e. we fetch only threads with all tags)
+ else:
+ meta_data['non_existing_tags'] = list()
if search_state.scope == 'unanswered':
qs = qs.filter(closed = False) # Do not show closed questions in unanswered section
diff --git a/askbot/search/state_manager.py b/askbot/search/state_manager.py
index 8096cbdd..f8154865 100644
--- a/askbot/search/state_manager.py
+++ b/askbot/search/state_manager.py
@@ -216,9 +216,14 @@ class SearchState(object):
ss.page = 1
return ss
- def remove_tags(self):
+ def remove_tags(self, tags = None):
ss = self.deepcopy()
- ss.tags = []
+ if tags:
+ ss.tags = list(
+ set(ss.tags) - set(tags)
+ )
+ else:
+ ss.tags = []
ss.page = 1
return ss
diff --git a/askbot/skins/common/media/jquery-openid/openid.css b/askbot/skins/common/media/jquery-openid/openid.css
index ec93881d..9a1db85f 100644
--- a/askbot/skins/common/media/jquery-openid/openid.css
+++ b/askbot/skins/common/media/jquery-openid/openid.css
@@ -1,9 +1,8 @@
-div#login-icons {padding:20px 0 0 0;}
+div#login-icons {padding: 0;}
ul.login-icons {width: 450px; margin:0;padding:0;text-align:left; list-style-type:none; display:block;}
ul.login-icons li {display:inline;}
ul.large input {height: 40px; width: 90px;border:1px solid #ccc;margin:0 5px 5px 0;}
.openid-signin h1 {padding-bottom: 10px;}
-.openid-signin h2 {margin-top:15px;}
.openid-signin h2#account-recovery-heading {margin-bottom:2px;}
#account-recovery-form p.hint a {color:#1b79bd; text-decoration: none;}
#account-recovery-form p.hint a:hover {text-decoration: underline;}
diff --git a/askbot/skins/common/media/js/live_search.js b/askbot/skins/common/media/js/live_search.js
index 100c3f67..f33862a1 100644
--- a/askbot/skins/common/media/js/live_search.js
+++ b/askbot/skins/common/media/js/live_search.js
@@ -1,3 +1,55 @@
+var TagWarningBox = function(){
+ WrappedElement.call(this);
+ this._tags = [];
+};
+inherits(TagWarningBox, WrappedElement);
+
+TagWarningBox.prototype.createDom = function(){
+ this._element = this.makeElement('div');
+ this._element
+ .css('display', 'block')
+ .css('margin', '0 0 13px 2px');
+ this._element.addClass('non-existing-tags');
+ this._warning = this.makeElement('p');
+ this._element.append(this._warning);
+ this._tag_container = this.makeElement('ul');
+ this._tag_container.addClass('tags');
+ this._element.append(this._tag_container);
+ this._element.append($('<div class="clearfix"></div>'));
+ this._element.hide();
+};
+
+TagWarningBox.prototype.clear = function(){
+ this._tags = [];
+ if (this._tag_container){
+ this._tag_container.empty();
+ }
+ this._warning.hide();
+ this._element.hide();
+};
+
+TagWarningBox.prototype.addTag = function(tag_name){
+ var tag = new Tag();
+ tag.setName(tag_name);
+ tag.setLinkable(false);
+ tag.setDeletable(false);
+ var elem = this.getElement();
+ this._tag_container.append(tag.getElement());
+ this._tag_container.css('display', 'block');
+ this._tags.push(tag);
+ elem.show();
+};
+
+TagWarningBox.prototype.showWarning = function(){
+ this._warning.html(
+ ngettext(
+ 'Sorry, this tag does not exist',
+ 'Sorry, these tags do not exist',
+ this._tags.length
+ )
+ );
+ this._warning.show();
+};
var liveSearch = function(query_string) {
var query = $('input#keywords');
@@ -7,6 +59,70 @@ var liveSearch = function(query_string) {
var q_list_sel = 'question-list';//id of question listing div
var search_url = askbot['urls']['questions'];
var x_button = $('input[name=reset_query]');
+ var tag_warning_box = new TagWarningBox();
+
+ //the tag search input is optional in askbot
+ $('#ab-tag-search').parent().before(
+ tag_warning_box.getElement()
+ );
+
+ var run_tag_search = function(){
+ var search_tags = $('#ab-tag-search').val().split(/\s+/);
+ if (search_tags.length === 0) {
+ return;
+ }
+ /** @todo: the questions/ might need translation... */
+ query_string = '/questions/scope:all/sort:activity-desc/page:1/'
+ $.each(search_tags, function(idx, tag) {
+ query_string = QSutils.add_search_tag(query_string, search_tags);
+ });
+ var url = search_url + query_string;
+ $.ajax({
+ url: url,
+ dataType: 'json',
+ success: function(data, text_status, xhr){
+ render_result(data, text_status, xhr);
+ $('#ab-tag-search').val('');
+ },
+ });
+ updateHistory(url);
+ };
+
+ var activate_tag_search_input = function(){
+ //the autocomplete is set up in tag_selector.js
+ var button = $('#ab-tag-search-add');
+ if (button.length === 0){//may be absent
+ return;
+ }
+ var ac = new AutoCompleter({
+ url: askbot['urls']['get_tag_list'],
+ preloadData: true,
+ minChars: 1,
+ useCache: true,
+ matchInside: true,
+ maxCacheLength: 100,
+ maxItemsToShow: 20,
+ onItemSelect: run_tag_search,
+ delay: 10
+ });
+ ac.decorate($('#ab-tag-search'));
+ setupButtonEventHandlers(button, run_tag_search);
+ //var tag_search_input = $('#ab-tag-search');
+ //tag_search_input.keydown(
+ // makeKeyHandler(13, run_tag_search)
+ //);
+ };
+
+ var render_tag_warning = function(tag_list){
+ if ( !tag_list ) {
+ return;
+ }
+ tag_warning_box.clear();
+ $.each(tag_list, function(idx, tag_name){
+ tag_warning_box.addTag(tag_name);
+ });
+ tag_warning_box.showWarning();
+ };
var refresh_x_button = function(){
if(query_val().length > 0){
@@ -69,6 +185,10 @@ var liveSearch = function(query_string) {
},
cache: false
});
+ updateHistory(url);
+ };
+
+ var updateHistory = function(url) {
var context = { state:1, rand:Math.random() };
History.pushState( context, "Questions", url );
setTimeout(function (){
@@ -195,6 +315,7 @@ var liveSearch = function(query_string) {
}
render_related_tags(data['related_tags'], data['query_string']);
render_relevance_sort_tab(data['query_string']);
+ render_tag_warning(data['non_existing_tags']);
set_active_sort_tab(data['query_data']['sort_order'], data['query_string']);
if(data['feed_url']){
// Change RSS URL
@@ -214,11 +335,11 @@ var liveSearch = function(query_string) {
var askHrefBase = askButton.attr('href').split('?')[0];
askButton.attr('href', askHrefBase + data['query_data']['ask_query_string']); /* INFO: ask_query_string should already be URL-encoded! */
-
query.focus();
var old_list = $('#' + q_list_sel);
var new_list = $('<div></div>').hide().html(data['questions']);
+ new_list.find('.timeago').timeago();
old_list.stop(true).after(new_list).fadeOut(200, function() {
//show new div with a fadeIn effect
old_list.remove();
@@ -260,6 +381,8 @@ var liveSearch = function(query_string) {
}
});
+ activate_tag_search_input();
+
$("form#searchForm").submit(function(event) {
// if user clicks the button the s(h)e probably wants page reload,
// so provide that experience but first update the query string
diff --git a/askbot/skins/common/media/js/post.js b/askbot/skins/common/media/js/post.js
index f6157848..294c5f41 100644
--- a/askbot/skins/common/media/js/post.js
+++ b/askbot/skins/common/media/js/post.js
@@ -1215,6 +1215,7 @@ EditCommentForm.prototype.attachTo = function(comment, mode){
this._submit_btn.html(gettext('save comment'));
}
this.getElement().show();
+ this.enableButtons();
this.focus();
putCursorAtEnd(this._textarea);
};
@@ -1342,13 +1343,13 @@ EditCommentForm.prototype.createDom = function(){
};
EditCommentForm.prototype.enableButtons = function(){
- this._submit_btn.attr('disabled', '');
- this._cancel_btn.attr('disabled', '');
+ this._submit_btn.attr('disabled', false);
+ this._cancel_btn.attr('disabled', false);
};
EditCommentForm.prototype.disableButtons = function(){
- this._submit_btn.attr('disabled', 'disabled');
- this._cancel_btn.attr('disabled', 'disabled');
+ this._submit_btn.attr('disabled', true);
+ this._cancel_btn.attr('disabled', true);
};
EditCommentForm.prototype.reset = function(){
@@ -1915,7 +1916,7 @@ var WMD = function(){
WrappedElement.call(this);
this._markdown = undefined;
this._enabled_buttons = 'bold italic link blockquote code ' +
- 'image ol ul heading hr';
+ 'image attachment ol ul heading hr';
this._is_previewer_enabled = true;
};
inherits(WMD, WrappedElement);
diff --git a/askbot/skins/common/media/js/utils.js b/askbot/skins/common/media/js/utils.js
index c82913a8..297e3f9a 100644
--- a/askbot/skins/common/media/js/utils.js
+++ b/askbot/skins/common/media/js/utils.js
@@ -216,7 +216,6 @@ QSutils.add_search_tag = function(query_string, tag){
return this.patch_query_string(query_string, 'tags:' + tag_string);
};
-
/* **************************************************** */
/* some google closure-like code for the ui elements */
@@ -716,16 +715,6 @@ TwoStateToggle.prototype.isOn = function(){
return this._element.hasClass('on');
};
-TwoStateToggle.prototype.setOn = function(is_on){
- if (is_on){
- this._element.addClass('on');
- this._element.html(this._state_messages['on-state']);
- } else {
- this._element.removeClass('on');
- this._element.html(this._state_messages['off-state']);
- }
-};
-
TwoStateToggle.prototype.getDefaultHandler = function(){
var me = this;
return function(){
@@ -739,7 +728,11 @@ TwoStateToggle.prototype.getDefaultHandler = function(){
data: data,
success: function(data) {
if (data['success']) {
- me.setOn('is_enabled');
+ if ( data['is_enabled'] ) {
+ me.setState('on-state');
+ } else {
+ me.setState('off-state');
+ }
} else {
showMessage(me.getElement(), data['message']);
}
@@ -748,22 +741,29 @@ TwoStateToggle.prototype.getDefaultHandler = function(){
};
};
+TwoStateToggle.prototype.isCheckBox = function(){
+ var element = this._element;
+ return element.attr('type') === 'checkbox';
+};
+
TwoStateToggle.prototype.setState = function(state){
var element = this._element;
this._state = state;
if (element) {
- if (
- element.attr('nodeName') === 'INPUT' &&
- element.attr('type') === 'checkbox'
- ) {
+ this.resetStyles();
+ element.addClass(state);
+ if (state === 'on-state') {
+ element.addClass('on');
+ } else if (state === 'off-state') {
+ element.removeClass('on');
+ }
+ if ( this.isCheckBox() ) {
if (state === 'on-state') {
element.attr('checked', true);
} else if (state === 'off-state') {
element.attr('checked', false);
}
} else {
- this.resetStyles();
- element.addClass(state);
this._element.html(this._state_messages[state]);
}
}
diff --git a/askbot/skins/common/media/js/wmd/wmd.js b/askbot/skins/common/media/js/wmd/wmd.js
index 19f32c87..98af264f 100644
--- a/askbot/skins/common/media/js/wmd/wmd.js
+++ b/askbot/skins/common/media/js/wmd/wmd.js
@@ -2478,7 +2478,7 @@ if(!Attacklab.wmd)
mergeEnv(top["wmd_options"]);
Attacklab.full = true;
- var defaultButtons = "bold italic link blockquote code image ol ul heading hr";
+ var defaultButtons = "bold italic link blockquote code image attachment ol ul heading hr";
Attacklab.wmd_env.buttons = Attacklab.wmd_env.buttons || defaultButtons;
};
Attacklab.loadEnv();
diff --git a/askbot/skins/common/templates/authopenid/signup_with_password.html b/askbot/skins/common/templates/authopenid/signup_with_password.html
index e79263d2..e65cd518 100644
--- a/askbot/skins/common/templates/authopenid/signup_with_password.html
+++ b/askbot/skins/common/templates/authopenid/signup_with_password.html
@@ -22,12 +22,12 @@
</form>
<h2>{% trans %}or create a new user name and password here{% endtrans %}</h2>
{% else %}
- <h1>{% trans %}Create login name and password{% endtrans %}</h1>
- <p class="message">{% trans %}<span class='strong big'>If you prefer, create your forum login name and
+ <h1 class="section-title">{% trans %}Create login name and password{% endtrans %}</h1>
+ <!--p class="message">{% trans %}<span class='strong big'>If you prefer, create your forum login name and
password here. However</span>, please keep in mind that we also support
<strong>OpenID</strong> login method. With <strong>OpenID</strong> you can
simply reuse your external login (e.g. Gmail or AOL) without ever sharing
-your login details with anyone and having to remember yet another password.{% endtrans %}
+your login details with anyone and having to remember yet another password.{% endtrans %}</p-->
{%endif%}
<form action="{% url user_signup_with_password %}" method="post" accept-charset="utf-8">{% csrf_token %}
{{form.login_provider}}
diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less
index 0c540698..69cdede0 100644
--- a/askbot/skins/default/media/style/style.less
+++ b/askbot/skins/default/media/style/style.less
@@ -516,6 +516,8 @@ body.anon {
p {
margin-bottom: 4px;
+ color: @info-text;
+ font-family:@main-font;
}
p.info-box-follow-up-links {
@@ -588,13 +590,22 @@ body.anon {
}
.inputs{
- #interestingTagInput, #ignoredTagInput, #subscribedTagInput{
+ #interestingTagInput,
+ #ignoredTagInput,
+ #subscribedTagInput,
+ #ab-tag-search {
width:153px;
padding-left:5px;
border:#c9c9b5 1px solid;
height:25px;
}
- #interestingTagAdd, #ignoredTagAdd, #subscribedTagAdd {
+ #ab-tag-search {
+ width: 135px;
+ }
+ #interestingTagAdd,
+ #ignoredTagAdd,
+ #subscribedTagAdd,
+ #ab-tag-search-add {
background:url(../images/small-button-blue.png) repeat-x top;
border:0;
color:@button-label;
@@ -605,10 +616,12 @@ body.anon {
margin-top:-2px;
cursor:pointer;
.rounded-corners(4px);
- .text-shadow(0px,1px,0px,#E6F6FA);
- .box-shadow(1px, 1px, 2px, #808080);
-
-
+ .text-shadow(0px,1px,0px,#E6F6FA);
+ .box-shadow(1px, 1px, 2px, #808080);
+ }
+ #ab-tag-search-add {
+ width: 47px;
+ margin-left: 3px;
}
#interestingTagAdd:hover, #ignoredTagAdd:hover, #subscribedTag:hover {
background:url(../images/small-button-blue.png) repeat-x bottom;
@@ -1286,7 +1299,7 @@ ul#related-tags li {
#askFormBar {
display:inline-block;
- padding: 4px 7px 5px 0px;
+ padding: 4px 7px 0px 0px;
margin-top:0px;
p{
@@ -1309,10 +1322,8 @@ ul#related-tags li {
.ask-page, .edit-question-page {
div#question-list {
- float: none;
border-bottom:#f0f0ec 1px solid;
- float:left;
- margin-bottom:10px;
+ float: none;
a{
line-height:30px;
}
@@ -1359,6 +1370,7 @@ ul#related-tags li {
.title-desc {
color: @info-text;
font-size: 13px;
+ margin-bottom: 5px;
}
#fmanswer input.submit,
@@ -2214,6 +2226,10 @@ ul#related-tags li {
}
}
+.openid-signin form {
+ margin-bottom: 5px;
+}
+
#email-input-fs,#local_login_buttons,#password-fs,#openid-fs{
margin-top:10px;
#id_email,#id_username,#id_password{
diff --git a/askbot/skins/default/templates/main_page/sidebar.html b/askbot/skins/default/templates/main_page/sidebar.html
index fbfbdeb8..7acbe091 100644
--- a/askbot/skins/default/templates/main_page/sidebar.html
+++ b/askbot/skins/default/templates/main_page/sidebar.html
@@ -10,6 +10,10 @@
{% include "widgets/contributors.html" %}
{% endif %}
+{% if settings.TAG_SEARCH_INPUT_ENABLED %}
+ {% include "main_page/tag_search.html" %}
+{% endif %}
+
{% if request.user.is_authenticated() and settings.SIDEBAR_MAIN_SHOW_TAG_SELECTOR %}
{% include "widgets/tag_selector.html" %}
{% endif %}
diff --git a/askbot/skins/default/templates/main_page/tag_search.html b/askbot/skins/default/templates/main_page/tag_search.html
new file mode 100644
index 00000000..45f12b2f
--- /dev/null
+++ b/askbot/skins/default/templates/main_page/tag_search.html
@@ -0,0 +1,7 @@
+<div id="tagSearch" class="box">
+ <h2>{% trans %}Tag search{% endtrans %}</h2>
+ <div class="inputs">
+ <input id="ab-tag-search" autocomplete="off" type="text"/>
+ <input id="ab-tag-search-add" type="submit" value="{% trans %}search{% endtrans %}"/>
+ </div>
+</div>
diff --git a/askbot/skins/default/templates/users.html b/askbot/skins/default/templates/users.html
index bce3f930..b7a16be1 100644
--- a/askbot/skins/default/templates/users.html
+++ b/askbot/skins/default/templates/users.html
@@ -3,7 +3,7 @@
<!-- users.html -->
{% block title %}{% spaceless %}{% trans %}Users{% endtrans %}{% endspaceless %}{% endblock %}
{% block before_css %}
- {% if group and request.user.is_administrator() %}
+ {% if group and request.user.is_authenticated() and request.user.is_administrator() %}
<link href="{{'/bootstrap/css/bootstrap.css'|media}}" rel="stylesheet" type="text/css" />
{% endif %}
{% endblock %}
@@ -82,7 +82,7 @@
askbot['urls']['delete_group_logo_url'] = '{% url delete_group_logo %}';
askbot['urls']['join_or_leave_group'] = '{% url join_or_leave_group %}';
</script>
- <script type="text/javascript" src='{{"/bootstrap/js/bootstrap.js"|media}}' />
+ <script type="text/javascript" src='{{"/bootstrap/js/bootstrap.js"|media}}'></script>
<script type='text/javascript' src='{{"/js/editor.js"|media}}'></script>
<script type='text/javascript' src='{{"/js/wmd/showdown.js"|media}}'></script>
<script type='text/javascript' src='{{"/js/wmd/wmd.js"|media}}'></script>
diff --git a/askbot/tests/page_load_tests.py b/askbot/tests/page_load_tests.py
index 558ee617..ebfba0c3 100644
--- a/askbot/tests/page_load_tests.py
+++ b/askbot/tests/page_load_tests.py
@@ -553,19 +553,17 @@ class QuestionPageRedirectTests(AskbotTestCase):
url = reverse('question', kwargs={'id': self.q.id})
resp = self.client.get(url)
- url = url + self.q.slug
- self.assertRedirects(resp, expected_url=url)
-
- resp = self.client.get(url)
- self.assertEqual(200, resp.status_code)
- self.assertEqual(self.q, resp.context['question'])
+ self.assertRedirects(
+ resp,
+ expected_url=self.q.get_absolute_url()
+ )
url = reverse('question', kwargs={'id': 101})
resp = self.client.get(url)
- url = reverse('question', kwargs={'id': self.q.id}) + self.q.slug # redirect uses the new question.id !
+ url = reverse('question', kwargs={'id': self.q.id}) + self.q.slug + '/'# redirect uses the new question.id !
self.assertRedirects(resp, expected_url=url)
- url = reverse('question', kwargs={'id': 101}) + self.q.slug
+ url = reverse('question', kwargs={'id': 101}) + self.q.slug + '/'
resp = self.client.get(url)
self.assertEqual(200, resp.status_code)
self.assertEqual(self.q, resp.context['question'])
@@ -578,7 +576,7 @@ class QuestionPageRedirectTests(AskbotTestCase):
url = reverse('question', kwargs={'id': self.q.id})
resp = self.client.get(url, data={'answer': self.a.id})
- url = url + self.q.slug
+ url = self.q.get_absolute_url()
self.assertRedirects(resp, expected_url=url + '?answer=%d' % self.a.id)
resp = self.client.get(url, data={'answer': self.a.id})
@@ -586,7 +584,8 @@ class QuestionPageRedirectTests(AskbotTestCase):
self.assertEqual(self.q, resp.context['question'])
self.assertEqual(self.a, resp.context['show_post'])
- url = reverse('question', kwargs={'id': 101}) + self.q.slug
+ #test redirect from old question
+ url = reverse('question', kwargs={'id': 101}) + self.q.slug + '/'
resp = self.client.get(url, data={'answer': 201})
self.assertRedirects(resp, expected_url=self.a.get_absolute_url())
@@ -597,10 +596,9 @@ class QuestionPageRedirectTests(AskbotTestCase):
self.assertEqual(self.a, resp.context['show_post'])
self.assertEqual(self.c, resp.context['show_comment'])
- url = reverse('question', kwargs={'id': self.q.id})
+ url = self.q.get_absolute_url()
resp = self.client.get(url, data={'comment': self.c.id})
- url = url + self.q.slug
- self.assertRedirects(resp, expected_url=url + '?comment=%d' % self.c.id)
+ self.assertEqual(200, resp.status_code)
resp = self.client.get(url, data={'comment': self.c.id})
self.assertEqual(200, resp.status_code)
@@ -608,6 +606,7 @@ class QuestionPageRedirectTests(AskbotTestCase):
self.assertEqual(self.a, resp.context['show_post'])
self.assertEqual(self.c, resp.context['show_comment'])
- url = reverse('question', kwargs={'id': 101}) + self.q.slug
- resp = self.client.get(url, data={'comment': 301})
- self.assertRedirects(resp, expected_url=self.c.get_absolute_url())
+ url = self.q.get_absolute_url()
+ #point to a non-existing comment
+ resp = self.client.get(url, data={'comment': 100301})
+ self.assertRedirects(resp, expected_url = self.q.get_absolute_url())
diff --git a/askbot/tests/post_model_tests.py b/askbot/tests/post_model_tests.py
index 06bceca1..dd1399c1 100644
--- a/askbot/tests/post_model_tests.py
+++ b/askbot/tests/post_model_tests.py
@@ -167,17 +167,17 @@ class PostModelTests(AskbotTestCase):
th.title = 'lala-x-lala'
p = Post(id=3, post_type='question')
p._thread_cache = th # cannot assign non-Thread instance directly
- self.assertEqual('/question/3/lala-x-lala', p.get_absolute_url(thread=th))
+ self.assertEqual('/question/3/lala-x-lala/', p.get_absolute_url(thread=th))
self.assertTrue(p._thread_cache is th)
- self.assertEqual('/question/3/lala-x-lala', p.get_absolute_url(thread=th))
+ self.assertEqual('/question/3/lala-x-lala/', p.get_absolute_url(thread=th))
def test_cached_get_absolute_url_2(self):
p = Post(id=3, post_type='question')
th = lambda:1
th.title = 'lala-x-lala'
- self.assertEqual('/question/3/lala-x-lala', p.get_absolute_url(thread=th))
+ self.assertEqual('/question/3/lala-x-lala/', p.get_absolute_url(thread=th))
self.assertTrue(p._thread_cache is th)
- self.assertEqual('/question/3/lala-x-lala', p.get_absolute_url(thread=th))
+ self.assertEqual('/question/3/lala-x-lala/', p.get_absolute_url(thread=th))
class ThreadTagModelsTests(AskbotTestCase):
@@ -673,4 +673,4 @@ class ThreadRenderCacheUpdateTests(AskbotTestCase):
# TODO: (in spare time - those cases should pass without changing anything in code but we should have them eventually for completness)
# - Publishing anonymous questions / answers
# - Re-posting question as answer and vice versa
-# - Management commands (like post_emailed_questions) \ No newline at end of file
+# - Management commands (like post_emailed_questions)
diff --git a/askbot/urls.py b/askbot/urls.py
index f4768412..799cc346 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -367,6 +367,8 @@ urlpatterns = patterns('',
),
)
+#todo - this url below won't work, because it is defined above
+#therefore the stackexchange urls feature won't work
if getattr(settings, 'ASKBOT_USE_STACKEXCHANGE_URLS', False):
urlpatterns += (url(
r'^%s(?P<id>\d+)/' % _('questions/'),
diff --git a/askbot/utils/decorators.py b/askbot/utils/decorators.py
index 940c3654..0042df8f 100644
--- a/askbot/utils/decorators.py
+++ b/askbot/utils/decorators.py
@@ -87,7 +87,7 @@ def ajax_only(view_func):
if data is None:
data = {}
except Exception, e:
- if isinstance(e, Exception):
+ if hasattr(e, 'messages'):
if len(e.messages) > 1:
message = u'<ul>' + \
u''.join(
diff --git a/askbot/utils/mail.py b/askbot/utils/mail.py
index 37245b5a..aa4df320 100644
--- a/askbot/utils/mail.py
+++ b/askbot/utils/mail.py
@@ -281,7 +281,7 @@ def process_emailed_question(from_address, subject, parts, tags = None):
user.post_question(
title = title,
- tags = tagnames,
+ tags = tagnames.strip(),
body_text = body_text,
by_email = True,
email_address = from_address
diff --git a/askbot/views/commands.py b/askbot/views/commands.py
index 2cad834e..6fd493cc 100644
--- a/askbot/views/commands.py
+++ b/askbot/views/commands.py
@@ -110,7 +110,7 @@ def manage_inbox(request):
mail.send_mail(
subject_line = _('your post was not accepted'),
body_text = unicode(body_text),
- recipient_list = [post.author,]
+ recipient_list = [post.author.email,]
)
memo.delete()
@@ -589,7 +589,7 @@ def set_tag_filter_strategy(request):
"""
filter_type = request.POST['filter_type']
filter_value = int(request.POST['filter_value'])
- assert(filter_type in 'display', 'email')
+ assert(filter_type in ('display', 'email'))
if filter_type == 'display':
assert(filter_value in dict(const.TAG_DISPLAY_FILTER_STRATEGY_CHOICES))
request.user.display_tag_filter_strategy = filter_value
diff --git a/askbot/views/readers.py b/askbot/views/readers.py
index 613cb3c9..3259cddd 100644
--- a/askbot/views/readers.py
+++ b/askbot/views/readers.py
@@ -76,6 +76,9 @@ def questions(request, **kwargs):
qs, meta_data = models.Thread.objects.run_advanced_search(request_user=request.user, search_state=search_state)
+ if meta_data['non_existing_tags']:
+ search_state = search_state.remove_tags(meta_data['non_existing_tags'])
+
paginator = Paginator(qs, page_size)
if paginator.num_pages < search_state.page:
search_state.page = 1
@@ -129,10 +132,7 @@ def questions(request, **kwargs):
if request.is_ajax():
q_count = paginator.count
- if search_state.tags:
- question_counter = ungettext('%(q_num)s question, tagged', '%(q_num)s questions, tagged', q_count)
- else:
- question_counter = ungettext('%(q_num)s question', '%(q_num)s questions', q_count)
+ question_counter = ungettext('%(q_num)s question', '%(q_num)s questions', q_count)
question_counter = question_counter % {'q_num': humanize.intcomma(q_count),}
if q_count > page_size:
@@ -166,6 +166,7 @@ def questions(request, **kwargs):
'query_string': search_state.query_string(),
'page_size' : page_size,
'questions': questions_html.replace('\n',''),
+ 'non_existing_tags': meta_data['non_existing_tags']
}
ajax_data['related_tags'] = [{
'name': tag.name,
@@ -358,7 +359,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
return HttpResponseRedirect(reverse('index'))
#redirect if slug in the url is wrong
- if request.path.split('/')[-1] != question_post.slug:
+ if request.path.split('/')[-2] != question_post.slug:
logging.debug('no slug match!')
question_url = '?'.join((
question_post.get_absolute_url(),
@@ -391,7 +392,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
'deleted and is no longer accessible'
)
request.user.message_set.create(message = error_message)
- return HttpResponseRedirect(reverse('question', kwargs = {'id': id}))
+ return HttpResponseRedirect(question_post.thread.get_absolute_url())
if str(show_comment.thread._question_post().id) != str(id):
return HttpResponseRedirect(show_comment.get_absolute_url())
diff --git a/askbot/views/writers.py b/askbot/views/writers.py
index c77c874f..935b7cfa 100644
--- a/askbot/views/writers.py
+++ b/askbot/views/writers.py
@@ -540,9 +540,10 @@ def __generate_comments_json(obj, user):#non-view generates json data for the po
comment_owner = comment.author
+ tz = ' ' + template_filters.TIMEZONE_STR
comment_data = {'id' : comment.id,
'object_id': obj.id,
- 'comment_added_at': str(comment.added_at.replace(microsecond = 0)),
+ 'comment_added_at': str(comment.added_at.replace(microsecond = 0)) + tz,
'html': comment.html,
'user_display_name': comment_owner.username,
'user_url': comment_owner.get_profile_url(),
@@ -602,11 +603,12 @@ def edit_comment(request):
is_deletable = template_filters.can_delete_comment(comment_post.author, comment_post)
is_editable = template_filters.can_edit_comment(comment_post.author, comment_post)
+ tz = ' ' + template_filters.TIMEZONE_STR
return {
'id' : comment_post.id,
'object_id': comment_post.parent.id,
- 'comment_added_at': str(comment_post.added_at.replace(microsecond = 0)),
+ 'comment_added_at': str(comment_post.added_at.replace(microsecond = 0)) + tz,
'html': comment_post.html,
'user_display_name': comment_post.author.username,
'user_url': comment_post.author.get_profile_url(),