From f7d0836fef60e876be23cb15747865aadfe27379 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Mon, 7 May 2012 16:31:15 -0400 Subject: added a possibility to have independent set of "subscribed" tags in addition to "favorite/ignored" --- askbot/conf/forum_data_rules.py | 14 + askbot/const/__init__.py | 13 +- askbot/forms.py | 2 +- askbot/management/commands/get_tag_stats.py | 50 +++- .../0122_auth_user__add_subscribed_tag_field.py | 320 +++++++++++++++++++++ askbot/models/__init__.py | 69 +++-- askbot/models/question.py | 15 +- askbot/models/tag.py | 6 +- askbot/skins/common/media/js/tag_selector.js | 64 +++-- .../common/templates/widgets/tag_selector.html | 33 ++- askbot/skins/default/media/style/style.less | 9 +- .../default/templates/main_page/javascript.html | 1 + askbot/urls.py | 7 + askbot/views/commands.py | 2 +- askbot/views/readers.py | 8 +- 15 files changed, 546 insertions(+), 67 deletions(-) create mode 100644 askbot/migrations/0122_auth_user__add_subscribed_tag_field.py 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 @@ -186,6 +186,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, 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 @@   + {% if settings.SUBSCRIBED_TAG_SELECTOR_ENABLED %} +

{% trans %}Subscribed tags{% endtrans %}

+ {{ + 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) + #} +
+   + +
+ {% endif %}

{% trans %}Display tag filter{% endtrans%}

{{ 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 ) }}
+ {% if settings.SUBSCRIBED_TAG_SELECTOR_ENABLED %} +

{% trans %}Email tag filter{% endtrans%}

+
+ {{ + macros.radio_select( + name = "email_tag_filter_strategy", + value = request.user.email_tag_filter_strategy, + choices = email_tag_filter_strategy_choices + ) + }} +
+ {% endif %} 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, @@ -171,6 +172,12 @@ urlpatterns = patterns('', kwargs={'reason':'bad','action':'add'}, 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, 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, -- cgit v1.2.3-1-g7c22