summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-03-19 23:16:52 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-03-19 23:16:52 -0400
commit0880dd09962294723a167dd924bedb78cc7d3660 (patch)
treeb0f63599468bf2d8a4fcedf7c9e9bea4cd457fcb
parent8d0b72bff3b5285ff08928ffb394f74c0da13000 (diff)
downloadaskbot-0880dd09962294723a167dd924bedb78cc7d3660.tar.gz
askbot-0880dd09962294723a167dd924bedb78cc7d3660.tar.bz2
askbot-0880dd09962294723a167dd924bedb78cc7d3660.zip
assigning users to groups about 2/3 way done
-rw-r--r--askbot/conf/__init__.py1
-rw-r--r--askbot/conf/group_settings.py20
-rw-r--r--askbot/forms.py4
-rw-r--r--askbot/migrations/0114_auto__add_groupmembership__add_field_tag_tag_wiki.py303
-rw-r--r--askbot/models/__init__.py8
-rw-r--r--askbot/models/tag.py16
-rw-r--r--askbot/models/user.py13
-rw-r--r--askbot/skins/common/media/js/user.js147
-rw-r--r--askbot/skins/default/templates/macros.html4
-rw-r--r--askbot/skins/default/templates/user_profile/user.html2
-rw-r--r--askbot/skins/default/templates/user_profile/user_stats.html40
-rw-r--r--askbot/tests/form_tests.py4
-rw-r--r--askbot/urls.py5
-rw-r--r--askbot/views/commands.py29
14 files changed, 574 insertions, 22 deletions
diff --git a/askbot/conf/__init__.py b/askbot/conf/__init__.py
index dff91d8e..8c3bd957 100644
--- a/askbot/conf/__init__.py
+++ b/askbot/conf/__init__.py
@@ -17,6 +17,7 @@ import askbot.conf.sidebar_profile
import askbot.conf.leading_sidebar
import askbot.conf.spam_and_moderation
import askbot.conf.user_settings
+import askbot.conf.group_settings
import askbot.conf.markup
import askbot.conf.social_sharing
import askbot.conf.badges
diff --git a/askbot/conf/group_settings.py b/askbot/conf/group_settings.py
new file mode 100644
index 00000000..a48fb55d
--- /dev/null
+++ b/askbot/conf/group_settings.py
@@ -0,0 +1,20 @@
+"""Group settings"""
+from askbot.conf.settings_wrapper import settings
+from askbot.conf.super_groups import LOGIN_USERS_COMMUNICATION
+from askbot.deps import livesettings
+from django.utils.translation import ugettext as _
+
+GROUP_SETTINGS = livesettings.ConfigurationGroup(
+ 'GROUP_SETTINGS',
+ _('Group settings'),
+ super_group = LOGIN_USERS_COMMUNICATION
+ )
+
+settings.register(
+ livesettings.BooleanValue(
+ GROUP_SETTINGS,
+ 'GROUPS_ENABLED',
+ default = False,
+ description = _('Enable user groups'),
+ )
+)
diff --git a/askbot/forms.py b/askbot/forms.py
index 1816c202..ebb1b519 100644
--- a/askbot/forms.py
+++ b/askbot/forms.py
@@ -1135,3 +1135,7 @@ class SimpleEmailSubscribeForm(forms.Form):
else:
email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL)
email_settings_form.save(user, save_unbound=True)
+
+class AddUserToGroupForm(forms.Form):
+ user_id = forms.IntegerField()
+ group_name = forms.CharField()
diff --git a/askbot/migrations/0114_auto__add_groupmembership__add_field_tag_tag_wiki.py b/askbot/migrations/0114_auto__add_groupmembership__add_field_tag_tag_wiki.py
new file mode 100644
index 00000000..c255b7bc
--- /dev/null
+++ b/askbot/migrations/0114_auto__add_groupmembership__add_field_tag_tag_wiki.py
@@ -0,0 +1,303 @@
+# -*- 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 'GroupMembership'
+ db.create_table('askbot_groupmembership', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Tag'])),
+ ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+ ))
+ db.send_create_signal('askbot', ['GroupMembership'])
+
+ # Adding field 'Tag.tag_wiki'
+ db.add_column(u'tag', 'tag_wiki',
+ self.gf('django.db.models.fields.related.OneToOneField')(related_name='described_tag', unique=True, null=True, to=orm['askbot.Post']),
+ keep_default=False)
+
+ def backwards(self, orm):
+ # Deleting model 'GroupMembership'
+ db.delete_table('askbot_groupmembership')
+
+ # Deleting field 'Tag.tag_wiki'
+ db.delete_column(u'tag', 'tag_wiki_id')
+
+ 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': {'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': {'object_name': 'GroupMembership'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ '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'}),
+ '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', [], {'related_name': "'posts'", '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.postrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}),
+ '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'}),
+ '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'}),
+ '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 09ec0018..19c1c559 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -30,6 +30,7 @@ from askbot.models.answer import AnonymousAnswer
from askbot.models.tag import Tag, MarkedTag
from askbot.models.meta import Vote
from askbot.models.user import EmailFeedSetting, ActivityAuditStatus, Activity
+from askbot.models.user import GroupMembership
from askbot.models.post import Post, PostRevision
from askbot.models.reply_by_email import ReplyAddress
from askbot.models import signals
@@ -2173,6 +2174,11 @@ def user_update_wildcard_tag_selections(
return new_tags
+def user_add_user_to_group(self, user = None, group = None):
+ """allows one user to add another to a pre-existing group"""
+ GroupMembership.objects.get_or_create(user, group)
+
+
User.add_to_class(
'add_missing_askbot_subscriptions',
user_add_missing_askbot_subscriptions
@@ -2235,6 +2241,7 @@ User.add_to_class('can_post_comment', user_can_post_comment)
User.add_to_class('is_administrator', user_is_administrator)
User.add_to_class('is_administrator_or_moderator', user_is_administrator_or_moderator)
User.add_to_class('set_admin_status', user_set_admin_status)
+User.add_to_class('add_user_to_group', user_add_user_to_group)
User.add_to_class('remove_admin_status', user_remove_admin_status)
User.add_to_class('is_moderator', user_is_moderator)
User.add_to_class('is_approved', user_is_approved)
@@ -2847,6 +2854,7 @@ __all__ = [
'Activity',
'ActivityAuditStatus',
'EmailFeedSetting',
+ 'GroupMembership',
'User',
diff --git a/askbot/models/tag.py b/askbot/models/tag.py
index a13de661..1cfb1573 100644
--- a/askbot/models/tag.py
+++ b/askbot/models/tag.py
@@ -82,6 +82,16 @@ class TagManager(BaseQuerySetManager):
def get_query_set(self):
return TagQuerySet(self.model)
+ def get_or_create_group_tag(self, group_name = None, user = None):
+ """creates a group tag or finds one, if exists"""
+ #todo: here we might fill out the group profile
+ try:
+ tag = self.get(name = group_name)
+ except self.model.DoesNotExist:
+ tag = self.model(name = group_name, created_by = user)
+ tag.save()
+ return tag
+
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
created_by = models.ForeignKey(User, related_name='created_tags')
@@ -92,6 +102,12 @@ class Tag(models.Model):
deleted_at = models.DateTimeField(null=True, blank=True)
deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_tags')
+ tag_wiki = models.OneToOneField(
+ 'Post',
+ null=True,
+ related_name = 'described_tag'
+ )
+
objects = TagManager()
class Meta:
diff --git a/askbot/models/user.py b/askbot/models/user.py
index 6f27cbf3..c9a4e1e5 100644
--- a/askbot/models/user.py
+++ b/askbot/models/user.py
@@ -9,6 +9,7 @@ from django.utils.translation import ugettext as _
from django.utils.html import strip_tags
from askbot import const
from askbot.utils import functions
+from askbot.models.tag import Tag
class ResponseAndMentionActivityManager(models.Manager):
def get_query_set(self):
@@ -333,3 +334,15 @@ class EmailFeedSetting(models.Model):
class Meta:
app_label = 'askbot'
+
+
+class GroupMembership(models.Model):
+ """an explicit model to link users and the tags
+ that by being recorded with this relation automatically
+ become group tags
+ """
+ group = models.ForeignKey(Tag)
+ user = models.ForeignKey(User)
+
+ class Meta:
+ app_label = 'askbot'
diff --git a/askbot/skins/common/media/js/user.js b/askbot/skins/common/media/js/user.js
index 5d205560..8a8a7f50 100644
--- a/askbot/skins/common/media/js/user.js
+++ b/askbot/skins/common/media/js/user.js
@@ -126,6 +126,22 @@ $(document).ready(function(){
$(re_content).slideToggle();
}
);
+
+ $('.badge-context-toggle').each(function(idx, elem){
+ var context_list = $(elem).parent().next('ul');
+ if (context_list.children().length > 0){
+ $(elem).addClass('active');
+ var toggle_display = function(){
+ if (context_list.css('display') == 'none'){
+ $('.badge-context-list').hide();
+ context_list.show();
+ } else {
+ context_list.hide();
+ }
+ };
+ $(elem).click(toggle_display);
+ }
+ });
});
/**
@@ -204,6 +220,131 @@ FollowUser.prototype.toggleState = function(){
}
};
+GroupsContainer = function(){
+ WrappedElement.call(this);
+};
+inherits(GroupsContainer, WrappedElement);
+
+GroupsContainer.prototype.decorate = function(element){
+ this._element = element;
+};
+
+GroupsContainer.prototype.addGroup = function(group_name){
+ var group = this.makeElement('li');
+ group.html(group_name);
+ this._element.append(group);
+};
+
+GroupAdderWidget = function(){
+ WrappedElement.call(this);
+ this._state = 'display';//display or edit
+};
+inherits(GroupAdderWidget, WrappedElement);
+
+/**
+ * @param {string} state
+ */
+GroupAdderWidget.prototype.setState = function(state){
+ if (state === 'display'){
+ this._element.html(gettext('add group'));
+ this._input.hide();
+ this._button.hide();
+ } else if (state === 'edit'){
+ this._element.html(gettext('cancel'));
+ this._input.show();
+ this._button.show();
+ } else {
+ return;
+ }
+ this._state = state;
+};
+
+GroupAdderWidget.prototype.getValue = function(){
+ return this._input.val();
+};
+
+GroupAdderWidget.prototype.addGroup = function(group){
+ this._groups_container.addGroup(group);
+};
+
+GroupAdderWidget.prototype.getAddGroupHandler = function(){
+ var me = this;
+ return function(){
+ var group_name = me.getValue();
+ var data = {
+ group_name: group_name,
+ user_id: askbot['data']['viewUserId']
+ };
+ $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ data: data,
+ cache: false,
+ url: askbot['urls']['add_user_to_group'],
+ success: function(data){
+ if (data['success'] === true){
+ me.addGroup(group_name);
+ me.setState('display');
+ } else {
+ var message = data['message'];
+ showMessage(me.getElement(), message, 'after');
+ }
+ }
+ });
+ };
+};
+
+GroupAdderWidget.prototype.setGroupsContainer = function(container){
+ this._groups_container = container;
+};
+
+GroupAdderWidget.prototype.toggleState = function(){
+ if (this._state === 'display'){
+ this.setState('edit');
+ } else if (this._state === 'edit'){
+ this.setState('display');
+ }
+};
+
+GroupAdderWidget.prototype.decorate = function(element){
+ this._element = element;
+ var input = this.makeElement('input');
+ this._input = input;
+ var button = this.makeElement('button');
+ button.html(gettext('add'));
+ this._button = button;
+ element.before(input);
+ input.after(button);
+ this.setState('display');
+ setupButtonEventHandlers(button, this.getAddGroupHandler());
+ var me = this;
+ setupButtonEventHandlers(
+ element,
+ function(){ me.toggleState() }
+ );
+};
+
+/**
+ * @constructor
+ * allows editing user groups
+ */
+UserGroupsEditor = function(){
+ WrappedElement.call(this);
+};
+inherits(UserGroupsEditor, WrappedElement);
+
+UserGroupsEditor.prototype.decorate = function(element){
+ this._element = element;
+ var add_link = element.find('#add-group');
+ var adder = new GroupAdderWidget();
+ adder.decorate(add_link);
+
+ var groups_container = new GroupsContainer();
+ groups_container.decorate(element.find('ul'));
+ adder.setGroupsContainer(groups_container);
+ //todo - add group deleters
+};
+
(function(){
var fbtn = $('.follow-toggle');
if (fbtn.length === 1){
@@ -211,5 +352,11 @@ FollowUser.prototype.toggleState = function(){
follow_user.decorate(fbtn);
follow_user.setUserName(askbot['data']['viewUserName']);
}
+ if (askbot['data']['userIsAdminOrMod']){
+ var group_editor = new UserGroupsEditor();
+ group_editor.decorate($('#user-groups'));
+ } else {
+ $('#add-group').remove();
+ }
})();
diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html
index 20e2055c..71db477f 100644
--- a/askbot/skins/default/templates/macros.html
+++ b/askbot/skins/default/templates/macros.html
@@ -174,6 +174,10 @@ poor design of the data or methods on data objects #}
</ul>
{%- endmacro -%}
+{%- macro user_group(group) -%}
+ {{ group.name }}
+{%- endmacro -%}
+
{# todo: remove the extra content argument to make its usage more explicit #}
{%- macro tag_widget(
tag,
diff --git a/askbot/skins/default/templates/user_profile/user.html b/askbot/skins/default/templates/user_profile/user.html
index 789c3c86..a6fe614a 100644
--- a/askbot/skins/default/templates/user_profile/user.html
+++ b/askbot/skins/default/templates/user_profile/user.html
@@ -22,6 +22,8 @@
<script type="text/javascript">
var viewUserID = {{view_user.id}};
askbot['data']['viewUserName'] = '{{ view_user.username }}';
+ askbot['data']['viewUserId'] = {{view_user.id}};
+ askbot['urls']['add_user_to_group'] = '{% url add_user_to_group %}';
</script>
{% if request.user|can_moderate_user(view_user) %}
<script type='text/javascript' src='{{"/js/jquery.form.js"|media}}'></script>
diff --git a/askbot/skins/default/templates/user_profile/user_stats.html b/askbot/skins/default/templates/user_profile/user_stats.html
index 774550d8..f47ba52b 100644
--- a/askbot/skins/default/templates/user_profile/user_stats.html
+++ b/askbot/skins/default/templates/user_profile/user_stats.html
@@ -6,6 +6,24 @@
{% endblock %}
{% block usercontent %}
{% include "user_profile/user_info.html" %}
+ {% if settings.GROUPS_ENABLED %}
+ <div id="user-groups">
+ <h2>{% trans
+ username = view_user.username
+ %}{{username}}'s groups{% endtrans %}
+ </h2>
+ {% if user_groups %}
+ <ul>
+ {% for group in user_groups %}
+ <li>
+ {{ macros.user_group(group) }}
+ </li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ <a id="add-group">{% trans %}add group{% endtrans %}</a>
+ </div>
+ {% endif %}
<a name="questions"></a>
{% spaceless %}
<h2>{% trans counter=question_count %}<span class="count">{{counter}}</span> Question{% pluralize %}<span class="count">{{counter}}</span> Questions{% endtrans %}</h2>
@@ -132,26 +150,4 @@
</table>
</div>
{% endblock %}
-{% block endjs %}
- {{ super() }}
- <script type="text/javascript">
- $(document).ready(function(){
- $('.badge-context-toggle').each(function(idx, elem){
- var context_list = $(elem).parent().next('ul');
- if (context_list.children().length > 0){
- $(elem).addClass('active');
- var toggle_display = function(){
- if (context_list.css('display') == 'none'){
- $('.badge-context-list').hide();{# hide all context lists #}
- context_list.show();
- } else {
- context_list.hide();
- }
- };
- $(elem).click(toggle_display);
- }
- });
- });
- </script>
-{% endblock %}
<!-- end user_stats.html -->
diff --git a/askbot/tests/form_tests.py b/askbot/tests/form_tests.py
index 22f2a77c..654272b3 100644
--- a/askbot/tests/form_tests.py
+++ b/askbot/tests/form_tests.py
@@ -47,9 +47,12 @@ class AskByEmailFormTests(AskbotTestCase):
'subject': '[tag-one] where is titanic?',
'body_text': 'where is titanic?'
}
+
def test_subject_line(self):
"""loops through various forms of the subject line
and makes sure that tags and title are parsed out"""
+ setting_backup = askbot_settings.TAGS_ARE_REQUIRED
+ askbot_settings.update('TAGS_ARE_REQUIRED', True)
for test_case in SUBJECT_LINE_CASES:
self.data['subject'] = test_case[0]
form = forms.AskByEmailForm(self.data)
@@ -66,6 +69,7 @@ class AskByEmailFormTests(AskbotTestCase):
form.cleaned_data['title'],
output[1]
)
+ askbot_settings.update('TAGS_ARE_REQUIRED', setting_backup)
def test_email(self):
"""loops through variants of the from field
diff --git a/askbot/urls.py b/askbot/urls.py
index 1ab3ea5d..4cf00313 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -252,6 +252,11 @@ urlpatterns = patterns('',
views.commands.manage_inbox,
name='manage_inbox'
),
+ url(#ajax only
+ r'^add_user_to_group/$',
+ views.commands.add_user_to_group,
+ name='add_user_to_group'
+ ),
url(
r'^feeds/(?P<url>.*)/$',
'django.contrib.syndication.views.feed',
diff --git a/askbot/views/commands.py b/askbot/views/commands.py
index 5d86d1a1..c534d87a 100644
--- a/askbot/views/commands.py
+++ b/askbot/views/commands.py
@@ -639,3 +639,32 @@ def read_message(request):#marks message a read
if request.user.is_authenticated():
request.user.delete_messages()
return HttpResponse('')
+
+@csrf.csrf_exempt
+@decorators.ajax_only
+@decorators.post_only
+def add_user_to_group(request):
+ if request.user.is_anonymous():
+ raise exceptions.PermissionDenied()
+
+ if not request.user.is_administrator_or_moderator():
+ raise exceptions.PermissionDenied(
+ _('Only moderators and administrators can assign users to groups')
+ )
+
+ form = forms.AddUserToGroupForm(request.POST)
+ if form.is_valid():
+ group_name = form.cleaned_data['group_name']
+ user_id = form.cleaned_data['user_id']
+
+ group = models.Tag.get_or_create_group_tag(group_name)
+ try:
+ user = models.User.objects.get(id = user_id)
+ except models.User.DoesNotExist:
+ raise exceptions.PermissionDenied(
+ 'user with id %d not found' % user_id
+ )
+
+ request.user.add_user_to_group(user, group)
+ else:
+ raise exceptions.PermissionDenied()