diff options
author | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-03-19 23:16:52 -0400 |
---|---|---|
committer | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-03-19 23:16:52 -0400 |
commit | 0880dd09962294723a167dd924bedb78cc7d3660 (patch) | |
tree | b0f63599468bf2d8a4fcedf7c9e9bea4cd457fcb | |
parent | 8d0b72bff3b5285ff08928ffb394f74c0da13000 (diff) | |
download | askbot-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__.py | 1 | ||||
-rw-r--r-- | askbot/conf/group_settings.py | 20 | ||||
-rw-r--r-- | askbot/forms.py | 4 | ||||
-rw-r--r-- | askbot/migrations/0114_auto__add_groupmembership__add_field_tag_tag_wiki.py | 303 | ||||
-rw-r--r-- | askbot/models/__init__.py | 8 | ||||
-rw-r--r-- | askbot/models/tag.py | 16 | ||||
-rw-r--r-- | askbot/models/user.py | 13 | ||||
-rw-r--r-- | askbot/skins/common/media/js/user.js | 147 | ||||
-rw-r--r-- | askbot/skins/default/templates/macros.html | 4 | ||||
-rw-r--r-- | askbot/skins/default/templates/user_profile/user.html | 2 | ||||
-rw-r--r-- | askbot/skins/default/templates/user_profile/user_stats.html | 40 | ||||
-rw-r--r-- | askbot/tests/form_tests.py | 4 | ||||
-rw-r--r-- | askbot/urls.py | 5 | ||||
-rw-r--r-- | askbot/views/commands.py | 29 |
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() |