summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askbot/context.py34
-rw-r--r--askbot/forms.py9
-rw-r--r--askbot/media/js/editor.js1
-rw-r--r--askbot/media/js/post.js2
-rw-r--r--askbot/media/js/tinymce/plugins/askbot_attachment/editor_plugin.js14
-rw-r--r--askbot/media/js/tinymce/plugins/askbot_imageuploader/editor_plugin.js12
-rw-r--r--askbot/media/js/utils.js41
-rw-r--r--askbot/models/__init__.py7
-rw-r--r--askbot/models/post.py63
-rw-r--r--askbot/models/question.py40
-rw-r--r--askbot/models/user.py7
-rw-r--r--askbot/setup_templates/settings.py2
-rw-r--r--askbot/setup_templates/settings.py.mustache2
-rw-r--r--askbot/setup_templates/tinymce_sample_config.py4
-rw-r--r--askbot/startup_procedures.py34
-rw-r--r--askbot/templates/email/base_mail.html4
-rw-r--r--askbot/templates/email/instant_notification.html3
-rw-r--r--askbot/templates/email/macros.html16
-rw-r--r--askbot/templates/main_page/tag_search.html1
-rw-r--r--askbot/templates/user_inbox/base.html6
-rw-r--r--askbot/templatetags/extra_filters_jinja.py10
-rw-r--r--askbot/tests/db_api_tests.py14
-rw-r--r--askbot/tests/email_alert_tests.py24
-rw-r--r--askbot/tests/templatefilter_tests.py8
-rw-r--r--askbot/utils/decorators.py1
-rw-r--r--askbot/utils/forms.py12
-rw-r--r--askbot/views/writers.py4
27 files changed, 273 insertions, 102 deletions
diff --git a/askbot/context.py b/askbot/context.py
index 48c1cf3b..5e53febc 100644
--- a/askbot/context.py
+++ b/askbot/context.py
@@ -59,21 +59,33 @@ def application_settings(request):
}
if askbot_settings.GROUPS_ENABLED:
-
+ #calculate context needed to list all the groups
def _get_group_url(group):
+ """calculates url to the group based on its id and name"""
group_slug = slugify(group['name'])
- return reverse('users_by_group',
- kwargs={'group_id': group['id'],
- 'group_slug': group_slug})
-
+ return reverse(
+ 'users_by_group',
+ kwargs={'group_id': group['id'], 'group_slug': group_slug}
+ )
+ #load id's and names of all groups
global_group = models.tag.get_global_group()
- groups = models.Group.objects.exclude_personal().exclude(id=global_group.id).order_by('name')
- groups = groups.values('id', 'name')
- group_list = [{'link': _get_group_url({'name': global_group.name,
- 'id': global_group.id}),
- 'name': global_group.name},]
- for group in groups:
+ groups = models.Group.objects.exclude_personal()
+ groups = groups.exclude(id=global_group.id)
+ groups_data = list(groups.values('id', 'name'))
+
+ #sort groups_data alphanumerically, but case-insensitive
+ groups_data = sorted(
+ groups_data,
+ lambda x, y: cmp(x['name'].lower(), y['name'].lower())
+ )
+
+ #insert data for the global group at the first position
+ groups_data.insert(0, {'id': global_group.id, 'name': global_group.name})
+
+ #build group_list for the context
+ group_list = list()
+ for group in groups_data:
link = _get_group_url(group)
group_list.append({'name': group['name'], 'link': link})
context['group_list'] = simplejson.dumps(group_list)
diff --git a/askbot/forms.py b/askbot/forms.py
index 8548683f..0659e338 100644
--- a/askbot/forms.py
+++ b/askbot/forms.py
@@ -365,6 +365,10 @@ class TagNamesField(forms.CharField):
attrs={'size': 50, 'autocomplete': 'off'}
)
self.max_length = 255
+ self.error_messages['max_length'] = _(
+ 'We ran out of space for recording the tags. '
+ 'Please shorten or delete some of them.'
+ )
self.label = _('tags')
self.help_text = ungettext_lazy(
'Tags are short keywords, with no spaces within. '
@@ -412,6 +416,11 @@ class TagNamesField(forms.CharField):
if cleaned_tag not in cleaned_entered_tags:
cleaned_entered_tags.append(clean_tag(tag))
+ result = u' '.join(cleaned_entered_tags)
+
+ if len(result) > 125:#magic number!, the same as max_length in db
+ raise forms.ValidationError(self.error_messages['max_length'])
+
return u' '.join(cleaned_entered_tags)
diff --git a/askbot/media/js/editor.js b/askbot/media/js/editor.js
index c6f1c873..25fdaa7e 100644
--- a/askbot/media/js/editor.js
+++ b/askbot/media/js/editor.js
@@ -69,7 +69,6 @@ function ajaxFileUpload(options) {
$('#' + uploadInputId).unbind('change').change(startUploadHandler);
},
error: function (data, status, e) {
- alert(e);
if (startUploadHandler){
/* re-install this as the upload extension
* will remove the handler to prevent double uploading */
diff --git a/askbot/media/js/post.js b/askbot/media/js/post.js
index 4c0dfa67..483973ec 100644
--- a/askbot/media/js/post.js
+++ b/askbot/media/js/post.js
@@ -1241,7 +1241,6 @@ var questionRetagger = function(){
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
var tagAc = new AutoCompleter({
url: askbot['urls']['get_tag_list'],
@@ -1253,6 +1252,7 @@ var questionRetagger = function(){
delay: 10
});
tagAc.decorate(tagInput);
+ tagInput.val(old_tags_string);
div.append(tagInput);
//div.append(tagLabel);
setupInputEventHandlers(tagInput);
diff --git a/askbot/media/js/tinymce/plugins/askbot_attachment/editor_plugin.js b/askbot/media/js/tinymce/plugins/askbot_attachment/editor_plugin.js
index b1d9a918..717a4716 100644
--- a/askbot/media/js/tinymce/plugins/askbot_attachment/editor_plugin.js
+++ b/askbot/media/js/tinymce/plugins/askbot_attachment/editor_plugin.js
@@ -10,15 +10,21 @@
(function() {
var insertIntoDom = function(url, description) {
- var sel = tinyMCE.activeEditor.selection;
-
var content = '<a href="' + url;
if (description) {
content = content + '" title="' + description;
}
- content = content + '">see attachment</a>';
+ content = content + '"/>';
- sel.setContent(content);
+ tinyMCE.activeEditor.focus();
+ if (document.selection) {
+ //this branch is a work around the IE's "this" quirk
+ var sel = document.selection.createRange();
+ sel.pasteHTML(content);
+ } else {
+ var sel = tinyMCE.activeEditor.selection;
+ sel.setContent(content);
+ }
};
var modalMenuHeadline = gettext('Insert a file');
diff --git a/askbot/media/js/tinymce/plugins/askbot_imageuploader/editor_plugin.js b/askbot/media/js/tinymce/plugins/askbot_imageuploader/editor_plugin.js
index f23d89db..7fa6b6be 100644
--- a/askbot/media/js/tinymce/plugins/askbot_imageuploader/editor_plugin.js
+++ b/askbot/media/js/tinymce/plugins/askbot_imageuploader/editor_plugin.js
@@ -10,15 +10,21 @@
(function() {
var insertIntoDom = function(url, description) {
- var sel = tinyMCE.activeEditor.selection;
-
var content = '<img src="' + url;
if (description) {
content = content + '" alt="' + description;
}
content = content + '"/>';
- sel.setContent(content);
+ tinyMCE.activeEditor.focus();
+ if (document.selection) {
+ //this branch is a work around the IE "this" quirk
+ var sel = document.selection.createRange();
+ sel.pasteHTML(content);
+ } else {
+ var sel = tinyMCE.activeEditor.selection;
+ sel.setContent(content);
+ }
};
var modalMenuHeadline = gettext('Upload an image');
diff --git a/askbot/media/js/utils.js b/askbot/media/js/utils.js
index f34ee876..f6b46de5 100644
--- a/askbot/media/js/utils.js
+++ b/askbot/media/js/utils.js
@@ -1712,14 +1712,37 @@ GroupDropdown.prototype.decorate = function(element){
}
};
-GroupDropdown.prototype.prependGroup = function(group_name, url){
- new_group_li = this.makeElement('li');
- new_group_a = this.makeElement('a');
- new_group_a.attr('href', url);
- new_group_a.attr('class', 'group-name');
- new_group_a.text(group_name);
- new_group_li.append(new_group_a);
- this._element.prepend(new_group_li);
+GroupDropdown.prototype.insertGroup = function(group_name, url){
+ var new_group_li = this.makeElement('li');
+ new_group_a = this.makeElement('a');
+ new_group_a.attr('href', url);
+ new_group_a.attr('class', 'group-name');
+ new_group_a.text(group_name);
+ new_group_li.append(new_group_a);
+ links_array = this._element.find('a')
+ for (i=1; i < links_array.length; i++){
+ var listedName = links_array[i].text;
+ var cleanedListedName = listedName.toLowerCase();
+ var cleanedNewName = group_name.toLowerCase()
+ if (listedName < newName) {
+ if (i == links_array.length - 1){
+ new_group_li.insertAfter(this._element.find('li')[i-1])
+ break;
+ } else {
+ continue;
+ }
+ } else if (cleanedNewName === cleanedNewName) {
+ var message = interpolate(gettext(
+ 'Group %(name)s already exists. Group names are case-insensitive.'
+ ), {'name': listedName}, true
+ );
+ notify.show(message);
+ return;
+ } else {
+ new_group_li.insertAfter(this._element.find('li')[i-1])
+ break;
+ }
+ }
};
GroupDropdown.prototype._add_group_handler = function(group_name){
@@ -1735,7 +1758,7 @@ GroupDropdown.prototype._add_group_handler = function(group_name){
data: {group: group_name},
success: function(data){
if (data.success){
- self.prependGroup(data.group_name, data.url);
+ self.insertGroup(data.group_name, data.url);
self._input_box_element.hide();
self._add_link.show();
return true;
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index 2044dc16..d8f30503 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -1754,7 +1754,7 @@ def user_create_post_reject_reason(
added_at = timestamp,
text = details
)
- details.parse_and_save(author = self)
+ details.parse_and_save(author=self)
details.add_revision(
author = self,
revised_at = timestamp,
@@ -2942,6 +2942,7 @@ def format_instant_notification_email(
user_action = user_action % {
'user': '<a href="%s">%s</a>' % (user_url, from_user.username),
'post_link': '<a href="%s">%s</a>' % (post_url, _(post.post_type))
+ #'post_link': '%s <a href="%s">>>></a>' % (_(post.post_type), post_url)
}
can_reply = to_user.can_post_by_email()
@@ -2959,6 +2960,10 @@ def format_instant_notification_email(
reply_separator += '<p>' + \
const.REPLY_WITH_COMMENT_TEMPLATE % data
reply_separator += '</p>'
+ else:
+ reply_separator = '<p>%s</p>' % reply_separator
+
+ reply_separator += user_action
else:
reply_separator = user_action
diff --git a/askbot/models/post.py b/askbot/models/post.py
index 49220a63..57847088 100644
--- a/askbot/models/post.py
+++ b/askbot/models/post.py
@@ -225,7 +225,18 @@ class PostManager(BaseQuerySetManager):
is_private = is_private or \
(thread and thread.requires_response_moderation(author))
- post.parse_and_save(author=author, is_private=is_private)
+ parse_results = post.parse_and_save(author=author, is_private=is_private)
+
+ from askbot.models import signals
+ signals.post_updated.send(
+ post=post,
+ updated_by=author,
+ newly_mentioned_users=parse_results['newly_mentioned_users'],
+ timestamp=added_at,
+ created=True,
+ diff=parse_results['diff'],
+ sender=post.__class__
+ )
post.add_revision(
author = author,
@@ -529,19 +540,6 @@ class Post(models.Model):
timestamp = self.get_time_of_last_edit()
- #todo: this is handled in signal because models for posts
- #are too spread out
- from askbot.models import signals
- signals.post_updated.send(
- post = self,
- updated_by = author,
- newly_mentioned_users = newly_mentioned_users,
- timestamp = timestamp,
- created = created,
- diff = diff,
- sender = self.__class__
- )
-
try:
from askbot.conf import settings as askbot_settings
if askbot_settings.GOOGLE_SITEMAP_CODE != '':
@@ -549,6 +547,8 @@ class Post(models.Model):
except Exception:
logging.debug('cannot ping google - did you register with them?')
+ return {'diff': diff, 'newly_mentioned_users': newly_mentioned_users}
+
def is_question(self):
return self.post_type == 'question'
@@ -840,15 +840,12 @@ class Post(models.Model):
if quote_level > 0, the post will be indented that number of times
todo: move to views?
"""
- from askbot.templatetags.extra_filters_jinja \
- import absolutize_urls_func
from askbot.skins.loaders import get_template
from django.template import Context
template = get_template('email/quoted_post.html')
data = {
'post': self,
'quote_level': quote_level,
- #'html': absolutize_urls_func(self.html),
'is_leaf_post': is_leaf_post,
'format': format
}
@@ -1629,7 +1626,19 @@ class Post(models.Model):
by_email = by_email
)
- self.parse_and_save(author=edited_by, is_private=is_private)
+ parse_results = self.parse_and_save(author=edited_by, is_private=is_private)
+
+ from askbot.models import signals
+ signals.post_updated.send(
+ post=self,
+ updated_by=edited_by,
+ newly_mentioned_users=parse_results['newly_mentioned_users'],
+ timestamp=edited_at,
+ created=False,
+ diff=parse_results['diff'],
+ sender=self.__class__
+ )
+
def _answer__apply_edit(
self,
@@ -1947,11 +1956,11 @@ class PostRevision(models.Model):
)
post = models.ForeignKey('askbot.Post', related_name='revisions', null=True, blank=True)
- revision = models.PositiveIntegerField()
- author = models.ForeignKey('auth.User', related_name='%(class)ss')
+ revision = models.PositiveIntegerField()
+ author = models.ForeignKey('auth.User', related_name='%(class)ss')
revised_at = models.DateTimeField()
- summary = models.CharField(max_length=300, blank=True)
- text = models.TextField()
+ summary = models.CharField(max_length=300, blank=True)
+ text = models.TextField()
approved = models.BooleanField(default=False, db_index=True)
approved_by = models.ForeignKey(User, null = True, blank = True)
@@ -1961,8 +1970,8 @@ class PostRevision(models.Model):
email_address = models.EmailField(null = True, blank = True)
# Question-specific fields
- title = models.CharField(max_length=300, blank=True, default='')
- tagnames = models.CharField(max_length=125, blank=True, default='')
+ title = models.CharField(max_length=300, blank=True, default='')
+ tagnames = models.CharField(max_length=125, blank=True, default='')
is_anonymous = models.BooleanField(default=False)
objects = PostRevisionManager()
@@ -2105,10 +2114,10 @@ class PostRevision(models.Model):
# Determine the revision number, if not set
if not self.revision:
# TODO: Maybe use Max() aggregation? Or `revisions.count() + 1`
- self.revision = self.parent().revisions.values_list('revision', flat=True)[0] + 1
-
+ self.revision = self.parent().revisions.values_list(
+ 'revision', flat=True
+ )[0] + 1
self.full_clean()
-
super(PostRevision, self).save(**kwargs)
def get_absolute_url(self):
diff --git a/askbot/models/question.py b/askbot/models/question.py
index a4b3233a..6b233188 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -133,7 +133,8 @@ class ThreadManager(BaseQuerySetManager):
question.wikified_at = added_at
#this is kind of bad, but we save assign privacy groups to posts and thread
- question.parse_and_save(author = author, is_private = is_private)
+ #this call is rather heavy, we should split into several functions
+ parse_results = question.parse_and_save(author=author, is_private=is_private)
revision = question.add_revision(
author=author,
@@ -155,7 +156,19 @@ class ThreadManager(BaseQuerySetManager):
thread.make_public()
# INFO: Question has to be saved before update_tags() is called
- thread.update_tags(tagnames = tagnames, user = author, timestamp = added_at)
+ thread.update_tags(tagnames=tagnames, user=author, timestamp=added_at)
+
+ #todo: this is handled in signal because models for posts
+ #are too spread out
+ signals.post_updated.send(
+ post=question,
+ updated_by=author,
+ newly_mentioned_users=parse_results['newly_mentioned_users'],
+ timestamp=added_at,
+ created=True,
+ diff=parse_results['diff'],
+ sender=question.__class__
+ )
return thread
@@ -246,16 +259,19 @@ class ThreadManager(BaseQuerySetManager):
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
+ existing_tags = set()
+ non_existing_tags = set()
+ #we're using a one-by-one tag retreival, b/c
+ #we want to take advantage of case-insensitive search indexes
+ #in postgresql, plus it is most likely that there will be
+ #only one or two search tags anyway
+ for tag in tags:
+ try:
+ tag_record = Tag.objects.get(name__iexact=tag)
+ existing_tags.add(tag_record.name)
+ except Tag.DoesNotExist:
+ non_existing_tags.add(tag)
+
meta_data['non_existing_tags'] = list(non_existing_tags)
tags = existing_tags
else:
diff --git a/askbot/models/user.py b/askbot/models/user.py
index bdb94100..39bb8ea9 100644
--- a/askbot/models/user.py
+++ b/askbot/models/user.py
@@ -271,6 +271,13 @@ class EmailFeedSetting(models.Model):
'q_sel': 'n',
'm_and_c': 'n'
}
+ MAX_EMAIL_SCHEDULE = {
+ 'q_ask': 'i',
+ 'q_ans': 'i',
+ 'q_all': 'i',
+ 'q_sel': 'i',
+ 'm_and_c': 'i'
+ }
FEED_TYPE_CHOICES = (
('q_all',_('Entire forum')),
('q_ask',_('Questions that I asked')),
diff --git a/askbot/setup_templates/settings.py b/askbot/setup_templates/settings.py
index a257a9f0..8577281c 100644
--- a/askbot/setup_templates/settings.py
+++ b/askbot/setup_templates/settings.py
@@ -230,7 +230,7 @@ CSRF_COOKIE_NAME = 'askbot_csrf'
#CSRF_COOKIE_DOMAIN = ''
STATICFILES_DIRS = (
- ('default/media', os.path.join(ASKBOT_ROOT, 'media'),
+ ('default/media', os.path.join(ASKBOT_ROOT, 'media')),
)
RECAPTCHA_USE_SSL = True
diff --git a/askbot/setup_templates/settings.py.mustache b/askbot/setup_templates/settings.py.mustache
index 8ed69803..6a40969d 100644
--- a/askbot/setup_templates/settings.py.mustache
+++ b/askbot/setup_templates/settings.py.mustache
@@ -231,7 +231,7 @@ CSRF_COOKIE_NAME = '{{domain_name}}_csrf'
STATIC_ROOT = os.path.join(PROJECT_ROOT, "static")
STATICFILES_DIRS = (
- ('default/media', os.path.join(ASKBOT_ROOT, 'media'),
+ ('default/media', os.path.join(ASKBOT_ROOT, 'media')),
)
RECAPTCHA_USE_SSL = True
diff --git a/askbot/setup_templates/tinymce_sample_config.py b/askbot/setup_templates/tinymce_sample_config.py
index c75170b0..ac49da68 100644
--- a/askbot/setup_templates/tinymce_sample_config.py
+++ b/askbot/setup_templates/tinymce_sample_config.py
@@ -1,7 +1,7 @@
TINYMCE_COMPRESSOR = True
TINYMCE_SPELLCHECKER = False
-TINYMCE_JS_ROOT = os.path.join(STATIC_ROOT, 'common/media/js/tinymce/')
-TINYMCE_URL = STATIC_URL + 'common/media/js/tinymce/'
+TINYMCE_JS_ROOT = os.path.join(STATIC_ROOT, 'default/media/js/tinymce/')
+TINYMCE_URL = STATIC_URL + 'default/media/js/tinymce/'
TINYMCE_DEFAULT_CONFIG = {
'plugins': 'askbot_imageuploader,askbot_attachment',
'theme': 'advanced',
diff --git a/askbot/startup_procedures.py b/askbot/startup_procedures.py
index bfce701b..a33f8047 100644
--- a/askbot/startup_procedures.py
+++ b/askbot/startup_procedures.py
@@ -313,6 +313,19 @@ class SettingsTester(object):
'\n\n* '.join(self.messages)
)
+
+def test_new_skins():
+ """tests that there are no directories in the `askbot/skins`
+ because we've moved skin files a few levels up"""
+ askbot_root = askbot.get_install_directory()
+ for item in os.listdir(os.path.join(askbot_root, 'skins')):
+ item_path = os.path.join(askbot_root, 'skins', item)
+ if os.path.isdir(item_path):
+ raise AskbotConfigError(
+ ('Time to move skin files from %s.\n'
+ 'Now we have `askbot/templates` and `askbot/media`') % item_path
+ )
+
def test_staticfiles():
"""tests configuration of the staticfiles app"""
errors = list()
@@ -567,20 +580,28 @@ def test_tinymce():
if compressor_on is False:
errors.append('add line: TINYMCE_COMPRESSOR = True')
- #check js root setting
+ #check js root setting - before version 0.7.44 we used to have
+ #"common" skin and after we combined it into the default
js_root = getattr(django_settings, 'TINYMCE_JS_ROOT', '')
- relative_js_path = 'common/media/js/tinymce/'
+ old_relative_js_path = 'common/media/js/tinymce/'
+ relative_js_path = 'default/media/js/tinymce/'
expected_js_root = os.path.join(django_settings.STATIC_ROOT, relative_js_path)
+ old_expected_js_root = os.path.join(django_settings.STATIC_ROOT, old_relative_js_path)
if os.path.normpath(js_root) != os.path.normpath(expected_js_root):
- js_root_template = "add line: TINYMCE_JS_ROOT = os.path.join(STATIC_ROOT, '%s')"
- errors.append(js_root_template % relative_js_path)
+ error_tpl = "add line: TINYMCE_JS_ROOT = os.path.join(STATIC_ROOT, '%s')"
+ if os.path.normpath(js_root) == os.path.normpath(old_expected_js_root):
+ error_tpl += '\nNote: we have moved files from "common" into "default"'
+ errors.append(error_tpl % relative_js_path)
#check url setting
url = getattr(django_settings, 'TINYMCE_URL', '')
expected_url = django_settings.STATIC_URL + relative_js_path
+ old_expected_url = django_settings.STATIC_URL + old_relative_js_path
if urls_equal(url, expected_url) is False:
- js_url_template = "add line: TINYMCE_URL = STATIC_URL + '%s'"
- errors.append(js_url_template % relative_js_path)
+ error_tpl = "add line: TINYMCE_URL = STATIC_URL + '%s'"
+ if urls_equal(url, old_expected_url):
+ error_tpl += '\nNote: we have moved files from "common" into "default"'
+ errors.append(error_tpl % relative_js_path)
if errors:
header = 'Please add the tynymce editor configuration ' + \
@@ -623,6 +644,7 @@ def run_startup_tests():
#test_csrf_cookie_domain()
test_tinymce()
test_staticfiles()
+ test_new_skins()
test_longerusername()
test_avatar()
settings_tester = SettingsTester({
diff --git a/askbot/templates/email/base_mail.html b/askbot/templates/email/base_mail.html
index dfd252f2..eacbf87d 100644
--- a/askbot/templates/email/base_mail.html
+++ b/askbot/templates/email/base_mail.html
@@ -143,10 +143,10 @@
</tr>
<tr>
<td valign="top">
- <table border="0" align="center" cellspacing="0" cellpadding="0" style="background-color: #fff;" width=80%>
+ <table border="0" align="center" cellspacing="0" cellpadding="0" style="background-color: #fff;" width=90%>
<tr>
<td valign="top">
- <table border="0" align="center" cellspacing="0" cellpadding="0" width=80%>
+ <table border="0" align="center" cellspacing="0" cellpadding="0" width=95%>
<tr>
<td valign="top">
<h1>{%block headline%}{%endblock%}</h1>
diff --git a/askbot/templates/email/instant_notification.html b/askbot/templates/email/instant_notification.html
index 3e8533b6..d353450d 100644
--- a/askbot/templates/email/instant_notification.html
+++ b/askbot/templates/email/instant_notification.html
@@ -4,7 +4,8 @@
{{ reply_separator }}
<div>{{ content_preview }}</div>
{% trans %}
-<p>Please note - you can easily <a href="{{user_subscriptions_url}}">change</a>
+<p style="font-size:10px; font-style:italic;">
+Please note - you can easily <a href="{{user_subscriptions_url}}">change</a>
how often you receive these notifications or unsubscribe. Thank you for your interest in our forum!</p>
{% endtrans %}
{%endblock%}
diff --git a/askbot/templates/email/macros.html b/askbot/templates/email/macros.html
index d7817bf9..f1b06fc8 100644
--- a/askbot/templates/email/macros.html
+++ b/askbot/templates/email/macros.html
@@ -9,7 +9,7 @@
{{ start_quote(quote_level) }}
{% set author = post.author.username|escape %}
{% if post.post_type == 'question' %}
- <p>
+ <p style="font-size:10px; font-weight: bold;">
{% if format == 'parent_subthread' %}
{% if is_leaf_post %}
{% trans %}Question by {{ author }}:{% endtrans %}
@@ -22,21 +22,21 @@
{% trans %}Question :{% endtrans %}
{% endif %}
{{ post.thread.title }}
- </p>
- <p>
+ </p>
+ <p style="font-size:10px; font-weight: bold;">
{% if format != 'parent_subthread' %}
{% trans %}Asked by {{ author }}:{% endtrans %}
{% endif %}
- </p>
+ </p>
{% set tag_names = post.get_tag_names() %}
{% if tag_names %}
- <p>
+ <p style="font-size:10px; font-style:italic;">
{% trans %}Tags:{% endtrans %}
{{ tag_names|join(', ') }}.
</p>
{% endif %}
{% elif post.post_type == 'answer' %}
- <p>
+ <p style="font-size:10px; font-weight: bold;">
{% if format == 'parent_subthread' %}
{% if is_leaf_post %}
{% trans -%}
@@ -52,7 +52,7 @@
{% endif %}
</p>
{% else %}
- <p>
+ <p style="font-size:10px; font-weight: bold;">
{% if format == 'parent_subthread' %}
{% if is_leaf_post %}
{% trans -%}
@@ -70,7 +70,7 @@
{% endif %}
</p>
{% endif %}
- {{ post.html }}
+ {{ post.html|absolutize_urls }}
{{ end_quote(quote_level) }}
{% endspaceless %}
{% endmacro %}
diff --git a/askbot/templates/main_page/tag_search.html b/askbot/templates/main_page/tag_search.html
index 0d81bf4e..45f12b2f 100644
--- a/askbot/templates/main_page/tag_search.html
+++ b/askbot/templates/main_page/tag_search.html
@@ -1,6 +1,5 @@
<div id="tagSearch" class="box">
<h2>{% trans %}Tag search{% endtrans %}</h2>
- <label for="ab-tag-search">{% trans %}Please note that tag search is case sensitive!{% endtrans %}</label>
<div class="inputs">
<input id="ab-tag-search" autocomplete="off" type="text"/>
<input id="ab-tag-search-add" type="submit" value="{% trans %}search{% endtrans %}"/>
diff --git a/askbot/templates/user_inbox/base.html b/askbot/templates/user_inbox/base.html
index 890cb0f7..2f8b805e 100644
--- a/askbot/templates/user_inbox/base.html
+++ b/askbot/templates/user_inbox/base.html
@@ -13,10 +13,10 @@
<div id="re_sections">
{% trans %}Sections:{% endtrans %}
{% set sep = joiner('|') %}
- {{ sep() }}
- <a href="{{request.user.get_absolute_url()}}?sort=inbox&section=messages"
+ {#{ sep() }}
+ {<a href="{{request.user.get_absolute_url()}}?sort=inbox&section=messages"
{% if inbox_section == 'messages' %}class="on"{% endif %}
- >{% trans %}messages{% endtrans %}</a>
+ >{% trans %}messages{% endtrans %}</a>#}
{% if re_count > 0 %}{{ sep() }}
<a href="{{request.user.get_absolute_url()}}?sort=inbox&section=forum"
{% if inbox_section == 'forum' %}class="on"{% endif %}
diff --git a/askbot/templatetags/extra_filters_jinja.py b/askbot/templatetags/extra_filters_jinja.py
index 3643e3c9..62a41895 100644
--- a/askbot/templatetags/extra_filters_jinja.py
+++ b/askbot/templatetags/extra_filters_jinja.py
@@ -24,17 +24,19 @@ from django_countries import settings as countries_settings
register = coffin_template.Library()
-def absolutize_urls_func(text):
+@register.filter
+def absolutize_urls(text):
url_re1 = re.compile(r'(?P<prefix><img[^<]+src=)"(?P<url>/[^"]+)"', re.I)
url_re2 = re.compile(r"(?P<prefix><img[^<]+src=)'(?P<url>/[^']+)'", re.I)
url_re3 = re.compile(r'(?P<prefix><a[^<]+href=)"(?P<url>/[^"]+)"', re.I)
url_re4 = re.compile(r"(?P<prefix><a[^<]+href=)'(?P<url>/[^']+)'", re.I)
+ img_replacement = '\g<prefix>"%s\g<url>" style="max-width:500px;"' % askbot_settings.APP_URL
replacement = '\g<prefix>"%s\g<url>"' % askbot_settings.APP_URL
- text = url_re1.sub(replacement, text)
- text = url_re2.sub(replacement, text)
+ text = url_re1.sub(img_replacement, text)
+ text = url_re2.sub(img_replacement, text)
text = url_re3.sub(replacement, text)
return url_re4.sub(replacement, text)
-absolutize_urls = register.filter(absolutize_urls_func)
+
TIMEZONE_STR = pytz.timezone(
django_settings.TIME_ZONE
diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py
index fa166ba7..ec4210e8 100644
--- a/askbot/tests/db_api_tests.py
+++ b/askbot/tests/db_api_tests.py
@@ -10,6 +10,7 @@ from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django import forms
from askbot.tests.utils import AskbotTestCase
+from askbot.tests.utils import with_settings
from askbot import models
from askbot import const
from askbot.conf import settings as askbot_settings
@@ -164,6 +165,19 @@ class DBApiTests(AskbotTestCase):
count = models.Tag.objects.filter(name='one-tag').count()
self.assertEquals(count, 0)
+
+ @with_settings({
+ 'MAX_TAG_LENGTH': 200,
+ 'MAX_TAGS_PER_POST': 50
+ })
+ def test_retag_tags_too_long_raises(self):
+ tags = "aoaoesuouooeueooeuoaeuoeou aostoeuoaethoeastn oasoeoa nuhoasut oaeeots aoshootuheotuoehao asaoetoeatuoasu o aoeuethut aoaoe uou uoetu uouuou ao aouosutoeh"
+ question = self.post_question(user=self.user)
+ self.assertRaises(
+ exceptions.ValidationError,
+ self.user.retag_question,
+ question=question, tags=tags
+ )
def test_search_with_apostrophe_works(self):
self.post_question(
diff --git a/askbot/tests/email_alert_tests.py b/askbot/tests/email_alert_tests.py
index 1937da6f..f5b5e43b 100644
--- a/askbot/tests/email_alert_tests.py
+++ b/askbot/tests/email_alert_tests.py
@@ -10,6 +10,7 @@ from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.client import Client
from askbot.tests import utils
+from askbot.tests.utils import with_settings
from askbot import models
from askbot import mail
from askbot.conf import settings as askbot_settings
@@ -954,6 +955,29 @@ class EmailFeedSettingTests(utils.AskbotTestCase):
data_after = TO_JSON(self.get_user_feeds())
self.assertEquals(data_before, data_after)
+
+class EmailAlertTestsWithGroupsEnabled(utils.AskbotTestCase):
+
+ def setUp(self):
+ self.backup = askbot_settings.GROUPS_ENABLED
+ askbot_settings.update('GROUPS_ENABLED', True)
+
+ def tearDown(self):
+ askbot_settings.update('GROUPS_ENABLED', self.backup)
+
+ @with_settings({'MIN_REP_TO_TRIGGER_EMAIL': 1})
+ def test_notification_for_global_group_works(self):
+ sender = self.create_user('sender')
+ recipient = self.create_user(
+ 'recipient',
+ notification_schedule=models.EmailFeedSetting.MAX_EMAIL_SCHEDULE
+ )
+ self.post_question(user=sender)
+ outbox = django.core.mail.outbox
+ self.assertEqual(len(outbox), 1)
+ self.assertEqual(outbox[0].recipients(), [recipient.email])
+
+
class PostApprovalTests(utils.AskbotTestCase):
"""test notifications sent to authors when their posts
are approved or published"""
diff --git a/askbot/tests/templatefilter_tests.py b/askbot/tests/templatefilter_tests.py
index a82737a4..090be956 100644
--- a/askbot/tests/templatefilter_tests.py
+++ b/askbot/tests/templatefilter_tests.py
@@ -7,14 +7,16 @@ class AbsolutizeUrlsTests(TestCase):
askbot_settings.update('APP_URL', 'http://example.com')
def test_absolutize_image_urls(self):
text = """<img class="junk" src="/some.gif"> <IMG SRC='/some.png'>"""
- output = filters.absolutize_urls_func(text)
+ #jinja register.filter decorator works in a weird way
+ output = filters.absolutize_urls[0](text)
self.assertEqual(
output,
- '<img class="junk" src="http://example.com/some.gif"> <IMG SRC="http://example.com/some.png">'
+ '<img class="junk" src="http://example.com/some.gif" style="max-width:500px;"> <IMG SRC="http://example.com/some.png" style="max-width:500px;">'
)
def test_absolutize_anchor_urls(self):
text = """<a class="junk" href="/something">link</a> <A HREF='/something'>link</A>"""
- output = filters.absolutize_urls_func(text)
+ #jinja register.filter decorator works in a weird way
+ output = filters.absolutize_urls[0](text)
self.assertEqual(
output,
'<a class="junk" href="http://example.com/something">link</a> <A HREF="http://example.com/something">link</A>'
diff --git a/askbot/utils/decorators.py b/askbot/utils/decorators.py
index 0042df8f..da1cd7db 100644
--- a/askbot/utils/decorators.py
+++ b/askbot/utils/decorators.py
@@ -87,6 +87,7 @@ def ajax_only(view_func):
if data is None:
data = {}
except Exception, e:
+ #todo: also check field called "message"
if hasattr(e, 'messages'):
if len(e.messages) > 1:
message = u'<ul>' + \
diff --git a/askbot/utils/forms.py b/askbot/utils/forms.py
index 6c3af066..6f57f71f 100644
--- a/askbot/utils/forms.py
+++ b/askbot/utils/forms.py
@@ -47,6 +47,18 @@ def get_db_object_or_404(params):
#need catch-all b/c of the nature of the function
raise Http404
+def format_errors(error_list):
+ """If there is only one error - returns a string
+ corresponding to that error, to remove the <ul> tag.
+
+ If there is > 1 error - then convert the error_list into
+ a string.
+ """
+ if len(error_list) == 1:
+ return unicode(error_list[0])
+ else:
+ return unicode(error_list)
+
class StrippedNonEmptyCharField(forms.CharField):
def clean(self, value):
value = value.strip()
diff --git a/askbot/views/writers.py b/askbot/views/writers.py
index 85a92d23..4024b4b0 100644
--- a/askbot/views/writers.py
+++ b/askbot/views/writers.py
@@ -31,6 +31,7 @@ from askbot import models
from askbot.conf import settings as askbot_settings
from askbot.skins.loaders import render_into_skin
from askbot.utils import decorators
+from askbot.utils.forms import format_errors
from askbot.utils.functions import diff_date
from askbot.utils import url_utils
from askbot.utils.file_utils import store_file
@@ -314,6 +315,7 @@ def retag_question(request, id):
request.user.assert_can_retag_question(question)
if request.method == 'POST':
form = forms.RetagQuestionForm(question, request.POST)
+
if form.is_valid():
if form.has_changed():
request.user.retag_question(question=question, tags=form.cleaned_data['tags'])
@@ -334,7 +336,7 @@ def retag_question(request, id):
return HttpResponseRedirect(question.get_absolute_url())
elif request.is_ajax():
response_data = {
- 'message': unicode(form.errors['tags']),
+ 'message': format_errors(form.errors['tags']),
'success': False
}
data = simplejson.dumps(response_data)