summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-05-07 16:31:15 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-05-07 16:31:15 -0400
commitf7d0836fef60e876be23cb15747865aadfe27379 (patch)
tree87f6238b6d27eeffff72f3921808e58fdec918a0
parent0c46a9b987373e5a791cf1c5bd731a682aa652c6 (diff)
downloadaskbot-f7d0836fef60e876be23cb15747865aadfe27379.tar.gz
askbot-f7d0836fef60e876be23cb15747865aadfe27379.tar.bz2
askbot-f7d0836fef60e876be23cb15747865aadfe27379.zip
added a possibility to have independent set of "subscribed" tags in addition to "favorite/ignored"
-rw-r--r--askbot/conf/forum_data_rules.py14
-rw-r--r--askbot/const/__init__.py13
-rw-r--r--askbot/forms.py2
-rw-r--r--askbot/management/commands/get_tag_stats.py50
-rw-r--r--askbot/migrations/0122_auth_user__add_subscribed_tag_field.py320
-rw-r--r--askbot/models/__init__.py69
-rw-r--r--askbot/models/question.py15
-rw-r--r--askbot/models/tag.py6
-rw-r--r--askbot/skins/common/media/js/tag_selector.js64
-rw-r--r--askbot/skins/common/templates/widgets/tag_selector.html33
-rw-r--r--askbot/skins/default/media/style/style.less9
-rw-r--r--askbot/skins/default/templates/main_page/javascript.html1
-rw-r--r--askbot/urls.py7
-rw-r--r--askbot/views/commands.py2
-rw-r--r--askbot/views/readers.py8
15 files changed, 546 insertions, 67 deletions
diff --git a/askbot/conf/forum_data_rules.py b/askbot/conf/forum_data_rules.py
index be23eab7..491ebfa8 100644
--- a/askbot/conf/forum_data_rules.py
+++ b/askbot/conf/forum_data_rules.py
@@ -187,6 +187,20 @@ settings.register(
)
settings.register(
+ livesettings.BooleanValue(
+ FORUM_DATA_RULES,
+ 'SUBSCRIBED_TAG_SELECTOR_ENABLED',
+ default = False,
+ description = _('Use separate set for subscribed tags'),
+ help_text = _(
+ 'If enabled, users will have a third set of tag selections '
+ '- "subscribed" (by email) in additon to "interesting" '
+ 'and "ignored"'
+ )
+ )
+)
+
+settings.register(
livesettings.IntegerValue(
FORUM_DATA_RULES,
'MAX_COMMENTS_TO_SHOW',
diff --git a/askbot/const/__init__.py b/askbot/const/__init__.py
index 305239f5..7eef91d1 100644
--- a/askbot/const/__init__.py
+++ b/askbot/const/__init__.py
@@ -249,10 +249,15 @@ POST_STATUS = {
INCLUDE_ALL = 0
EXCLUDE_IGNORED = 1
INCLUDE_INTERESTING = 2
-TAG_FILTER_STRATEGY_CHOICES = (
- (INCLUDE_ALL, _('off')),
- (EXCLUDE_IGNORED, _('exclude ignored')),
- (INCLUDE_INTERESTING, _('only selected')),
+TAG_DISPLAY_FILTER_STRATEGY_CHOICES = (
+ (INCLUDE_ALL, _('show all tags')),
+ (EXCLUDE_IGNORED, _('exclude ignored tags')),
+ (INCLUDE_INTERESTING, _('only interesting tags')),
+)
+TAG_EMAIL_FILTER_STRATEGY_CHOICES = (
+ (INCLUDE_ALL, _('email for all tags')),
+ (EXCLUDE_IGNORED, _('exclude ignored tags')),
+ (INCLUDE_INTERESTING, _('only subscribed tags')),
)
NOTIFICATION_DELIVERY_SCHEDULE_CHOICES = (
diff --git a/askbot/forms.py b/askbot/forms.py
index c5b72365..11df5f8d 100644
--- a/askbot/forms.py
+++ b/askbot/forms.py
@@ -1020,7 +1020,7 @@ class EditUserForm(forms.Form):
class TagFilterSelectionForm(forms.ModelForm):
email_tag_filter_strategy = forms.ChoiceField(
- choices = const.TAG_FILTER_STRATEGY_CHOICES,
+ choices = const.TAG_DISPLAY_FILTER_STRATEGY_CHOICES,
initial = const.EXCLUDE_IGNORED,
label = _('Choose email tag filter'),
widget = forms.RadioSelect
diff --git a/askbot/management/commands/get_tag_stats.py b/askbot/management/commands/get_tag_stats.py
index c30643b3..b065a6a1 100644
--- a/askbot/management/commands/get_tag_stats.py
+++ b/askbot/management/commands/get_tag_stats.py
@@ -2,6 +2,7 @@ import sys
import optparse
from django.core.management.base import BaseCommand, CommandError
from askbot import models
+from askbot import const
def get_tag_lines(tag_marks, width = 25):
output = list()
@@ -120,15 +121,30 @@ class Command(BaseCommand):
for bad_tag in user.ignored_tags.split():
ignored_tags.append(bad_tag)
+ subscribed_tags = list()
+ subscribed_tags.extend(
+ tag_marks.filter(
+ reason='subscribed'
+ ).values_list(
+ 'tag__name', flat = True
+ )
+ )
+
+ for subscribed_tag in user.subscribed_tags.split():
+ subscribed_tags.append(subscribed_tag)
+
followed_count = len(followed_tags)
ignored_count = len(ignored_tags)
- if followed_count == 0 and ignored_count == 0 and print_empty == False:
+ subscribed_count = len(subscribed_tags)
+ total_count = followed_count + ignored_count + subscribed_count
+ if total_count == 0 and print_empty == False:
continue
if item_count == 0:
- print '%-28s %25s %25s' % ('User (id)', 'Interesting tags', 'Ignored tags')
- print '%-28s %25s %25s' % ('=========', '================', '============')
+ print '%-28s %25s %25s %25s' % ('User (id)', 'Interesting tags', 'Ignored tags', 'Subscribed tags')
+ print '%-28s %25s %25s %25s' % ('=========', '================', '============', '===============')
followed_lines = get_tag_lines(followed_tags, width = 25)
ignored_lines = get_tag_lines(ignored_tags, width = 25)
+ subscribed_lines = get_tag_lines(subscribed_tags, width = 25)
follow = '*'
if user.email_tag_filter_strategy == const.INCLUDE_INTERESTING:
@@ -138,7 +154,8 @@ class Command(BaseCommand):
[user_string,],
followed_lines,
ignored_lines,
- format_string = '%-28s %25s %25s'
+ subscribed_lines,
+ format_string = '%-28s %25s %25s %25s'
)
item_count += 1
for line in output_lines:
@@ -163,16 +180,23 @@ class Command(BaseCommand):
interesting_tags = models.Tag.objects.get_by_wildcards(wk)
for tag in interesting_tags:
if tag.name not in wild:
- wild[tag.name] = [0, 0]
+ wild[tag.name] = [0, 0, 0]
wild[tag.name][0] += 1
wk = user.ignored_tags.strip().split()
ignored_tags = models.Tag.objects.get_by_wildcards(wk)
for tag in ignored_tags:
if tag.name not in wild:
- wild[tag.name] = [0, 0]
+ wild[tag.name] = [0, 0, 0]
wild[tag.name][1] += 1
+ wk = user.subscribed_tags.strip().split()
+ subscribed_tags = models.Tag.objects.get_by_wildcards(wk)
+ for tag in subscribed_tags:
+ if tag.name not in wild:
+ wild[tag.name] = [0, 0, 0]
+ wild[tag.name][2] += 1
+
return wild
def print_sub_counts(self, print_empty):
@@ -185,6 +209,7 @@ class Command(BaseCommand):
for tag in tags:
wild_follow = 0
wild_ignore = 0
+ wild_sub = 0
if tag.name in wild_tags:
(wild_follow, wild_ignore) = wild_tags[tag.name]
@@ -193,17 +218,22 @@ class Command(BaseCommand):
+ wild_follow
ignore_count = tag_marks.filter(reason='bad').count() \
+ wild_ignore
+ subscribe_count = tag_marks.filter(reason='subscribe').count() \
+ + wild_sub
follow_str = '%d (%d)' % (follow_count, wild_follow)
ignore_str = '%d (%d)' % (ignore_count, wild_ignore)
+ subscribe_str = '%d (%d)' % (subscribe_count, wild_sub)
+ counts = (11-len(subscribe_str)) * ' ' + subscribe_str + ' '
counts = (11-len(follow_str)) * ' ' + follow_str + ' '
counts += (11-len(ignore_str)) * ' ' + ignore_str
- if follow_count + ignore_count == 0 and print_empty == False:
+ total_count = follow_count + ignore_count + subscribe_count
+ if total_count == 0 and print_empty == False:
continue
if item_count == 0:
- print '%-32s %12s %12s' % ('', 'Interesting', 'Ignored ')
- print '%-32s %12s %12s' % ('Tag name', 'Total(wild)', 'Total(wild)')
- print '%-32s %12s %12s' % ('========', '===========', '===========')
+ print '%-32s %12s %12s %12s' % ('', 'Subscribed', 'Ignored ', 'Interesting')
+ print '%-32s %12s %12s %12s' % ('Tag name', 'Total(wild)', 'Total(wild)', 'Total(wild)')
+ print '%-32s %12s %12s %12s' % ('========', '===========', '===========', '===========')
print '%-32s %s' % (tag.name, counts)
item_count += 1
diff --git a/askbot/migrations/0122_auth_user__add_subscribed_tag_field.py b/askbot/migrations/0122_auth_user__add_subscribed_tag_field.py
new file mode 100644
index 00000000..50d4c379
--- /dev/null
+++ b/askbot/migrations/0122_auth_user__add_subscribed_tag_field.py
@@ -0,0 +1,320 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ try:
+ # Adding field 'User.interesting_tags'
+ db.add_column(u'auth_user', 'subscribed_tags', self.gf('django.db.models.fields.TextField')(blank=True, default = ''), keep_default=False)
+ except:
+ pass
+
+ def backwards(self, orm):
+ # Deleting field 'User.interesting_tags'
+ db.delete_column('auth_user', 'subscribed_tags')
+
+
+ models = {
+ 'askbot.activity': {
+ 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"},
+ 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True'}),
+ 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'incoming_activity'", 'symmetrical': 'False', 'through': "orm['askbot.ActivityAuditStatus']", 'to': "orm['auth.User']"}),
+ 'summary': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.activityauditstatus': {
+ 'Meta': {'unique_together': "(('user', 'activity'),)", 'object_name': 'ActivityAuditStatus'},
+ 'activity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Activity']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.anonymousanswer': {
+ 'Meta': {'object_name': 'AnonymousAnswer'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_answers'", 'to': "orm['askbot.Post']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.anonymousquestion': {
+ 'Meta': {'object_name': 'AnonymousQuestion'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'GroupMembership'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_memberships'", 'to': "orm['askbot.Tag']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_memberships'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.groupprofile': {
+ 'Meta': {'object_name': 'GroupProfile'},
+ 'group_tag': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group_profile'", 'unique': 'True', 'to': "orm['askbot.Tag']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_open': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'null': 'True'})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'old_answer_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_comment_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_question_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'posts'", 'null': 'True', 'blank': 'True', 'to': "orm['askbot.Thread']"}),
+ 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.postflagreason': {
+ 'Meta': {'object_name': 'PostFlagReason'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'details': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_reject_reasons'", 'to': "orm['askbot.Post']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'askbot.postrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'},
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'approved_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'approved_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}),
+ 'by_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'revisions'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'revised_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'revision_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '125', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'to': "orm['askbot.Post']"}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.thread': {
+ 'Meta': {'object_name': 'Thread'},
+ 'accepted_answer': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'answer_accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'unused_favorite_threads'", 'symmetrical': 'False', 'through': "orm['askbot.FavoriteQuestion']", 'to': "orm['auth.User']"}),
+ 'favourite_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'followed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followed_threads'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'unused_last_active_in_threads'", 'to': "orm['auth.User']"}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}),
+ 'vote': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index 12e2c97b..6513d918 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -99,17 +99,18 @@ User.add_to_class('about', models.TextField(blank=True))
#interesting tags and ignored tags are to store wildcard tag selections only
User.add_to_class('interesting_tags', models.TextField(blank = True))
User.add_to_class('ignored_tags', models.TextField(blank = True))
+User.add_to_class('subscribed_tags', models.TextField(blank = True))
User.add_to_class(
'email_tag_filter_strategy',
models.SmallIntegerField(
- choices=const.TAG_FILTER_STRATEGY_CHOICES,
+ choices=const.TAG_DISPLAY_FILTER_STRATEGY_CHOICES,
default=const.EXCLUDE_IGNORED
)
)
User.add_to_class(
'display_tag_filter_strategy',
models.SmallIntegerField(
- choices=const.TAG_FILTER_STRATEGY_CHOICES,
+ choices=const.TAG_EMAIL_FILTER_STRATEGY_CHOICES,
default=const.INCLUDE_ALL
)
)
@@ -210,8 +211,12 @@ def user_has_affinity_to_question(self, question = None, affinity_type = None):
affinity_type can be either "like" or "dislike"
"""
if affinity_type == 'like':
- tag_selection_type = 'good'
- wildcards = self.interesting_tags.split()
+ if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED:
+ tag_selection_type = 'subscribed'
+ wildcards = self.subscribed_tags.split()
+ else:
+ tag_selection_type = 'good'
+ wildcards = self.interesting_tags.split()
elif affinity_type == 'dislike':
tag_selection_type = 'bad'
wildcards = self.ignored_tags.split()
@@ -1087,7 +1092,10 @@ def user_mark_tags(
cleaned_wildcards = list()
assert(action in ('add', 'remove'))
if action == 'add':
- assert(reason in ('good', 'bad'))
+ if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED:
+ assert(reason in ('good', 'bad', 'subscribed'))
+ else:
+ assert(reason in ('good', 'bad'))
if wildcards:
cleaned_wildcards = self.update_wildcard_tag_selections(
action = action,
@@ -1102,6 +1110,13 @@ def user_mark_tags(
user = self,
tag__name__in = tagnames
)
+ #Marks for "good" and "bad" reasons are exclusive,
+ #to make it impossible to "like" and "dislike" something at the same time
+ #but the subscribed set is independent - e.g. you can dislike a topic
+ #and still subscribe for it.
+ if reason == 'subscribed':#don't touch good/bad marks
+ marked_ts = marked_ts.filter(reason = 'subscribed')
+
#todo: use the user api methods here instead of the straight ORM
cleaned_tagnames = list() #those that were actually updated
if action == 'remove':
@@ -1123,7 +1138,8 @@ def user_mark_tags(
cleaned_tagnames.extend(marked_names)
cleaned_tagnames.extend(new_marks)
else:
- marked_ts.update(reason=reason)
+ if reason in ('good', 'bad'):#to maintain exclusivity of 'good' and 'bad'
+ marked_ts.update(reason=reason)
cleaned_tagnames = tagnames
return cleaned_tagnames, cleaned_wildcards
@@ -1945,20 +1961,26 @@ def user_get_tag_filtered_questions(self, questions = None):
thread__tags__in = ignored_tags
).exclude(
thread__tags__in = ignored_by_wildcards
- )
+ ).distinct()
elif self.email_tag_filter_strategy == const.INCLUDE_INTERESTING:
+ if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED:
+ reason = 'subscribed'
+ wk = self.subscribed_tags.strip().split()
+ else:
+ reason = 'good'
+ wk = self.interesting_tags.strip().split()
+
selected_tags = Tag.objects.filter(
- user_selections__reason = 'good',
+ user_selections__reason = reason,
user_selections__user = self
)
- wk = self.interesting_tags.strip().split()
selected_by_wildcards = Tag.objects.get_by_wildcards(wk)
tag_filter = models.Q(thread__tags__in = list(selected_tags)) \
| models.Q(thread__tags__in = list(selected_by_wildcards))
- return questions.filter( tag_filter )
+ return questions.filter( tag_filter ).distinct()
else:
return questions
@@ -2293,29 +2315,40 @@ def user_update_wildcard_tag_selections(
"""updates the user selection of wildcard tags
and saves the user object to the database
"""
+ if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED:
+ assert reason in ('good', 'bad', 'subscribed')
+ else:
+ assert reason in ('good', 'bad')
+
new_tags = set(wildcards)
interesting = set(self.interesting_tags.split())
ignored = set(self.ignored_tags.split())
+ subscribed = set(self.subscribed_tags.split())
- target_set = interesting
- other_set = ignored
if reason == 'good':
- pass
+ target_set = interesting
+ other_set = ignored
elif reason == 'bad':
target_set = ignored
other_set = interesting
+ elif reason == 'subscribed':
+ target_set = subscribed
+ other_set = None
else:
assert(action == 'remove')
if action == 'add':
target_set.update(new_tags)
- other_set.difference_update(new_tags)
+ if reason in ('good', 'bad'):
+ other_set.difference_update(new_tags)
else:
target_set.difference_update(new_tags)
- other_set.difference_update(new_tags)
+ if reason in ('good', 'bad'):
+ other_set.difference_update(new_tags)
self.interesting_tags = ' '.join(interesting)
self.ignored_tags = ' '.join(ignored)
+ self.subscribed_tags = ' '.join(subscribed)
self.save()
return new_tags
@@ -2938,10 +2971,14 @@ def complete_pending_tag_subscriptions(sender, request, *args, **kwargs):
"""save pending tag subscriptions saved in the session"""
if 'subscribe_for_tags' in request.session:
(pure_tag_names, wildcards) = request.session.pop('subscribe_for_tags')
+ if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED:
+ reason = 'subscribed'
+ else:
+ reason = 'good'
request.user.mark_tags(
pure_tag_names,
wildcards,
- reason = 'good',
+ reason = reason,
action = 'add'
)
request.user.message_set.create(
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 4e590d94..597a95ae 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -226,8 +226,19 @@ class ThreadManager(models.Manager):
if request_user and request_user.is_authenticated():
#mark questions tagged with interesting tags
#a kind of fancy annotation, would be nice to avoid it
- interesting_tags = Tag.objects.filter(user_selections__user=request_user, user_selections__reason='good')
- ignored_tags = Tag.objects.filter(user_selections__user=request_user, user_selections__reason='bad')
+ interesting_tags = Tag.objects.filter(
+ user_selections__user = request_user,
+ user_selections__reason = 'good'
+ )
+ ignored_tags = Tag.objects.filter(
+ user_selections__user = request_user,
+ user_selections__reason = 'bad'
+ )
+ if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED:
+ meta_data['subscribed_tag_names'] = Tag.objects.filter(
+ user_selections__user = request_user,
+ user_selections__reason = 'subscribed'
+ ).values_list('name', flat = True)
meta_data['interesting_tag_names'] = [tag.name for tag in interesting_tags]
meta_data['ignored_tag_names'] = [tag.name for tag in ignored_tags]
diff --git a/askbot/models/tag.py b/askbot/models/tag.py
index 06171067..779c0b68 100644
--- a/askbot/models/tag.py
+++ b/askbot/models/tag.py
@@ -157,7 +157,11 @@ class Tag(models.Model):
return self.name
class MarkedTag(models.Model):
- TAG_MARK_REASONS = (('good', _('interesting')), ('bad', _('ignored')))
+ TAG_MARK_REASONS = (
+ ('good', _('interesting')),
+ ('bad', _('ignored')),
+ ('subscribed', _('subscribed')),
+ )
tag = models.ForeignKey('Tag', related_name='user_selections')
user = models.ForeignKey(User, related_name='tag_selections')
reason = models.CharField(max_length=16, choices=TAG_MARK_REASONS)
diff --git a/askbot/skins/common/media/js/tag_selector.js b/askbot/skins/common/media/js/tag_selector.js
index 445a1e44..274ac7de 100644
--- a/askbot/skins/common/media/js/tag_selector.js
+++ b/askbot/skins/common/media/js/tag_selector.js
@@ -1,4 +1,3 @@
-
var TagDetailBox = function(box_type){
WrappedElement.call(this);
this.box_type = box_type;
@@ -101,17 +100,20 @@ function pickedTags(){
var interestingTags = {};
var ignoredTags = {};
+ var subscribedTags {};
var interestingTagDetailBox = new TagDetailBox('interesting');
var ignoredTagDetailBox = new TagDetailBox('ignored');
+ var subscribedTagDetailBox = new TagDetailBox('subscribed');
var sendAjax = function(tagnames, reason, action, callback){
var url = '';
- if (action == 'add'){
- if (reason == 'good'){
+ if (action == 'add') {
+ if (reason == 'good') {
url = askbot['urls']['mark_interesting_tag'];
- }
- else {
+ } else if (reason == 'bad') {
url = askbot['urls']['mark_ignored_tag'];
+ } else {
+ url = askbot['urls']['mark_subscribed_tag'];
}
}
else {
@@ -154,19 +156,23 @@ function pickedTags(){
var getTagList = function(reason){
var base_selector = '.marked-tags';
- if (reason === 'good'){
+ if (reason === 'good') {
var extra_selector = '.interesting';
- } else {
+ } else if (reason === 'bad') {
var extra_selector = '.ignored';
+ } else if (reason === 'subscribed') {
+ var extra_selector = '.subscribed';
}
return $(base_selector + extra_selector);
};
var getWildcardTagDetailBox = function(reason){
- if (reason === 'good'){
+ if (reason === 'good') {
return interestingTagDetailBox;
- } else {
+ } else if (reason === 'bad') {
return ignoredTagDetailBox;
+ } else if (reason === 'subscribed') {
+ return subscribedTagDetailBox;
}
};
@@ -230,27 +236,29 @@ function pickedTags(){
var to_target = interestingTags;
var from_target = ignoredTags;
var to_tag_container;
- if (reason == 'bad'){
+ if (reason === 'bad') {
var input_sel = '#ignoredTagInput';
to_target = ignoredTags;
from_target = interestingTags;
to_tag_container = $('div .tags.ignored');
- }
- else if (reason == 'good'){
+ } else if (reason === 'good') {
var input_sel = '#interestingTagInput';
to_tag_container = $('div .tags.interesting');
- }
- else {
+ } else if (reason === 'subscribed') {
+ var input_sel = $('div .tags.subscribed');
+ } else {
return;
}
var tagnames = getUniqueWords($(input_sel).attr('value'));
- $.each(tagnames, function(idx, tagname){
- if (tagname in from_target){
- unpickTag(from_target,tagname,reason,false);
- }
- });
+ if (reason !== 'subscribed') {//for "subscribed" we do not remove
+ $.each(tagnames, function(idx, tagname){
+ if (tagname in from_target){
+ unpickTag(from_target,tagname,reason,false);
+ }
+ });
+ }
var clean_tagnames = [];
$.each(tagnames, function(idx, tagname){
@@ -281,15 +289,16 @@ function pickedTags(){
};
var collectPickedTags = function(section){
- if (section === 'interesting'){
+ if (section === 'interesting') {
var reason = 'good';
var tag_store = interestingTags;
- }
- else if (section === 'ignored'){
+ } else if (section === 'ignored') {
var reason = 'bad';
var tag_store = ignoredTags;
- }
- else {
+ } else if (section === 'subscribed') {
+ var reason = 'subscribed';
+ var tag_store = subscribedTags;
+ } else {
return;
}
$('.' + section + '.tags.marked-tags .tag-left').each(
@@ -344,7 +353,9 @@ function pickedTags(){
init: function(){
collectPickedTags('interesting');
collectPickedTags('ignored');
+ collectPickedTags('subscribed');
setupTagFilterControl('display');
+ setupTagFilterControl('email');
var ac = new AutoCompleter({
url: askbot['urls']['get_tag_list'],
preloadData: true,
@@ -364,8 +375,13 @@ function pickedTags(){
ignoredTagAc.decorate($('#ignoredTagInput'));
ignoredTagAc.setOption('onItemSelect', getResultCallback('bad'));
+ var subscribedTagAc = $.extend(true, {}, ac);
+ subscribedTagAc.decorate($('#subscribedTagInput'));
+ subscribedTagAc.setOption('onItemSelect', getResultCallback('subscribed'));
+
$("#interestingTagAdd").click(getResultCallback('good'));
$("#ignoredTagAdd").click(getResultCallback('bad'));
+ $("#subscribedTagAdd").click(getResultCallback('subscribed'));
}
};
}
diff --git a/askbot/skins/common/templates/widgets/tag_selector.html b/askbot/skins/common/templates/widgets/tag_selector.html
index ff298488..8d955f25 100644
--- a/askbot/skins/common/templates/widgets/tag_selector.html
+++ b/askbot/skins/common/templates/widgets/tag_selector.html
@@ -35,14 +35,45 @@
<input id="ignoredTagInput" autocomplete="off" type="text"/>&nbsp;
<input id="ignoredTagAdd" type="submit" value="{% trans %}add{% endtrans%}"/>
</div>
+ {% if settings.SUBSCRIBED_TAG_SELECTOR_ENABLED %}
+ <h2>{% trans %}Subscribed tags{% endtrans %}</h2>
+ {{
+ macros.tag_list_widget(
+ subscribed_tag_names,
+ deletable = True,
+ css_class = 'ignored marked-tags',
+ search_state = search_state
+ )
+ }}
+ {# todo: add this via javascript
+ "remove '%(tag_name)s' from the list of ignored tags"|
+ format(tag_name = tag_name)
+ #}
+ <div class="inputs">
+ <input id="subscribedTagInput" autocomplete="off" type="text"/>&nbsp;
+ <input id="subscribedTagAdd" type="submit" value="{% trans %}add{% endtrans%}"/>
+ </div>
+ {% endif %}
<h3>{% trans %}Display tag filter{% endtrans%}</h3>
<div id="displayTagFilterControl">
{{
macros.radio_select(
name = "display_tag_filter_strategy",
value = request.user.display_tag_filter_strategy,
- choices = tag_filter_strategy_choices
+ choices = display_tag_filter_strategy_choices
)
}}
</div>
+ {% if settings.SUBSCRIBED_TAG_SELECTOR_ENABLED %}
+ <h3>{% trans %}Email tag filter{% endtrans%}</h3>
+ <div id="emailTagFilterControl">
+ {{
+ macros.radio_select(
+ name = "email_tag_filter_strategy",
+ value = request.user.email_tag_filter_strategy,
+ choices = email_tag_filter_strategy_choices
+ )
+ }}
+ </div>
+ {% endif %}
</div>
diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less
index 27424871..00c429dc 100644
--- a/askbot/skins/default/media/style/style.less
+++ b/askbot/skins/default/media/style/style.less
@@ -559,7 +559,8 @@ body.anon {
margin-right:18px;
}
- #displayTagFilterControl label { /*Especial width just for the display tag filter box in index page*/
+ #displayTagFilterControl label,
+ #emailTagFilterControl label { /*Especial width just for the tag filter boxes in index page*/
width:160px;
}
@@ -587,13 +588,13 @@ body.anon {
}
.inputs{
- #interestingTagInput, #ignoredTagInput{
+ #interestingTagInput, #ignoredTagInput, #subscribedTagInput{
width:153px;
padding-left:5px;
border:#c9c9b5 1px solid;
height:25px;
}
- #interestingTagAdd, #ignoredTagAdd{
+ #interestingTagAdd, #ignoredTagAdd, #subscribedTagAdd {
background:url(../images/small-button-blue.png) repeat-x top;
border:0;
color:@button-label;
@@ -609,7 +610,7 @@ body.anon {
}
- #interestingTagAdd:hover, #ignoredTagAdd:hover{
+ #interestingTagAdd:hover, #ignoredTagAdd:hover, #subscribedTag:hover {
background:url(../images/small-button-blue.png) repeat-x bottom;
}
}
diff --git a/askbot/skins/default/templates/main_page/javascript.html b/askbot/skins/default/templates/main_page/javascript.html
index 6a90c758..d968dcd5 100644
--- a/askbot/skins/default/templates/main_page/javascript.html
+++ b/askbot/skins/default/templates/main_page/javascript.html
@@ -17,6 +17,7 @@
askbot['urls']['mark_interesting_tag'] = '{% url mark_interesting_tag %}';
askbot['urls']['mark_ignored_tag'] = '{% url mark_ignored_tag %}';
+ askbot['urls']['mark_subscribed_tag'] = '{% url mark_subscribed_tag %}';
askbot['urls']['unmark_tag'] = '{% url unmark_tag %}';
askbot['urls']['set_tag_filter_strategy'] = '{% url "set_tag_filter_strategy" %}';
askbot['urls']['questions'] = '{% url "questions" %}';
diff --git a/askbot/urls.py b/askbot/urls.py
index fa29e64e..f4768412 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -159,6 +159,7 @@ urlpatterns = patterns('',
views.readers.tags,
name='tags'
),
+ #todo: collapse these three urls and use an extra json data var
url(#ajax only
r'^%s%s$' % ('mark-tag/', 'interesting/'),
views.commands.mark_tag,
@@ -172,6 +173,12 @@ urlpatterns = patterns('',
name='mark_ignored_tag'
),
url(#ajax only
+ r'^%s%s$' % ('mark-tag/', 'subscribed/'),
+ views.commands.mark_tag,
+ kwargs={'reason':'subscribed','action':'add'},
+ name='mark_subscribed_tag'
+ ),
+ url(#ajax only
r'^unmark-tag/',
views.commands.mark_tag,
kwargs={'action':'remove'},
diff --git a/askbot/views/commands.py b/askbot/views/commands.py
index 62db014f..3d99063b 100644
--- a/askbot/views/commands.py
+++ b/askbot/views/commands.py
@@ -590,7 +590,7 @@ def set_tag_filter_strategy(request):
filter_type = request.POST['filter_type']
filter_value = int(request.POST['filter_value'])
assert(filter_type == 'display')
- assert(filter_value in dict(const.TAG_FILTER_STRATEGY_CHOICES))
+ assert(filter_value in dict(const.TAG_DISPLAY_FILTER_STRATEGY_CHOICES))
request.user.display_tag_filter_strategy = filter_value
request.user.save()
return HttpResponse('', mimetype = "application/json")
diff --git a/askbot/views/readers.py b/askbot/views/readers.py
index 9bb6495d..613cb3c9 100644
--- a/askbot/views/readers.py
+++ b/askbot/views/readers.py
@@ -182,8 +182,9 @@ def questions(request, **kwargs):
'contributors' : contributors,
'context' : paginator_context,
'is_unanswered' : False,#remove this from template
- 'interesting_tag_names': meta_data.get('interesting_tag_names',None),
- 'ignored_tag_names': meta_data.get('ignored_tag_names',None),
+ 'interesting_tag_names': meta_data.get('interesting_tag_names', None),
+ 'ignored_tag_names': meta_data.get('ignored_tag_names', None),
+ 'subscribed_tag_names': meta_data.get('subscribed_tag_names', None),
'language_code': translation.get_language(),
'name_of_anonymous_user' : models.get_name_of_anonymous_user(),
'page_class': 'main-page',
@@ -200,7 +201,8 @@ def questions(request, **kwargs):
'tags' : related_tags,
'tag_list_type' : tag_list_type,
'font_size' : extra_tags.get_tag_font_size(related_tags),
- 'tag_filter_strategy_choices': const.TAG_FILTER_STRATEGY_CHOICES,
+ 'display_tag_filter_strategy_choices': const.TAG_DISPLAY_FILTER_STRATEGY_CHOICES,
+ 'email_tag_filter_strategy_choices': const.TAG_EMAIL_FILTER_STRATEGY_CHOICES,
'update_avatar_data': schedules.should_update_avatar_data(request),
'query_string': search_state.query_string(),
'search_state': search_state,