diff options
author | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2010-07-15 21:55:19 -0400 |
---|---|---|
committer | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2010-07-15 21:55:19 -0400 |
commit | 8888ae23938f9c505726aa00b8bf07d9b6780e54 (patch) | |
tree | 71117eadd2f0e627c66d31fa9ef4d1bdcd245c75 | |
parent | 85c72339a35288f8d310416421318e7897886f6b (diff) | |
download | askbot-8888ae23938f9c505726aa00b8bf07d9b6780e54.tar.gz askbot-8888ae23938f9c505726aa00b8bf07d9b6780e54.tar.bz2 askbot-8888ae23938f9c505726aa00b8bf07d9b6780e54.zip |
started working on user moderation
21 files changed, 1270 insertions, 373 deletions
diff --git a/askbot/auth.py b/askbot/auth.py index 55a1633f..0b2bd23b 100644 --- a/askbot/auth.py +++ b/askbot/auth.py @@ -15,9 +15,6 @@ import logging from askbot.conf import settings as askbot_settings -def can_moderate_users(user): - return user.is_superuser - def can_vote_up(user): """Determines if a User can vote Questions and Answers up.""" return user.is_authenticated() and ( diff --git a/askbot/const/__init__.py b/askbot/const/__init__.py index 3395cca7..1f83e7cb 100644 --- a/askbot/const/__init__.py +++ b/askbot/const/__init__.py @@ -207,5 +207,20 @@ TWITTER_STYLE_MENTION_TERMINATION_CHARS = '\n ;,.!?<>' COMMENT_HARD_MAX_LENGTH = 2048 +#user status ch +USER_STATUS_CHOICES = ( + #in addition to these there is administrator + #admin status is determined by the User.is_superuser() call + ('m', _('moderator')), #user with moderation privilege + ('a', _('approved')), #regular user + ('w', _('watched')), #regular user placed on the moderation watch + ('s', _('suspended')), #suspended user who cannot post new stuff + ('b', _('blocked')), #blocked +) +DEFAULT_USER_STATUS = 'w' + +#number of items to show in user views +USER_VIEW_DATA_SIZE = 50 + #an exception import * because that file has only strings from askbot.const.message_keys import * diff --git a/askbot/doc/source/user-moderation.rst b/askbot/doc/source/user-moderation.rst new file mode 100644 index 00000000..4f4d7d13 --- /dev/null +++ b/askbot/doc/source/user-moderation.rst @@ -0,0 +1,49 @@ +========================= +User moderation in Askbot +========================= + +.. note:: + + This is a draft specification + +Concepts +========= + +**User status**. The following user status levels are meaningful in askbot: + +* administrator - user with moderation and administration privileges +* moderator - user with moderation privileges +* approved - user that can make full use of the forum +* watched - like approved user, except his/her contributions are not sent by email +* suspended - only can edit own existing posts and own profile, will see suspension message +* blocked - can't do anything except send feedback, will see blocking message + +These status levels are mutually exclusive. + +**Admin panel**. Each user has a sub/view of his/her account giving tools. + +Admin panel exposes moderation and administration tools. Note that there are some +moderation tools located in other places (e.g. question views). + +If a user can see own admin panel, the panel will always be restricted in function, because +it never makes to communicate by email with him/herself, etc. + +**Moderation tools**: + +* change user status in range from blocked to approved +* arbitrarily add/subtract reputation and leave a message +* send PM to user +* merge tags (tags view, ) +* merge questions (need special tool - sticky selection in search?) + +**Restrictions on moderators** + +* cannot moderate other moderators or admins, cannot access admin tools + +**Administration tools**: + +* edit user profiles +* change user status to moderators and remove it +* merge users +* delete accounts +* delete user contributions diff --git a/askbot/forms.py b/askbot/forms.py index 7c35126e..c0ed2f15 100644 --- a/askbot/forms.py +++ b/askbot/forms.py @@ -11,6 +11,30 @@ from askbot.deps.recaptcha_django import ReCaptchaField from askbot.conf import settings as askbot_settings import logging +def filter_choices(remove_choices = None, from_choices = None): + """a utility function that will remove choice tuples + usable for the forms.ChoicesField from + ``from_choices``, the removed ones will be those given + by the ``remove_choice`` list + + there is no error checking, ``from_choices`` tuple must be as expected + to work with the forms.ChoicesField + """ + + if not isinstance(remove_choices, list): + raise TypeError('remove_choices must be a list') + + filtered_choices = tuple() + for choice_to_test in from_choices: + remove = False + for choice in remove_choices: + if choice == choice_to_test[0]: + remove = True + break + if remove == False: + filtered_choices += ( choice_to_test, ) + + return filtered_choices class TitleField(forms.CharField): def __init__(self, *args, **kwargs): @@ -115,18 +139,102 @@ class SummaryField(forms.CharField): self.label = _('update summary:') self.help_text = _('enter a brief summary of your revision (e.g. fixed spelling, grammar, improved style, this field is optional)') -class ModerateUserForm(forms.ModelForm): - is_approved = forms.BooleanField(label=_("Automatically accept user's contributions for the email updates"), - required=False) +class ChangeUserReputationForm(forms.Form): + reputation_change_points = forms.IntegerField( + label=_('Add or subtract reputation'), + required = False + ) + #this comment must be required with arbitrary karma changes + reputation_change_reason = forms.CharField( + label=_('Because ...'), + required = False + ) + +MODERATOR_STATUS_CHOICES = ( + ('a', _('approved')), + ('w', _('watched')), + ('s', _('suspended')), + ('b', _('blocked')), + ) +ADMINISTRATOR_STATUS_CHOICES = (('m', _('moderator')), ) \ + + MODERATOR_STATUS_CHOICES + +class ChangeUserStatusForm(forms.Form): + user_status = forms.ChoiceField( + label = _('Change status to'), + ) - def clean_is_approved(self): - if 'is_approved' not in self.cleaned_data: - self.cleaned_data['is_approved'] = False - return self.cleaned_data['is_approved'] + def __init__(self, *arg, **kwarg): + + moderator = kwarg.pop('moderator') + subject = kwarg.pop('subject') + + super(ChangeUserStatusForm, self).__init__(*arg, **kwarg) + + #select user_status_choices depending on status of the moderator + if moderator.is_administrator(): + user_status_choices = ADMINISTRATOR_STATUS_CHOICES + elif moderator.is_moderator(): + user_status_choices = MODERATOR_STATUS_CHOICES + if subject.is_moderator() and subject != moderator: + raise ValueError('moderator cannot moderate another moderator') + else: + raise ValueError('moderator or admin expected from "moderator"') + + #remove current status of the "subject" user from choices + user_status_choices = filter_choices( + remove_choices = [subject.status, ], + from_choices = user_status_choices + ) + + #add prompt option + user_status_choices = ( ('select', _('which one?')), ) \ + + user_status_choices + + self.fields['user_status'].choices = user_status_choices + + #set prompt option as default + self.fields['user_status'].default = 'select' + self.moderator = moderator + self.subject = subject + + def clean(self): + #if moderator is looking at own profile - do not + #let change status + if 'user_status' in self.cleaned_data: + + user_status = self.cleaned_data['user_status'] + + #does not make sense to change own user status + #if necessary, this can be done from the Django admin interface + if self.moderator == self.subject: + del self.cleaned_data['user_status'] + raise forms.ValidationError(_('Cannot change own status')) + + #do not let moderators turn other users into moderators + if self.moderator.is_moderator() and user_status == 'moderator': + del self.cleanded_data['user_status'] + raise forms.ValidationError( + _('Cannot turn other user to moderator') + ) + + #do not allow moderator to change status of other moderators + if self.moderator.is_moderator() and self.subject.is_moderator(): + del self.cleaned_data['user_status'] + raise forms.ValidationError( + _('Cannot change status of another moderator') + ) + + if user_status == 'select': + del self.cleaned_data['user_status'] + msg = _( + 'If you wish to change %(username)s\'s status, ' \ + + 'please make a meaningful selection.' + ) % {'username': self.subject.username } + raise forms.ValidationError(msg) + + return self.cleaned_data - class Meta: - model = User - fields = ('is_approved',) class AdvancedSearchForm(forms.Form): #nothing must be required in this form diff --git a/askbot/migrations/0018_add___status__field_to_user_model.py b/askbot/migrations/0018_add___status__field_to_user_model.py new file mode 100644 index 00000000..e63a948c --- /dev/null +++ b/askbot/migrations/0018_add___status__field_to_user_model.py @@ -0,0 +1,309 @@ +# encoding: 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 field 'User.status' + try: + db.add_column( + u'auth_user', + 'status', + self.gf('django.db.models.fields.CharField')(default ='w', max_length = 2), + keep_default=False + ) + except: + pass + + + def backwards(self, orm): + + # Deleting field 'User.status' + db.delete_column(u'auth_user', 'status') + + + 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', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'to': "orm['auth.User']"}), + '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.Question']"}), + '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', 'blank': 'True'}) + }, + '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'}), + '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', 'blank': 'True'}) + }, + 'askbot.answer': { + 'Meta': {'object_name': 'Answer', 'db_table': "u'answer'"}, + 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['auth.User']"}), + 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': '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_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['askbot.Question']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + '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', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'askbot.answerrevision': { + 'Meta': {'object_name': 'AnswerRevision', 'db_table': "u'answer_revision'"}, + 'answer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Answer']"}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answerrevisions'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {}) + }, + '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.Badge']"}), + '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', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"}) + }, + 'askbot.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['auth.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'askbot.comment': { + 'Meta': {'object_name': 'Comment', 'db_table': "u'comment'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'comment': ('django.db.models.fields.CharField', [], {'max_length': '2048'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'html': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '2048'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['auth.User']"}) + }, + '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'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"}) + }, + 'askbot.flaggeditem': { + 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'FlaggedItem', 'db_table': "u'flagged_item'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flaggeditems'", '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.question': { + 'Meta': {'object_name': 'Question', 'db_table': "u'question'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'answer_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questions'", 'to': "orm['auth.User']"}), + 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'closed_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': '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_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'through': "'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_questions'", 'to': "orm['auth.User']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + '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': "'last_active_in_questions'", 'to': "orm['auth.User']"}), + '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_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'questions'", 'to': "orm['askbot.Tag']"}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + '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', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'askbot.questionrevision': { + 'Meta': {'object_name': 'QuestionRevision', 'db_table': "u'question_revision'"}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questionrevisions'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Question']"}), + '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', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + '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.Question']"}), + 'when': ('django.db.models.fields.DateTimeField', [], {}), + 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"}) + }, + 'askbot.repute': { + 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"}, + '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.Question']"}), + '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': {'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', 'blank': '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_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'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'askbot.vote': { + 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'Vote', 'db_table': "u'vote'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + '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'}) + }, + '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']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'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'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + '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']", 'blank': 'True'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + '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'}), + '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'}), + 'response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'tag_filter_setting': ('django.db.models.fields.CharField', [], {'default': "'ignored'", 'max_length': '16'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", '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': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot'] diff --git a/askbot/migrations/0019_populate_user_status_field.py b/askbot/migrations/0019_populate_user_status_field.py new file mode 100644 index 00000000..a79cbe33 --- /dev/null +++ b/askbot/migrations/0019_populate_user_status_field.py @@ -0,0 +1,309 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + """populates User.status field. + """ + for user in orm['auth.user'].objects.all(): + if user.is_superuser or user.is_staff: + #moderator + user.status = 'm' + elif user.is_approved: + #approved user + user.status = 'a' + else: + #watched user + user.status = 'w' + user.save() + + + def backwards(self, orm): + "Backwards migration is meaningless here" + pass + + 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', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'to': "orm['auth.User']"}), + '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.Question']"}), + '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', 'blank': 'True'}) + }, + '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'}), + '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', 'blank': 'True'}) + }, + 'askbot.answer': { + 'Meta': {'object_name': 'Answer', 'db_table': "u'answer'"}, + 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['auth.User']"}), + 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': '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_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['askbot.Question']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + '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', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'askbot.answerrevision': { + 'Meta': {'object_name': 'AnswerRevision', 'db_table': "u'answer_revision'"}, + 'answer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Answer']"}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answerrevisions'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {}) + }, + '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.Badge']"}), + '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', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"}) + }, + 'askbot.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['auth.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'askbot.comment': { + 'Meta': {'object_name': 'Comment', 'db_table': "u'comment'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'comment': ('django.db.models.fields.CharField', [], {'max_length': '2048'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'html': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '2048'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['auth.User']"}) + }, + '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'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"}) + }, + 'askbot.flaggeditem': { + 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'FlaggedItem', 'db_table': "u'flagged_item'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flaggeditems'", '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.question': { + 'Meta': {'object_name': 'Question', 'db_table': "u'question'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'answer_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questions'", 'to': "orm['auth.User']"}), + 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'closed_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': '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_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'through': "'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_questions'", 'to': "orm['auth.User']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + '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': "'last_active_in_questions'", 'to': "orm['auth.User']"}), + '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_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'questions'", 'to': "orm['askbot.Tag']"}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + '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', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'askbot.questionrevision': { + 'Meta': {'object_name': 'QuestionRevision', 'db_table': "u'question_revision'"}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questionrevisions'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Question']"}), + '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', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + '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.Question']"}), + 'when': ('django.db.models.fields.DateTimeField', [], {}), + 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"}) + }, + 'askbot.repute': { + 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"}, + '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.Question']"}), + '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': {'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', 'blank': '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_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'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'askbot.vote': { + 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'Vote', 'db_table': "u'vote'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + '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'}) + }, + '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']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'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'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + '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']", 'blank': 'True'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + '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'}), + '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'}), + 'response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'tag_filter_setting': ('django.db.models.fields.CharField', [], {'default': "'ignored'", 'max_length': '16'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", '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': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot'] diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index 555209b2..f43dc123 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -29,6 +29,16 @@ from askbot.models.repute import Badge, Award, Repute from askbot import auth User.add_to_class('is_approved', models.BooleanField(default=False)) + +User.add_to_class( + 'status', + models.CharField( + max_length = 2, + default = const.DEFAULT_USER_STATUS, + choices = const.USER_STATUS_CHOICES + ) + ) + User.add_to_class('email_isvalid', models.BooleanField(default=False)) User.add_to_class('email_key', models.CharField(max_length=32, null=True)) #hardcoded initial reputaion of 1, no setting for this one @@ -38,10 +48,10 @@ User.add_to_class('gold', models.SmallIntegerField(default=0)) User.add_to_class('silver', models.SmallIntegerField(default=0)) User.add_to_class('bronze', models.SmallIntegerField(default=0)) User.add_to_class('questions_per_page', - models.SmallIntegerField( - choices=const.QUESTIONS_PER_PAGE_USER_CHOICES, - default=10) - ) + models.SmallIntegerField( + choices=const.QUESTIONS_PER_PAGE_USER_CHOICES, + default=10) + ) User.add_to_class('last_seen', models.DateTimeField(default=datetime.datetime.now)) User.add_to_class('real_name', models.CharField(max_length=100, blank=True)) @@ -165,7 +175,7 @@ def user_visit_question(self, question = None, timestamp = None): the post - question, answer or comments """ if not isinstance(question, Question): - raise TypeError('question type expected') + raise TypeError('question type expected, have %s' % type(question)) if timestamp is None: timestamp = datetime.datetime.now() @@ -216,6 +226,79 @@ def user_is_username_taken(cls,username): except cls.DoesNotExist: return False +def user_is_administrator(self): + return (self.is_superuser or self.is_staff) + +def user_is_moderator(self): + return (self.status == 'm' and self.is_administrator() == False) + +def user_is_suspended(self): + return (self.status == 's') + +def user_is_blocked(self): + return (self.status == 'b') + +def user_is_watched(self): + return (self.status == 'w') + +def user_is_approved(self): + return (self.status == 'a') + +def user_set_status(self, new_status): + """sets new status to user + + this method understands that administrator status is + stored in the User.is_superuser field, but + everything else in User.status field + + there is a slight aberration - administrator status + can be removed, but not added yet + + if new status is applied to user, then the record is + committed to the database + """ + assert(new_status in ('m','s','b','w','a')) + if new_status == self.status: + return + + #clear admin status if user was an administrator + if self.is_administrator: + self.is_superuser = False + self.is_staff = False + + self.status = new_status + self.save() + +def user_get_status_display(self, soft = False): + if self.is_administrator(): + return _('Site Adminstrator') + elif self.is_moderator(): + return _('Forum Moderator') + elif self.is_suspended(): + return _('Suspended User') + elif self.is_blocked(): + return _('Blocked User') + elif soft == True: + return _('Registered User') + elif self.is_watched(): + return _('Watched User') + elif self.is_approved(): + return _('Approved User') + else: + raise ValueError('Unknown user status') + + +def user_can_moderate_user(self, other): + if self.is_administrator(): + return True + elif self.is_moderator(): + if other.is_moderator() or other.is_administrator(): + return False + else: + return True + else: + return False + def user_get_q_sel_email_feed_frequency(self): #print 'looking for frequency for user %s' % self try: @@ -425,6 +508,15 @@ User.add_to_class('unfollow_question', user_unfollow_question) User.add_to_class('is_following', user_is_following) User.add_to_class('decrement_response_count', user_decrement_response_count) User.add_to_class('increment_response_count', user_increment_response_count) +User.add_to_class('is_administrator', user_is_administrator) +User.add_to_class('is_moderator', user_is_moderator) +User.add_to_class('is_approved', user_is_approved) +User.add_to_class('is_watched', user_is_watched) +User.add_to_class('is_suspended', user_is_suspended) +User.add_to_class('is_blocked', user_is_blocked) +User.add_to_class('can_moderate_user', user_can_moderate_user) +User.add_to_class('set_status', user_set_status) +User.add_to_class('get_status_display', user_get_status_display) #todo: move this to askbot/utils ?? def format_instant_notification_body( diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css index 709e30f5..b2e74d37 100755 --- a/askbot/skins/default/media/style/style.css +++ b/askbot/skins/default/media/style/style.css @@ -1501,6 +1501,14 @@ table.form-as-table th { font-weight: normal; } +table.ab-subscr-form { + width: 45em; +} + +table.ab-tag-filter-form { + width: 45em; +} + /*.form-row li label { display: inline }*/ diff --git a/askbot/skins/default/templates/ask.html b/askbot/skins/default/templates/ask.html index 4ba11ebf..8c96a953 100644 --- a/askbot/skins/default/templates/ask.html +++ b/askbot/skins/default/templates/ask.html @@ -21,7 +21,7 @@ captureLength: 5, callback: lanai.highlightSyntax}); //toggle preview of editor - //todo remove copy-paste + //todo remove copy-paste var display = true; var txt = "[{% trans "hide preview" %}]"; $('#pre-collapse').text(txt); @@ -34,18 +34,18 @@ //Tags autocomplete action var tags = {{ tags|safe }}; - $("#id_tags").autocomplete(tags, { - minChars: 1, - matchContains: true, - max: 20, - multiple: true, - multipleSeparator: " ", - formatItem: function(row, i, max) { - return row.n + " ("+ row.c +")"; - }, - formatResult: function(row, i, max){ - return row.n; - } + $("#id_tags").autocomplete(tags, { + minChars: 1, + matchContains: true, + max: 20, + multiple: true, + multipleSeparator: " ", + formatItem: function(row, i, max) { + return row.n + " ("+ row.c +")"; + }, + formatResult: function(row, i, max){ + return row.n; + } }); diff --git a/askbot/skins/default/templates/edit_user_email_feeds_form.html b/askbot/skins/default/templates/edit_user_email_feeds_form.html index 65902e7e..c5b23227 100644 --- a/askbot/skins/default/templates/edit_user_email_feeds_form.html +++ b/askbot/skins/default/templates/edit_user_email_feeds_form.html @@ -1,4 +1,4 @@ {% load i18n %} -<table class='form-as-table'> +<table class='form-as-table ab-subscr-form'> {{email_feeds_form.as_table}} </table> diff --git a/askbot/skins/default/templates/user.html b/askbot/skins/default/templates/user.html index dee52cd7..956bb229 100644 --- a/askbot/skins/default/templates/user.html +++ b/askbot/skins/default/templates/user.html @@ -6,13 +6,14 @@ {% load i18n %} {% block title %}{% spaceless %}{{ page_title }}{% endspaceless %}{% endblock %} {% block forestyle%} - <style type="text/css"> - .history-table td { padding: 5px; } - .user-stats-table { margin-left:50px; } - </style> +<style type="text/css"> + .history-table td { padding: 5px; } + .user-stats-table { margin-left:50px; } +</style> {% endblock %} {% block forejs %} - {% if request.user|can_moderate_users %} + {% if request.user|can_moderate_user:view_user %} + <!-- todo: add condition that scripts are loaded for admins only --> <script type='text/javascript' src='{% media "/media/js/com.cnprog.admin.js" %}'></script> <script type='text/javascript' src='{% media "/media/js/jquery.form.js" %}'></script> {% endif %} @@ -38,5 +39,5 @@ {% block usercontent %} {% endblock %} {%comment%}{% include "user_footer.html" %}{%endcomment%} - </div> + </div> {% endblock %}<!-- end user.html --> diff --git a/askbot/skins/default/templates/user_email_subscriptions.html b/askbot/skins/default/templates/user_email_subscriptions.html index 4ee8a1bc..325383db 100644 --- a/askbot/skins/default/templates/user_email_subscriptions.html +++ b/askbot/skins/default/templates/user_email_subscriptions.html @@ -7,13 +7,13 @@ {% block usercontent %} <h2>{% trans "Email subscription settings" %}</h2> <p class="message">{% trans "email subscription settings info" %}</p> - <div class='inline-block'> + <div><!-- class='inline-block'--> {% if action_status %} <p class="action-status"><span>{{action_status}}</span></p> {% endif %} <form method="post" action=""> {% include "edit_user_email_feeds_form.html" %} - <table class='form-as-table'> + <table class='form-as-table ab-tag-filter-form'> {{tag_filter_selection_form}} </table> <div class="submit-row text-align-right"> diff --git a/askbot/skins/default/templates/user_info.html b/askbot/skins/default/templates/user_info.html index c99dd2c1..e942ceec 100644 --- a/askbot/skins/default/templates/user_info.html +++ b/askbot/skins/default/templates/user_info.html @@ -26,17 +26,6 @@ </td> <td width="360" style="vertical-align: top;"> <table class="user-details"> - {% if view_user != request.user and request.user|can_moderate_users %} - <tr> - <td class="admin" align="left" colspan="2"> - <h3>{% trans "Moderate this user" %}</h3> - <form id="moderate_user_form" method="post"> - {{moderate_user_form.as_p}} - </form> - <p id="action_status"></p> - </td> - </tr> - {% endif %} {% if request.user|can_view_user_edit:view_user %} <tr> <td class="user-profile-tool-links" align="left" colspan="2"> @@ -47,7 +36,9 @@ </tr> {% endif %} <tr> - <th colspan="2" align="left"><h3>{% trans "Registered user" %}</h3></th> + <th colspan="2" align="left"> + <h3>{{user_status_for_display}}</h3> + </th> </tr> {% if view_user.real_name %} <tr> diff --git a/askbot/skins/default/templates/user_moderate.html b/askbot/skins/default/templates/user_moderate.html new file mode 100644 index 00000000..992f5287 --- /dev/null +++ b/askbot/skins/default/templates/user_moderate.html @@ -0,0 +1,21 @@ +{% extends "user.html" %} +<!-- user_moderate.html --> +{% load i18n %} +{% load smart_if %} +{% block usercontent %} +{% if request.user != view_user %} + <p>{% blocktrans with view_user.username as username and view_user.get_status_display as status %}{{username}}'s current status is "{{status}}"{% endblocktrans %} + </p> + <form method="post"> + <input type="hidden" name="sort" value="moderate"/> + {{ change_user_status_form.as_table }}<br/> + <input type="submit" class="submit" name="change_status" value="{% trans "Save" %}" /> + </form> +{% endif %} + +<!--- +{{ change_user_reputation_form.as_table }} +<input type="submit" class="submit" name="subtract" value="{% trans "Subtract" %}" /> +<input type="submit" class="submit" name="add" value="{% trans "Add" %}" /> +--> +{% endblock %} diff --git a/askbot/skins/default/templates/user_stats.html b/askbot/skins/default/templates/user_stats.html index 0e0f4d36..bb7fcef4 100644 --- a/askbot/skins/default/templates/user_stats.html +++ b/askbot/skins/default/templates/user_stats.html @@ -4,13 +4,13 @@ {% load extra_tags %} {% load extra_filters %} {% load humanize %} - {% block usercontent %} + {% block usercontent %} {% include "user_info.html" %} <a name="questions"></a> {% spaceless %} <h2>{% blocktrans count questions|length as counter %}<span class="count">{{counter}}</span> Question{% plural %}<span class="count">{{counter}}</span> Questions{% endblocktrans %}</h2> {% endspaceless %} - {% include "users_questions.html" %} + {% include "users_questions.html" %} <a name="answers"></a> {% spaceless %} <h2>{% blocktrans count answered_questions|length as counter %}<span class="count">{{counter}}</span> Answer{% plural %}<span class="count">{{counter}}</span> Answers{% endblocktrans %}</h2> @@ -21,7 +21,7 @@ <a title="{{answered_question.summary|collapse}}" href="{% url question answered_question.id %}{{answered_question.title|slugify}}#{{answered_question.answer_id}}"> <span class="answer-votes {% if answered_question.accepted %}answered-accepted{% endif %}" - title="{% blocktrans with answered_question.answer_score as answer_score %}the answer has been voted for {{ answer_score }} times{% endblocktrans %} {% if answered_question.accepted %}{% trans "this answer has been selected as correct" %}{%endif%}"> + title="{% blocktrans with answered_question.answer_score as answer_score %}the answer has been voted for {{ answer_score }} times{% endblocktrans %} {% if answered_question.accepted %}{% trans "this answer has been selected as correct" %}{%endif%}"> {{ answered_question.answer_score }} </span> </a> diff --git a/askbot/skins/default/templates/user_tabs.html b/askbot/skins/default/templates/user_tabs.html index d0d52930..6e1f973a 100644 --- a/askbot/skins/default/templates/user_tabs.html +++ b/askbot/skins/default/templates/user_tabs.html @@ -1,32 +1,41 @@ <!-- user_tabs.html --> {% load extra_filters %} {% load i18n %} +{% load smart_if %} <div class="tabBar"> <div class="tabsA"> <a id="stats" {% ifequal tab_name "stats" %}class="on"{% endifequal %} - title="{% trans "User profile" %}" href="{% url user_profile view_user.id view_user.username|slugify %}?sort=stats">{% trans "overview" %}</a> + title="{% trans "User profile" %}" + href="{% url user_profile view_user.id view_user.username|slugify %}?sort=stats">{% trans "overview" %}</a> <a id="recent" {% ifequal tab_name "recent" %}class="on"{% endifequal %} - title="{% trans "recent activity" %}" href="{% url user_profile view_user.id view_user.username|slugify %}?sort=recent">{% trans "recent activity" %}</a> - {% if request.user|is_user_self:view_user %} + title="{% trans "recent activity" %}" + href="{% url user_profile view_user.id view_user.username|slugify %}?sort=recent">{% trans "activity" %}</a> + {% if request.user == view_user or request.user|can_moderate_user:view_user %} <a id="responses" {% ifequal tab_name "responses" %}class="on"{% endifequal %} - title="{% trans "comments and answers to others questions" %}" - href="{% url user_profile view_user.id view_user.username|slugify %}?sort=responses">{% trans "responses" %}</a> + title="{% trans "comments and answers to others questions" %}" + href="{% url user_profile view_user.id view_user.username|slugify %}?sort=responses">{% trans "responses" %}</a> {% endif %} <a id="reputation" {% ifequal tab_name "reputation" %}class="on"{% endifequal %} - title="{% trans "graph of user reputation" %}" - href="{% url user_profile view_user.id view_user.username|slugify %}?sort=reputation">{% trans "reputation history" %}</a> - {% if request.user|can_view_user_votes:view_user %} + title="{% trans "graph of user reputation" %}" + href="{% url user_profile view_user.id view_user.username|slugify %}?sort=reputation">{% trans "reputation history" %}</a> + {% if request.user == view_user or request.user|can_moderate_user:view_user %} <a id="votes" {% ifequal tab_name "votes" %}class="on"{% endifequal %} - title="{% trans "user vote record" %}" href="{% url user_profile view_user.id view_user.username|slugify %}?sort=votes">{% trans "casted votes" %}</a> + title="{% trans "user vote record" %}" + href="{% url user_profile view_user.id view_user.username|slugify %}?sort=votes">{% trans "casted votes" %}</a> {% endif %} <a id="favorites" {% ifequal tab_name "favorites" %}class="on"{% endifequal %} - title="{% trans "questions that user selected as his/her favorite" %}" - href="{% url user_profile view_user.id view_user.username|slugify %}?sort=favorites">{% trans "favorites" %}</a> - {% if request.user|can_view_user_preferences:view_user %} + title="{% trans "questions that user selected as his/her favorite" %}" + href="{% url user_profile view_user.id view_user.username|slugify %}?sort=favorites">{% trans "favorites" %}</a> + {% if request.user == view_user or request.user|can_moderate_user:view_user %} <a id="email_subscriptions" {% ifequal tab_name "email_subscriptions" %}class="on"{% endifequal %} - title="{% trans "email subscription settings" %}" - href="{% url user_profile view_user.id view_user.username|slugify %}?sort=email_subscriptions">{% trans "email subscriptions" %}</a> + title="{% trans "email subscription settings" %}" + href="{% url user_profile view_user.id view_user.username|slugify %}?sort=email_subscriptions">{% trans "subscriptions" %}</a> {% endif %} + {% if request.user|can_moderate_user:view_user %} + <a id="moderation" {% ifequal tab_name "moderation" %}class="on"{% endifequal %} + title="{% trans "moderate this user" %}" + href="{% url user_profile view_user.id view_user.username|slugify %}?sort=moderation">{% trans "moderation" %}</a> + {% endif %} </div> </div> <!-- end user_tabs.html --> diff --git a/askbot/templatetags/extra_filters.py b/askbot/templatetags/extra_filters.py index df47ed9a..1418f535 100644 --- a/askbot/templatetags/extra_filters.py +++ b/askbot/templatetags/extra_filters.py @@ -12,8 +12,10 @@ def collapse(input): return ' '.join(input.split()) @register.filter -def can_moderate_users(user): - return auth.can_moderate_users(user) +def can_moderate_user(user, other_user): + if user.is_authenticated() and user.can_moderate_user(other_user): + return True + return False @register.filter def can_vote_up(user): diff --git a/askbot/tests.py b/askbot/tests.py index b68c677f..c56f70c5 100644 --- a/askbot/tests.py +++ b/askbot/tests.py @@ -1364,7 +1364,31 @@ class OnScreenUpdateNotificationTests(TestCase): ) -class AnonymousVisitorTests(TestCase): +class PageLoadTestCase(TestCase): + def try_url( + self, + url_name, status_code=200, template=None, + kwargs={}, redirect_url=None, follow=False, + data = {}, + ): + url = reverse(url_name, kwargs = kwargs) + url_info = 'getting url %s' % url + if data: + url_info += '?' + '&'.join(['%s=%s' % (k, v) for k, v in data.iteritems()]) + print url_info + + r = self.client.get(url, data=data, follow=follow) + if hasattr(self.client, 'redirect_chain'): + print 'redirect chain: %s' % ','.join(self.client.redirect_chain) + + self.assertEqual(r.status_code, status_code) + + if template: + #asuming that there is more than one template + print 'templates are %s' % ','.join([t.name for t in r.template]) + self.assertEqual(r.template[0].name, template) + +class PageLoadTests(PageLoadTestCase): fixtures = ['tmp/fixture1.json', ] def test_index(self): @@ -1379,241 +1403,229 @@ class AnonymousVisitorTests(TestCase): self.assertEqual(t.name, 'questions.html') print 'index works' - def test_reader_urls(self): + def proto_test_non_user_urls(self): """test all reader views thoroughly on non-crashiness (no correcteness tests here) """ - def try_url( - url_name, status_code=200, template=None, - kwargs={}, redirect_url=None, follow=False, - data = {}, - ): - url = reverse(url_name, kwargs = kwargs) - url_info = 'getting url %s' % url - if data: - url_info += '?' + '&'.join(['%s=%s' % (k, v) for k, v in data.iteritems()]) - print url_info - - r = self.client.get(url, data=data, follow=follow) - if hasattr(self.client, 'redirect_chain'): - print 'redirect chain: %s' % ','.join(self.client.redirect_chain) - - self.assertEqual(r.status_code, status_code) - - if template: - #asuming that there is more than one template - print 'templates are %s' % ','.join([t.name for t in r.template]) - self.assertEqual(r.template[0].name, template) - - try_url('sitemap') - try_url('feeds', kwargs={'url':'rss'}) - try_url('about', template='about.html') - try_url('privacy', template='privacy.html') - try_url('logout', template='logout.html') - try_url('user_signin', template='authopenid/signin.html') + self.try_url('sitemap') + self.try_url('feeds', kwargs={'url':'rss'}) + self.try_url('about', template='about.html') + self.try_url('privacy', template='privacy.html') + self.try_url('logout', template='logout.html') + self.try_url('user_signin', template='authopenid/signin.html') #todo: test different tabs - try_url('tags', template='tags.html') - try_url('tags', data={'sort':'name'}, template='tags.html') - try_url('tags', data={'sort':'used'}, template='tags.html') - try_url('badges', template='badges.html') - try_url( + self.try_url('tags', template='tags.html') + self.try_url('tags', data={'sort':'name'}, template='tags.html') + self.try_url('tags', data={'sort':'used'}, template='tags.html') + self.try_url('badges', template='badges.html') + self.try_url( 'answer_revisions', template='revisions_answer.html', kwargs={'id':38} ) #todo: test different sort methods and scopes - try_url( + self.try_url( 'questions', template='questions.html' ) - try_url( + self.try_url( 'questions', data={'start_over':'true'}, template='questions.html' ) - try_url( + self.try_url( 'questions', data={'scope':'unanswered'}, template='questions.html' ) - try_url( + self.try_url( 'questions', data={'scope':'all'}, template='questions.html' ) - try_url( + self.try_url( 'questions', data={'scope':'favorite'}, template='questions.html' ) - try_url( + self.try_url( 'questions', data={'scope':'unanswered', 'sort':'latest'}, template='questions.html' ) - try_url( + self.try_url( 'questions', data={'scope':'unanswered', 'sort':'oldest'}, template='questions.html' ) - try_url( + self.try_url( 'questions', data={'scope':'unanswered', 'sort':'active'}, template='questions.html' ) - try_url( + self.try_url( 'questions', data={'scope':'unanswered', 'sort':'inactive'}, template='questions.html' ) - try_url( + self.try_url( 'questions', data={'sort':'hottest'}, template='questions.html' ) - try_url( + self.try_url( 'questions', data={'sort':'coldest'}, template='questions.html' ) - try_url( + self.try_url( 'questions', data={'sort':'mostvoted'}, template='questions.html' ) - try_url( + self.try_url( 'questions', data={'sort':'leastvoted'}, template='questions.html' ) - try_url( + self.try_url( 'question', kwargs={'id':1}, ) - try_url( + self.try_url( 'question', kwargs={'id':2}, ) - try_url( + self.try_url( 'question', kwargs={'id':3}, ) - try_url( + self.try_url( 'question_revisions', kwargs={'id':17}, template='revisions_question.html' ) - try_url('users', template='users.html') + self.try_url('users', template='users.html') #todo: really odd naming conventions for sort methods - try_url( + self.try_url( 'users', template='users.html', data={'sort':'reputation'}, ) - try_url( + self.try_url( 'users', template='users.html', data={'sort':'newest'}, ) - try_url( + self.try_url( 'users', template='users.html', data={'sort':'last'}, ) - try_url( + self.try_url( 'users', template='users.html', data={'sort':'user'}, ) - try_url( + self.try_url( 'users', template='users.html', data={'sort':'reputation', 'page':2}, ) - try_url( + self.try_url( 'users', template='users.html', data={'sort':'newest', 'page':2}, ) - try_url( + self.try_url( 'users', template='users.html', data={'sort':'last', 'page':2}, ) - try_url( + self.try_url( 'users', template='users.html', data={'sort':'user', 'page':2}, ) - try_url( + self.try_url( 'users', template='users.html', data={'sort':'reputation', 'page':1}, ) - try_url( + self.try_url( 'users', template='users.html', data={'sort':'newest', 'page':1}, ) - try_url( + self.try_url( 'users', template='users.html', data={'sort':'last', 'page':1}, ) - try_url( + self.try_url( 'users', template='users.html', data={'sort':'user', 'page':1}, ) - try_url( + self.try_url( 'edit_user', template='authopenid/signin.html', kwargs={'id':4}, status_code=200, follow=True, ) + + def test_non_user_urls(self): + self.proto_test_non_user_urls() + + #def test_non_user_urls_logged_in(self): + #user = User.objects.get(id=1) + #somehow login this user + #self.proto_test_non_user_urls() + + def test_user_urls(self): user = User.objects.get(id=2) name_slug = defaultfilters.slugify(user.username) - try_url( + self.try_url( 'user_profile', kwargs={'id': 2, 'slug': name_slug}, data={'sort':'stats'}, template='user_stats.html' ) - try_url( + self.try_url( 'user_profile', kwargs={'id': 2, 'slug': name_slug}, data={'sort':'recent'}, template='user_recent.html' ) - try_url( + self.try_url( 'user_profile', kwargs={'id': 2, 'slug': name_slug}, data={'sort':'responses'}, status_code=404, template='404.html' ) - try_url( + self.try_url( 'user_profile', kwargs={'id': 2, 'slug': name_slug}, data={'sort':'reputation'}, template='user_reputation.html' ) - try_url( + self.try_url( 'user_profile', kwargs={'id': 2, 'slug': name_slug}, data={'sort':'votes'}, status_code=404, template='404.html' ) - try_url( + self.try_url( 'user_profile', kwargs={'id': 2, 'slug': name_slug}, data={'sort':'favorites'}, template='user_favorites.html' ) - try_url( + self.try_url( 'user_profile', kwargs={'id': 2, 'slug': name_slug}, data={'sort':'email_subscriptions'}, diff --git a/askbot/urls.py b/askbot/urls.py index 9274f6dc..1415d624 100644 --- a/askbot/urls.py +++ b/askbot/urls.py @@ -157,11 +157,6 @@ urlpatterns = patterns('', app.users.users, name='users' ), - url( - r'^%s(?P<id>\d+)/$' % _('moderate-user/'), - app.users.moderate_user, - name='moderate_user' - ), #todo: rename as user_edit, b/c that's how template is named url( r'^%s(?P<id>\d+)/%s$' % (_('users/'), _('edit/')), diff --git a/askbot/views/readers.py b/askbot/views/readers.py index 706584bc..9d3229c7 100644 --- a/askbot/views/readers.py +++ b/askbot/views/readers.py @@ -346,9 +346,8 @@ def question(request, id):#refactor - long subroutine. display question body, an #2) question view count per user and clear response displays if request.user.is_authenticated(): - #get response notifications - request.user.visit_question() + request.user.visit_question(question) return render_to_response('question.html', { 'view_name': 'question', diff --git a/askbot/views/users.py b/askbot/views/users.py index 2b20ccc7..bd8e60bd 100644 --- a/askbot/views/users.py +++ b/askbot/views/users.py @@ -22,6 +22,7 @@ from askbot.utils.html import sanitize_html from askbot import auth from askbot import forms import calendar +import functools from askbot import const from askbot.conf import settings as askbot_settings from askbot import models @@ -46,6 +47,18 @@ question_revision_type_id = question_revision_type.id answer_revision_type_id = answer_revision_type.id repute_type_id = repute_type.id +def owner_or_moderator_required(f): + @functools.wraps(f) + def wrapped_func(request, profile_owner): + if profile_owner == request.user: + pass + elif request.user.is_authenticated() and request.user.can_moderate_user(profile_owner): + pass + else: + raise Http404 #todo: change to access forbidden? + return f(request, profile_owner) + return wrapped_func + def users(request): is_paginated = True sortby = request.GET.get('sort', 'reputation') @@ -113,28 +126,42 @@ def users(request): context_instance=RequestContext(request) ) -@login_required -def moderate_user(request, id): - """ajax handler of user moderation +def user_moderate(request, subject): + """user subview for moderation """ - if not auth.can_moderate_users(request.user) or request.method != 'POST': + moderator = request.user + + if not moderator.can_moderate_user(subject): raise Http404 - if not request.is_ajax(): - return HttpResponseForbidden(mimetype="application/json") - user = get_object_or_404(models.User, id=id) - form = forms.ModerateUserForm(request.POST, instance=user) - - if form.is_valid(): - form.save() - logging.debug('data saved') - response = HttpResponse( - simplejson.dumps(''), - mimetype="application/json" - ) - else: - response = HttpResponseForbidden(mimetype="application/json") - return response + if request.method == 'POST': + if 'change_status' in request.POST: + user_status_form = forms.ChangeUserStatusForm( + request.POST, + moderator = moderator, + subject = subject + ) + if user_status_form.is_valid(): + subject.set_status( user_status_form.cleaned_data['user_status'] ) + + #need to re-initialize the form even if it was posted, because + #initial values will most likely be different from the previous + user_status_form = forms.ChangeUserStatusForm( + moderator = moderator, + subject = subject + ) + return render_to_response( + 'user_moderate.html', + { + 'active_tab': 'users', + 'tab_name': 'moderation', + 'tab_description': _('moderate this user'), + 'page_title': _('moderate user'), + 'view_user': subject, + 'change_user_status_form': user_status_form, + }, + context_instance=RequestContext(request) + ) #non-view function def set_new_email(user, new_email, nomessage=False): @@ -147,8 +174,11 @@ def set_new_email(user, new_email, nomessage=False): @login_required def edit_user(request, id): + """View that allows to edit user profile. + This view is accessible to profile owners or site administrators + """ user = get_object_or_404(models.User, id=id) - if request.user != user: + if request.user != user or not request.user.is_superuser: raise Http404 if request.method == "POST": form = forms.EditUserForm(user, request.POST) @@ -192,8 +222,8 @@ def edit_user(request, id): context_instance=RequestContext(request) ) -def user_stats(request, user_id, user_view): - user = get_object_or_404(models.User, id=user_id) +def user_stats(request, user): + questions = models.Question.objects.extra( select={ 'score' : 'question.score', @@ -205,10 +235,10 @@ def user_stats(request, user_id, user_view): 'la_user_bronze' : 'auth_user.bronze', 'la_user_reputation' : 'auth_user.reputation' }, - select_params=[user_id], + select_params=[user.id], tables=['question', 'auth_user'], where=['question.deleted=False AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'], - params=[user_id], + params=[user.id], order_by=['-score', '-last_activity_at'] ).values('score', 'favorited_myself', @@ -245,9 +275,9 @@ def user_stats(request, user_id, user_view): }, tables=['question', 'answer'], where=['answer.deleted=False AND question.deleted=False AND answer.author_id=%s AND answer.question_id=question.id'], - params=[user_id], + params=[user.id], order_by=['-answer_score', '-answer_id'], - select_params=[user_id] + select_params=[user.id] ).distinct().values('comment_count', 'id', 'answer_id', @@ -311,36 +341,42 @@ def user_stats(request, user_id, user_view): ) user_tags.query.group_by = ['name'] - if auth.can_moderate_users(request.user): - moderate_user_form = forms.ModerateUserForm(instance=user) + if user.is_administrator(): + user_status = _('Site Adminstrator') + elif user.is_moderator(): + user_status = _('Forum Moderator') + elif user.is_suspended(): + user_status = _('Suspended User') + elif user.is_blocked(): + user_status = _('Blocked User') else: - moderate_user_form = None + user_status = _('Registered User') return render_to_response( - user_view.template_file, + 'user_stats.html', { 'active_tab':'users', - 'moderate_user_form': moderate_user_form, - "tab_name" : user_view.id, - "tab_description" : user_view.tab_description, - "page_title" : user_view.page_title, - "view_user" : user, - "questions" : questions, - "answered_questions" : answered_questions, - "up_votes" : up_votes, - "down_votes" : down_votes, - "total_votes": up_votes + down_votes, - "votes_today_left": votes_total-votes_today, - "votes_total_per_day": votes_total, - "user_tags" : user_tags[:50], - "awards": awards, - "total_awards" : total_awards, + 'tab_name' : 'stats', + 'tab_description' : _('user profile'), + 'page_title' : _('user profile overview'), + 'view_user' : user, + 'user_status_for_display': user.get_status_display(soft = True), + 'questions' : questions, + 'answered_questions' : answered_questions, + 'up_votes' : up_votes, + 'down_votes' : down_votes, + 'total_votes': up_votes + down_votes, + 'votes_today_left': votes_total-votes_today, + 'votes_total_per_day': votes_total, + 'user_tags' : user_tags[:const.USER_VIEW_DATA_SIZE], + 'awards': awards, + 'total_awards' : total_awards, }, context_instance=RequestContext(request) ) -def user_recent(request, user_id, user_view): - user = get_object_or_404(models.User, id=user_id) +def user_recent(request, user): + def get_type_name(type_id): for item in const.TYPE_ACTIVITY: if type_id in item: @@ -380,7 +416,7 @@ def user_recent(request, user_id, user_view): tables=['activity', 'question'], where=['activity.content_type_id = %s AND activity.object_id = ' + 'question.id AND question.deleted=False AND activity.user_id = %s AND activity.activity_type = %s'], - params=[question_type_id, user_id, const.TYPE_ACTIVITY_ASK_QUESTION], + params=[question_type_id, user.id, const.TYPE_ACTIVITY_ASK_QUESTION], order_by=['-activity.active_at'] ).values( 'title', @@ -417,7 +453,7 @@ def user_recent(request, user_id, user_view): where=['activity.content_type_id = %s AND activity.object_id = answer.id AND ' + 'answer.question_id=question.id AND answer.deleted=False AND activity.user_id=%s AND '+ 'activity.activity_type=%s AND question.deleted=False'], - params=[answer_type_id, user_id, const.TYPE_ACTIVITY_ANSWER], + params=[answer_type_id, user.id, const.TYPE_ACTIVITY_ANSWER], order_by=['-activity.active_at'] ).values( 'title', @@ -445,7 +481,7 @@ def user_recent(request, user_id, user_view): 'activity.user_id = comment.user_id AND comment.object_id=question.id AND '+ 'comment.content_type_id=%s AND activity.user_id = %s AND activity.activity_type=%s AND ' + 'question.deleted=False'], - params=[comment_type_id, question_type_id, user_id, const.TYPE_ACTIVITY_COMMENT_QUESTION], + params=[comment_type_id, question_type_id, user.id, const.TYPE_ACTIVITY_COMMENT_QUESTION], order_by=['-comment.added_at'] ).values( 'title', @@ -475,7 +511,7 @@ def user_recent(request, user_id, user_view): 'comment.content_type_id=%s AND question.id = answer.question_id AND '+ 'activity.user_id = %s AND activity.activity_type=%s AND '+ 'answer.deleted=False AND question.deleted=False'], - params=[comment_type_id, answer_type_id, user_id, const.TYPE_ACTIVITY_COMMENT_ANSWER], + params=[comment_type_id, answer_type_id, user.id, const.TYPE_ACTIVITY_COMMENT_ANSWER], order_by=['-comment.added_at'] ).values( 'title', @@ -504,7 +540,7 @@ def user_recent(request, user_id, user_view): 'question_revision.id=question.id AND question.deleted=False AND '+ 'activity.user_id = question_revision.author_id AND activity.user_id = %s AND '+ 'activity.activity_type=%s'], - params=[question_revision_type_id, user_id, const.TYPE_ACTIVITY_UPDATE_QUESTION], + params=[question_revision_type_id, user.id, const.TYPE_ACTIVITY_UPDATE_QUESTION], order_by=['-activity.active_at'] ).values( 'title', @@ -536,7 +572,7 @@ def user_recent(request, user_id, user_view): 'answer_revision.answer_id=answer.id AND answer.question_id = question.id AND '+ 'question.deleted=False AND answer.deleted=False AND '+ 'activity.activity_type=%s'], - params=[answer_revision_type_id, user_id, const.TYPE_ACTIVITY_UPDATE_ANSWER], + params=[answer_revision_type_id, user.id, const.TYPE_ACTIVITY_UPDATE_ANSWER], order_by=['-activity.active_at'] ).values( 'title', @@ -565,7 +601,7 @@ def user_recent(request, user_id, user_view): 'activity.user_id = question.author_id AND activity.user_id = %s AND '+ 'answer.deleted=False AND question.deleted=False AND '+ 'answer.question_id=question.id AND activity.activity_type=%s'], - params=[answer_type_id, user_id, const.TYPE_ACTIVITY_MARK_ANSWER], + params=[answer_type_id, user.id, const.TYPE_ACTIVITY_MARK_ANSWER], order_by=['-activity.active_at'] ).values( 'title', @@ -587,7 +623,7 @@ def user_recent(request, user_id, user_view): tables=['activity', 'award', 'badge'], where=['activity.user_id = award.user_id AND activity.user_id = %s AND '+ 'award.badge_id=badge.id AND activity.object_id=award.id AND activity.activity_type=%s'], - params=[user_id, const.TYPE_ACTIVITY_PRIZE], + params=[user.id, const.TYPE_ACTIVITY_PRIZE], order_by=['-activity.active_at'] ).values( 'badge_id', @@ -600,55 +636,51 @@ def user_recent(request, user_id, user_view): activities.sort(lambda x,y: cmp(y.time, x.time)) - return render_to_response(user_view.template_file,{ - 'active_tab':'users', - "tab_name" : user_view.id, - "tab_description" : user_view.tab_description, - "page_title" : user_view.page_title, + return render_to_response('user_recent.html', + { + 'active_tab': 'users', + "tab_name" : 'recent', + "tab_description" : _('recent user activity'), + "page_title" : _('profile - recent activity'), "view_user" : user, - "activities" : activities[:user_view.data_size] + "activities" : activities[:const.USER_VIEW_DATA_SIZE] }, context_instance=RequestContext(request)) -class Response: - """class that abstracts any kind of response - answer, comment, mention, post edits, etc. - """ - def __init__( - self, type, title, question_id, - answer_id, time, username, - user_id, content): - - self.type = type - self.title = title - self.titlelink = reverse( - 'question', - args=[question_id]) \ - + u'%s#%s' % (slugify(title), - answer_id - ) - self.time = time - self.userlink = reverse('users') + u'%s/%s/' % (user_id, username) - self.username = username - self.content = u'%s ...' % strip_tags(content)[:300] - - def __unicode__(self): - return u'%s %s' % (self.type, self.titlelink) - -def user_responses(request, user_id, user_view): +#class Response: +# """class that abstracts any kind of response +# answer, comment, mention, post edits, etc. +# """ +# def __init__( +# self, type, title, question_id, +# answer_id, time, username, +# user_id, content): +# +# self.type = type +# self.title = title +# self.titlelink = reverse( +# 'question', +# args=[question_id]) \ +# + u'%s#%s' % (slugify(title), +# answer_id +# ) +# self.time = time +# self.userlink = reverse('users') + u'%s/%s/' % (user_id, username) +# self.username = username +# self.content = u'%s ...' % strip_tags(content)[:300] + +# def __unicode__(self): +# return u'%s %s' % (self.type, self.titlelink) + +@owner_or_moderator_required +def user_responses(request, user): """ We list answers for question, comments, and answer accepted by others for this user. as well as mentions of the user - user_id - id of the profile owner - user_view - id of the user who is looking at the - page + user - the profile owner """ - user = get_object_or_404(models.User, id=user_id) - if request.user != user: - raise Http404 - user = get_object_or_404(models.User, id=user_id) response_list = [] activities = list() @@ -671,22 +703,21 @@ def user_responses(request, user_id, user_view): response_list.sort(lambda x,y: cmp(y['timestamp'], x['timestamp'])) return render_to_response( - user_view.template_file, + 'user_responses.html', { 'active_tab':'users', - 'tab_name' : user_view.id, - 'tab_description' : user_view.tab_description, - 'page_title' : user_view.page_title, + 'tab_name' : 'responses', + 'tab_description' : _('comments and answers to others questions'), + 'page_title' : _('profile - responses'), 'view_user' : user, - 'responses' : response_list[:user_view.data_size], + 'responses' : response_list[:const.USER_VIEW_DATA_SIZE], }, context_instance=RequestContext(request) ) -def user_votes(request, user_id, user_view): - user = get_object_or_404(models.User, id=user_id) - if not auth.can_view_user_votes(request.user, user): - raise Http404 +@owner_or_moderator_required +def user_votes(request, user): + votes = [] question_votes = models.Vote.objects.extra( select={ @@ -696,11 +727,11 @@ def user_votes(request, user_id, user_view): 'voted_at' : 'vote.voted_at', 'vote' : 'vote', }, - select_params=[user_id], + select_params=[user.id], tables=['vote', 'question', 'auth_user'], where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = question.id '+ 'AND vote.user_id=auth_user.id'], - params=[question_type_id, user_id], + params=[question_type_id, user.id], order_by=['-vote.id'] ).values( 'title', @@ -720,11 +751,11 @@ def user_votes(request, user_id, user_view): 'voted_at' : 'vote.voted_at', 'vote' : 'vote', }, - select_params=[user_id], + select_params=[user.id], tables=['vote', 'answer', 'question', 'auth_user'], where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = answer.id '+ 'AND answer.question_id = question.id AND vote.user_id=auth_user.id'], - params=[answer_type_id, user_id], + params=[answer_type_id, user.id], order_by=['-vote.id'] ).values( 'title', @@ -736,18 +767,17 @@ def user_votes(request, user_id, user_view): if(len(answer_votes) > 0): votes.extend(answer_votes) votes.sort(lambda x,y: cmp(y['voted_at'], x['voted_at'])) - return render_to_response(user_view.template_file,{ + + return render_to_response('user_votes.html', { 'active_tab':'users', - "tab_name" : user_view.id, - "tab_description" : user_view.tab_description, - "page_title" : user_view.page_title, + "tab_name" : 'votes', + "tab_description" : _('user vote record'), + "page_title" : _('profile - votes'), "view_user" : user, - "votes" : votes[:user_view.data_size] - + "votes" : votes[:const.USER_VIEW_DATA_SIZE] }, context_instance=RequestContext(request)) -def user_reputation(request, user_id, user_view): - user = get_object_or_404(models.User, id=user_id) +def user_reputation(request, user): try: from django.db.models import Sum reputation = models.Repute.objects.extra( @@ -777,18 +807,17 @@ def user_reputation(request, user_id, user_view): reps = ','.join(rep_list) reps = '[%s]' % reps - return render_to_response(user_view.template_file, { + return render_to_response('user_reputation.html', { 'active_tab':'users', - "tab_name": user_view.id, - "tab_description": user_view.tab_description, - "page_title": user_view.page_title, + "tab_name": 'reputation', + "tab_description": _('user reputation in the community'), + "page_title": _('profile - user reputation'), "view_user": user, "reputation": reputation, "reps": reps }, context_instance=RequestContext(request)) -def user_favorites(request, user_id, user_view): - user = get_object_or_404(models.User, id=user_id) +def user_favorites(request, user): questions = models.Question.objects.extra( select={ 'score' : 'question.vote_up_count + question.vote_down_count', @@ -801,11 +830,11 @@ def user_favorites(request, user_id, user_view): 'la_user_bronze' : 'auth_user.bronze', 'la_user_reputation' : 'auth_user.reputation' }, - select_params=[user_id], + select_params=[user.id], tables=['question', 'auth_user', 'favorite_question'], where=['question.deleted=False AND question.last_activity_by_id = auth_user.id '+ 'AND favorite_question.question_id = question.id AND favorite_question.user_id = %s'], - params=[user_id], + params=[user.id], order_by=['-score', '-question.id'] ).values('score', 'favorited_myself', @@ -829,19 +858,19 @@ def user_favorites(request, user_id, user_view): 'la_user_silver', 'la_user_bronze', 'la_user_reputation') - return render_to_response(user_view.template_file,{ + + return render_to_response('user_favorites.html',{ 'active_tab':'users', - "tab_name" : user_view.id, - "tab_description" : user_view.tab_description, - "page_title" : user_view.page_title, - "questions" : questions[:user_view.data_size], + "tab_name" : 'favorites', + "tab_description" : _('users favorite questions'), + "page_title" : _('profile - favorite questions'), + "questions" : questions[:const.USER_VIEW_DATA_SIZE], "view_user" : user }, context_instance=RequestContext(request)) -def user_email_subscriptions(request, user_id, user_view): - user = get_object_or_404(models.User, id=user_id) - if request.user != user: - raise Http404 +@owner_or_moderator_required +def user_email_subscriptions(request, user): + if request.method == 'POST': email_feeds_form = forms.EditUserEmailFeedsForm(request.POST) tag_filter_form = forms.TagFilterSelectionForm(request.POST, instance=user) @@ -866,100 +895,51 @@ def user_email_subscriptions(request, user_id, user_view): email_feeds_form.set_initial_values(user) tag_filter_form = forms.TagFilterSelectionForm(instance=user) action_status = None - return render_to_response(user_view.template_file,{ - 'active_tab':'users', - 'tab_name':user_view.id, - 'tab_description':user_view.tab_description, - 'page_title':user_view.page_title, - 'view_user':user, - 'email_feeds_form':email_feeds_form, - 'tag_filter_selection_form':tag_filter_form, - 'action_status':action_status, - }, context_instance=RequestContext(request)) -class UserView: - def __init__(self, id, tab_title, tab_description, page_title, view_func, template_file, data_size=0): - self.id = id - self.tab_title = tab_title - self.tab_description = tab_description - self.page_title = page_title - self.view_func = view_func - self.template_file = template_file - self.data_size = data_size - -USER_TEMPLATE_VIEWS = ( - UserView( - id = 'stats', - tab_title = _('overview'), - tab_description = _('user profile'), - page_title = _('user profile overview'), - view_func = user_stats, - template_file = 'user_stats.html' - ), - UserView( - id = 'recent', - tab_title = _('recent activity'), - tab_description = _('recent user activity'), - page_title = _('profile - recent activity'), - view_func = user_recent, - template_file = 'user_recent.html', - data_size = 50 - ), - UserView( - id = 'responses', - tab_title = _('responses'), - tab_description = _('comments and answers to others questions'), - page_title = _('profile - responses'), - view_func = user_responses, - template_file = 'user_responses.html', - data_size = 50 - ), - UserView( - id = 'reputation', - tab_title = _('reputation'), - tab_description = _('user reputation in the community'), - page_title = _('profile - user reputation'), - view_func = user_reputation, - template_file = 'user_reputation.html' - ), - UserView( - id = 'favorites', - tab_title = _('favorite questions'), - tab_description = _('users favorite questions'), - page_title = _('profile - favorite questions'), - view_func = user_favorites, - template_file = 'user_favorites.html', - data_size = 50 - ), - UserView( - id = 'votes', - tab_title = _('casted votes'), - tab_description = _('user vote record'), - page_title = _('profile - votes'), - view_func = user_votes, - template_file = 'user_votes.html', - data_size = 50 - ), - UserView( - id = 'email_subscriptions', - tab_title = _('email subscriptions'), - tab_description = _('email subscription settings'), - page_title = _('profile - email subscriptions'), - view_func = user_email_subscriptions, - template_file = 'user_email_subscriptions.html' - ) -) + return render_to_response('user_email_subscriptions.html',{ + 'active_tab': 'users', + 'tab_name': 'email_subscriptions', + 'tab_description': _('email subscription settings'), + 'page_title': _('profile - email subscriptions'), + 'view_user': user, + 'email_feeds_form': email_feeds_form, + 'tag_filter_selection_form': tag_filter_form, + 'action_status': action_status, + }, context_instance=RequestContext(request)) +user_view_call_table = { + 'stats': user_stats, + 'recent': user_recent, + 'responses': user_responses, + 'reputation': user_reputation, + 'favorites': user_favorites, + 'votes': user_votes, + 'email_subscriptions': user_email_subscriptions, + 'moderation': user_moderate, +} #todo: rename this function - variable named user is everywhere def user(request, id, slug=None): - sort = request.GET.get('sort', 'stats') - user_view = dict( - (v.id, v) for v in USER_TEMPLATE_VIEWS - ).get( - sort, USER_TEMPLATE_VIEWS[0] - ) - func = user_view.view_func - return func(request, id, user_view) + """Main user view function that works as a switchboard + + id - id of the profile owner + + todo: decide what to do with slug - it is not used + in the code in any way + """ + + profile_owner = get_object_or_404(models.User, id = id) + + #sort CGI parameter tells us which tab in the user + #profile to show, the default one is 'stats' + tab_name = request.GET.get('sort', 'stats') + + if tab_name in user_view_call_table: + #get the actual view function + user_view_func = user_view_call_table[tab_name] + else: + user_view_func = user_stats + + return user_view_func(request, profile_owner) @login_required def account_settings(request):#todo: is this actually used? |