summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-07-05 02:47:11 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-07-05 02:47:11 -0400
commit66a327ce3c03c1704e8bbe53b42731f7ed2c780c (patch)
treef7a5d308c1d520ab852fff4eaa82f907abf54f2b
parent3ed4d6c37552cc97464444a65074f3b8ddf7d9f5 (diff)
downloadaskbot-66a327ce3c03c1704e8bbe53b42731f7ed2c780c.tar.gz
askbot-66a327ce3c03c1704e8bbe53b42731f7ed2c780c.tar.bz2
askbot-66a327ce3c03c1704e8bbe53b42731f7ed2c780c.zip
created a page for tag moderation, at this point it is only listing the suggested tags
-rw-r--r--askbot/migrations/0130_auto__add_suggestedtag.py357
-rw-r--r--askbot/models/__init__.py57
-rw-r--r--askbot/models/post.py4
-rw-r--r--askbot/models/question.py26
-rw-r--r--askbot/models/tag.py65
-rw-r--r--askbot/skins/default/media/style/style.less21
-rw-r--r--askbot/skins/default/templates/moderate_tags.html39
-rw-r--r--askbot/skins/default/templates/tags.html25
-rw-r--r--askbot/skins/default/templates/tags/header.html34
-rw-r--r--askbot/urls.py5
-rw-r--r--askbot/views/meta.py48
-rw-r--r--askbot/views/readers.py31
12 files changed, 636 insertions, 76 deletions
diff --git a/askbot/migrations/0130_auto__add_suggestedtag.py b/askbot/migrations/0130_auto__add_suggestedtag.py
new file mode 100644
index 00000000..d769c76d
--- /dev/null
+++ b/askbot/migrations/0130_auto__add_suggestedtag.py
@@ -0,0 +1,357 @@
+# -*- 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):
+ # Adding model 'SuggestedTag'
+ db.create_table('askbot_suggestedtag', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
+ ('used_count', self.gf('django.db.models.fields.PositiveIntegerField')(default=1)),
+ ('thread_ids', self.gf('django.db.models.fields.TextField')()),
+ ))
+ db.send_create_signal('askbot', ['SuggestedTag'])
+
+ # Adding M2M table for field threads on 'SuggestedTag'
+ db.create_table('askbot_suggestedtag_threads', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('suggestedtag', models.ForeignKey(orm['askbot.suggestedtag'], null=False)),
+ ('thread', models.ForeignKey(orm['askbot.thread'], null=False))
+ ))
+ db.create_unique('askbot_suggestedtag_threads', ['suggestedtag_id', 'thread_id'])
+
+ # Adding M2M table for field users on 'SuggestedTag'
+ db.create_table('askbot_suggestedtag_users', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('suggestedtag', models.ForeignKey(orm['askbot.suggestedtag'], null=False)),
+ ('user', models.ForeignKey(orm['auth.user'], null=False))
+ ))
+ db.create_unique('askbot_suggestedtag_users', ['suggestedtag_id', 'user_id'])
+
+ def backwards(self, orm):
+ # Deleting model 'SuggestedTag'
+ db.delete_table('askbot_suggestedtag')
+
+ # Removing M2M table for field threads on 'SuggestedTag'
+ db.delete_table('askbot_suggestedtag_threads')
+
+ # Removing M2M table for field users on 'SuggestedTag'
+ db.delete_table('askbot_suggestedtag_users')
+
+ 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', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': '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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ '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', [], {}),
+ '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'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ '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.suggestedtag': {
+ 'Meta': {'ordering': "['-used_count', 'name']", 'object_name': 'SuggestedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'thread_ids': ('django.db.models.fields.TextField', [], {}),
+ 'threads': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Thread']", 'symmetrical': 'False'}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+ },
+ '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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': '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'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ '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'] \ No newline at end of file
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index 527c45bc..046f19a1 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -29,7 +29,8 @@ from askbot.skins import utils as skin_utils
from askbot.mail import messages
from askbot.models.question import QuestionView, AnonymousQuestion
from askbot.models.question import FavoriteQuestion
-from askbot.models.tag import Tag, MarkedTag, get_group_names, get_groups
+from askbot.models.tag import Tag, MarkedTag, SuggestedTag
+from askbot.models.tag import get_group_names, get_groups
from askbot.models.user import EmailFeedSetting, ActivityAuditStatus, Activity
from askbot.models.user import GroupMembership, GroupProfile
from askbot.models.post import Post, PostRevision, PostFlagReason, AnonymousAnswer
@@ -343,43 +344,54 @@ def user_can_create_tags(self):
else:
return True
-def user_try_creating_tags(self,tag_names):
+def user_try_creating_tags(self, tag_names = None, thread = None):
created_tags = list()
if self.can_create_tags():
+ suggested_tags = SuggestedTag.objects.filter(name__in = tag_names)
+ suggested_tags_dict = dict([(tag.name, tag) for tag in suggested_tags])
for name in tag_names:
+ #todo: keep better track of who creates the tag
+ suggested_tag = suggested_tags_dict.get(name, None)
+ if suggested_tag:
+ creator = suggested_tag.users.all()[0]
+ else:
+ creator = self
+
new_tag = Tag.objects.create(
name = name,
- created_by = self,
- used_count = 1
+ created_by = creator,
+ used_count = 1#wrong, but we are recounting downstream
)
created_tags.append(new_tag)
- #finally add tags to the relation and extend the modified list
+ #remove added tags from the suggested tag list
+ suggested_tags.delete()
elif tag_names:#notify admins by email about new tags
#maybe remove tags to report based on categories
#todo: maybe move this to tags_updated signal handler
if askbot_settings.TAG_SOURCE == 'category-tree':
category_names = category_tree.get_leaf_names()
+ #remove category tree tags from creation list
tag_names = tag_names - category_names
+ #here we put tags on the moderation queue
if len(tag_names) > 0:
- #todo: move all message sending codes to a separate module
- body_text = messages.notify_admins_about_new_tags(
- tags = tag_names,
- user = self,
- thread = self
- )
- site_name = askbot_settings.APP_SHORT_NAME
- subject_line = _('New tags added to %s') % site_name
- mail.mail_moderators(
- subject_line,
- body_text,
- headers = {'Reply-To': self.email}
+ suggested_tags = SuggestedTag.objects.filter(name__in = tag_names)
+
+ previously_suggested_tag_names = set()
+ for tag in suggested_tags:
+ tag.used_count += 1
+ tag.threads.add(thread)
+ tag.users.add(self)
+ tag.save()
+ previously_suggested_tag_names.add(tag.name)
+
+ tag_names = set(tag_names) - previously_suggested_tag_names
+
+ SuggestedTag.objects.create(
+ tag_names = tag_names,
+ user = self,
+ thread = thread
)
- msg = _(
- 'Tags %s are new and will be submitted for the '
- 'moderators approval'
- ) % ', '.join(tag_names)
- self.message_set.create(message = msg)
return created_tags
@@ -3412,6 +3424,7 @@ __all__ = [
'Vote',
'PostFlagReason',
'MarkedTag',
+ 'SuggestedTag',
'BadgeData',
'Award',
diff --git a/askbot/models/post.py b/askbot/models/post.py
index e5a1867b..21723128 100644
--- a/askbot/models/post.py
+++ b/askbot/models/post.py
@@ -1508,7 +1508,9 @@ class Post(models.Model):
# Update the Question tag associations
if latest_revision.tagnames != tags:
- self.thread.update_tags(tagnames = tags, user = edited_by, timestamp = edited_at)
+ self.thread.update_tags(
+ tagnames = tags, user = edited_by, timestamp = edited_at
+ )
self.thread.title = title
self.thread.tagnames = tags
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 446b4b1f..2a5c557d 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -129,7 +129,7 @@ class ThreadManager(BaseQuerySetManager):
#this is kind of bad, but we save assign privacy groups to posts and thread
question.parse_and_save(author = author, is_private = is_private)
- question.add_revision(
+ revision = question.add_revision(
author = author,
is_anonymous = is_anonymous,
text = text,
@@ -794,7 +794,9 @@ class Thread(models.Model):
return removed_tags
- def update_tags(self, tagnames = None, user = None, timestamp = None):
+ def update_tags(
+ self, tagnames = None, user = None, timestamp = None
+ ):
"""
Updates Tag associations for a thread to match the given
tagname string.
@@ -811,8 +813,10 @@ class Thread(models.Model):
"""
previous_tags = list(self.tags.all())
+ ordered_updated_tagnames = [t for t in tagnames.strip().split(' ')]
+
previous_tagnames = set([tag.name for tag in previous_tags])
- updated_tagnames = set(t for t in tagnames.strip().split(' '))
+ updated_tagnames = set(ordered_updated_tagnames)
removed_tagnames = previous_tagnames - updated_tagnames
#remove tags from the question's tags many2many relation
@@ -834,12 +838,26 @@ class Thread(models.Model):
reused_tags.mark_undeleted()
added_tags = list(reused_tags)
- created_tags = user.try_creating_tags(new_tagnames)
+ #tag moderation is in the call below
+ created_tags = user.try_creating_tags(new_tagnames, thread = self)
added_tags.extend(created_tags)
#todo: not nice that assignment of added_tags is way above
self.tags.add(*added_tags)
modified_tags.extend(added_tags)
+ else:
+ added_tags = Tag.objects.none()
+
+ #Save denormalized tag names on thread. Preserve order from user input.
+ added_tagnames = set([tag.name for tag in added_tags])
+ final_tagnames = (previous_tagnames - removed_tagnames) | added_tagnames
+ ordered_final_tagnames = list()
+ for tagname in ordered_updated_tagnames:
+ if tagname in final_tagnames:
+ ordered_final_tagnames.append(tagname)
+
+ self.tagnames = ' '.join(ordered_final_tagnames)
+ self.save()#need to save here?
####################################################################
self.update_summary_html() # regenerate question/thread summary html
diff --git a/askbot/models/tag.py b/askbot/models/tag.py
index 95223bad..54c632e7 100644
--- a/askbot/models/tag.py
+++ b/askbot/models/tag.py
@@ -4,6 +4,7 @@ from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from askbot.models.base import BaseQuerySetManager
from askbot import const
+from askbot.conf import settings as askbot_settings
def delete_tags(tags):
"""deletes tags in the list"""
@@ -236,3 +237,67 @@ def get_groups():
def get_group_names():
#todo: cache me
return get_groups().values_list('name', flat = True)
+
+class SuggestedTagManager(models.Manager):
+ def create(self, tag_names = None, user = None, thread = None):
+ """creates ``SuggestedTag`` records and saves them
+ in the database"""
+ suggested_tags = list()
+ for tag_name in tag_names:
+ #create new record
+ suggested_tag = SuggestedTag(name = tag_name)
+ suggested_tag.save()
+ #add user and thread
+ suggested_tag.users.add(user)
+ suggested_tag.threads.add(thread)
+ #add to the list that is to be returned
+ suggested_tags.append(suggested_tag)
+
+ #todo: stuff below will probably go after
+ #tag moderation actions are implemented
+ from askbot import mail
+ from askbot.mail import messages
+ body_text = messages.notify_admins_about_new_tags(
+ tags = tag_names,
+ user = user,
+ thread = thread
+ )
+ site_name = askbot_settings.APP_SHORT_NAME
+ subject_line = _('New tags added to %s') % site_name
+ mail.mail_moderators(
+ subject_line,
+ body_text,
+ headers = {'Reply-To': user.email}
+ )
+
+ msg = _(
+ 'Tags %s are new and will be submitted for the '
+ 'moderators approval'
+ ) % ', '.join(tag_names)
+ user.message_set.create(message = msg)
+
+ return suggested_tags
+
+class SuggestedTag(models.Model):
+ """Suggested tag knows about who suggested it
+ and in what thread"""
+ name = models.CharField(
+ max_length=255, unique=True, help_text = 'Name for the proposed tag'
+ )
+ used_count = models.PositiveIntegerField(default = 1)
+ #todo: instead these can be associated with revisions
+ #but the problem is that there would be too many joins
+ #to pull out threads
+ #if we used revisions, then we would not need to have
+ #a separate "user" field
+ threads = models.ManyToManyField('Thread')
+ users = models.ManyToManyField(User)
+ thread_ids = models.TextField(
+ help_text = 'comma-separated list of thread ids'
+ )
+
+ objects = SuggestedTagManager()
+
+ class Meta:
+ app_label = 'askbot'
+ ordering = ['-used_count', 'name']
diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less
index 50acb24d..8d6b4afb 100644
--- a/askbot/skins/default/media/style/style.less
+++ b/askbot/skins/default/media/style/style.less
@@ -1219,10 +1219,22 @@ ul#related-tags li {
color: #1A1A1A;
}
-.users-page h1,
-.tags-page h1,
-.groups-page h1 {
- float: left;
+.users-page,
+.tags-page,
+.groups-page,
+.moderate-tags-page {
+ h1 {
+ float: left;
+ padding-top: 7px;
+ }
+}
+
+.moderate-tags-page {
+ th, tr {
+ vertical-align: top;
+ text-align: left;
+ padding-right: 20px;
+ }
}
.main-page h1 {
@@ -2299,7 +2311,6 @@ ul#related-tags li {
/* tags page */
.tabBar-tags{
- width:270px;
margin-bottom:15px;
}
diff --git a/askbot/skins/default/templates/moderate_tags.html b/askbot/skins/default/templates/moderate_tags.html
new file mode 100644
index 00000000..59aa239a
--- /dev/null
+++ b/askbot/skins/default/templates/moderate_tags.html
@@ -0,0 +1,39 @@
+{% extends "two_column_body.html" %}
+{% import "macros.html" as macros %}
+<!-- tags.html -->
+{% block title %}{% spaceless %}{% trans %}Moderated Tags{% endtrans %}{% endspaceless %}{% endblock %}
+{% block content %}
+ {% include "tags/header.html" %}
+ {% if tags %}
+ <table>
+ <thead>
+ <tr>
+ <th>{% trans %}Name{% endtrans %}</th>
+ <th>{% trans %}Used count{% endtrans %}</th>
+ <th>{% trans %}Suggested by{% endtrans %}</th>
+ <th>{% trans %}In questions{% endtrans %}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for tag in tags %}
+ <tr>
+ <td>{{ tag.name }}</td>
+ <td>{{ tag.used_count }}</td>
+ <td>
+ {% for user in tag.users.all() %}
+ <p>{{ user.get_profile_link() }}</p>
+ {% endfor %}
+ </td>
+ <td>
+ {% for thread in tag.threads.all() %}
+ <p><a href="{{ thread.get_absolute_url() }}">{{ thread.title|escape }}</a></p>
+ {% endfor %}
+ </td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ <span>{% trans %}Nothing found{% endtrans %}</span>
+ {% endif %}
+{% endblock %}
diff --git a/askbot/skins/default/templates/tags.html b/askbot/skins/default/templates/tags.html
index 007388af..e9049e8e 100644
--- a/askbot/skins/default/templates/tags.html
+++ b/askbot/skins/default/templates/tags.html
@@ -4,30 +4,7 @@
{% block title %}{% spaceless %}{% trans %}Tags{% endtrans %}{% endspaceless %}{% endblock %}
{% block content %}
<!-- Tabs -->
-<div id="content-header">
- {% if stag %}
- <h1 class="section-title">{% trans %}Tags, matching "{{ stag }}"{% endtrans %}</h1>
- {% else %}
- <h1 class="section-title">{% trans %}Tag list{% endtrans %}</h1>
- {% endif %}
- <div class="tabBar tabBar-tags">
- <div class="tabsA">
- <span class="label">{% trans %}Sort by &raquo;{% endtrans %}</span>
- <a
- id="sort_name"
- href="{% url tags %}?sort=name"
- {% if tab_id == 'name' %}class="on"{% endif %}
- title="{% trans %}sorted alphabetically{% endtrans %}"
- ><span>{% trans %}by name{% endtrans %}</span></a>
- <a
- id="sort_used"
- href="{% url tags %}?sort=used"
- {% if tab_id == 'used' %}class="on"{% endif %}
- title="{% trans %}sorted by frequency of tag use{% endtrans %}"
- ><span>{% trans %}by popularity{% endtrans %}</span></a>
- </div>
- </div>
-</div>
+{% include "tags/header.html" %}
{% if tag_list_type == 'list' %}
{% if not tags.object_list %}
<span>{% trans %}Nothing found{% endtrans %}</span>
diff --git a/askbot/skins/default/templates/tags/header.html b/askbot/skins/default/templates/tags/header.html
new file mode 100644
index 00000000..4f614f30
--- /dev/null
+++ b/askbot/skins/default/templates/tags/header.html
@@ -0,0 +1,34 @@
+<div id="content-header">
+ {% if stag %}
+ <h1 class="section-title">{% trans %}Tags, matching "{{ stag }}"{% endtrans %}</h1>
+ {% else %}
+ <h1 class="section-title">{% trans %}Tags{% endtrans %}</h1>
+ {% endif %}
+ <div class="tabBar tabBar-tags">
+ <div class="tabsA">
+ <span class="label">{% trans %}Sort by &raquo;{% endtrans %}</span>
+ <a
+ id="sort_name"
+ href="{% url tags %}?sort=name"
+ {% if tab_id == 'name' %}class="on"{% endif %}
+ title="{% trans %}sorted alphabetically{% endtrans %}"
+ ><span>{% trans %}by name{% endtrans %}</span></a>
+ <a
+ id="sort_used"
+ href="{% url tags %}?sort=used"
+ {% if tab_id == 'used' %}class="on"{% endif %}
+ title="{% trans %}sorted by frequency of tag use{% endtrans %}"
+ ><span>{% trans %}by popularity{% endtrans %}</span></a>
+ {% if settings.ENABLE_TAG_MODERATION %}
+ {% if request.user.is_authenticated() and request.user.is_administrator_or_moderator() %}
+ <a
+ href="{% url moderate_tags %}"
+ {% if tab_id == 'used' %}class="on"{% endif %}
+ title="{% trans %}suggested{% endtrans %}"
+ ><span>{% trans %}moderated{% endtrans %}</span></a>
+ {% endif %}
+ {% endif %}
+ </div>
+ </div>
+</div>
+<div class="clearfix"></div>
diff --git a/askbot/urls.py b/askbot/urls.py
index 99b94860..7ea69579 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -159,6 +159,11 @@ urlpatterns = patterns('',
views.readers.tags,
name='tags'
),
+ url(
+ r'^%s$' % _('moderate-tags/'),
+ views.meta.moderate_tags,
+ name = 'moderate_tags'
+ ),
#todo: collapse these three urls and use an extra json data var
url(#ajax only
r'^%s%s$' % ('mark-tag/', 'interesting/'),
diff --git a/askbot/views/meta.py b/askbot/views/meta.py
index b8411b41..f9cbe602 100644
--- a/askbot/views/meta.py
+++ b/askbot/views/meta.py
@@ -5,6 +5,7 @@ This module contains a collection of views displaying all sorts of secondary and
"""
from django.shortcuts import render_to_response, get_object_or_404
from django.core.urlresolvers import reverse
+from django.core.paginator import Paginator, EmptyPage, InvalidPage
from django.template import RequestContext, Template
from django.http import HttpResponseRedirect, HttpResponse, Http404
from django.core.urlresolvers import reverse
@@ -12,14 +13,16 @@ from django.utils.translation import ugettext as _
from django.views import static
from django.views.decorators import csrf
from django.db.models import Max, Count
+from askbot import skins
+from askbot.conf import settings as askbot_settings
from askbot.forms import FeedbackForm
-from askbot.utils.forms import get_next_url
from askbot.mail import mail_moderators
-from askbot.models import BadgeData, Award, User
+from askbot.models import BadgeData, Award, User, SuggestedTag
from askbot.models import badges as badge_data
from askbot.skins.loaders import get_template, render_into_skin, render_text_into_skin
-from askbot.conf import settings as askbot_settings
-from askbot import skins
+from askbot.utils.decorators import admins_only
+from askbot.utils.forms import get_next_url
+from askbot.utils import functions
def generic_view(request, template = None, page_class = None):
"""this may be not necessary, since it is just a rewrite of render_into_skin"""
@@ -153,3 +156,40 @@ def badge(request, id):
'page_class': 'meta',
}
return render_into_skin('badge.html', data, request)
+
+@admins_only
+def moderate_tags(request):
+ """moderators and administrators can list tags that are
+ in the moderation queue, apply suggested tag to questions
+ or cancel the moderation reuest."""
+ if askbot_settings.ENABLE_TAG_MODERATION == False:
+ raise Http404
+ tags = SuggestedTag.objects.all()
+ #paginate moderated tags
+ paginator = Paginator(SuggestedTag.objects.all(), 20)
+
+ page_no = request.GET.get('page', '1')
+
+ try:
+ page = paginator.page(page_no)
+ except (EmptyPage, InvalidPage):
+ page = paginator.page(paginator.num_pages)
+
+ paginator_context = functions.setup_paginator({
+ 'is_paginated' : True,
+ 'pages': paginator.num_pages,
+ 'page': page_no,
+ 'has_previous': page.has_previous(),
+ 'has_next': page.has_next(),
+ 'previous': page.previous_page_number(),
+ 'next': page.next_page_number(),
+ 'base_url' : request.path
+ })
+
+ data = {
+ 'tags': page.object_list,
+ 'active_tab': 'tags',
+ 'page_class': 'moderate-tags-page',
+ 'paginator_context' : paginator_context,
+ }
+ return render_into_skin('moderate_tags.html', data, request)
diff --git a/askbot/views/readers.py b/askbot/views/readers.py
index 1bf37450..b37cacb2 100644
--- a/askbot/views/readers.py
+++ b/askbot/views/readers.py
@@ -244,23 +244,22 @@ def tags(request):#view showing a listing of available tags - plain list
except ValueError:
page = 1
- if request.method == "GET":
- stag = request.GET.get("query", "").strip()
- if stag != '':
- objects_list = Paginator(
- models.Tag.objects.filter(
- deleted=False,
- name__icontains=stag
- ).exclude(
- used_count=0
- ),
- DEFAULT_PAGE_SIZE
- )
+ stag = request.GET.get("query", "").strip()
+ if stag != '':
+ objects_list = Paginator(
+ models.Tag.objects.filter(
+ deleted=False,
+ name__icontains=stag
+ ).exclude(
+ used_count=0
+ ),
+ DEFAULT_PAGE_SIZE
+ )
+ else:
+ if sortby == "name":
+ objects_list = Paginator(models.Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("name"), DEFAULT_PAGE_SIZE)
else:
- if sortby == "name":
- objects_list = Paginator(models.Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("name"), DEFAULT_PAGE_SIZE)
- else:
- objects_list = Paginator(models.Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-used_count"), DEFAULT_PAGE_SIZE)
+ objects_list = Paginator(models.Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-used_count"), DEFAULT_PAGE_SIZE)
try:
tags = objects_list.page(page)