summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-07-15 21:55:19 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-07-15 21:55:19 -0400
commit8888ae23938f9c505726aa00b8bf07d9b6780e54 (patch)
tree71117eadd2f0e627c66d31fa9ef4d1bdcd245c75
parent85c72339a35288f8d310416421318e7897886f6b (diff)
downloadaskbot-8888ae23938f9c505726aa00b8bf07d9b6780e54.tar.gz
askbot-8888ae23938f9c505726aa00b8bf07d9b6780e54.tar.bz2
askbot-8888ae23938f9c505726aa00b8bf07d9b6780e54.zip
started working on user moderation
-rw-r--r--askbot/auth.py3
-rw-r--r--askbot/const/__init__.py15
-rw-r--r--askbot/doc/source/user-moderation.rst49
-rw-r--r--askbot/forms.py128
-rw-r--r--askbot/migrations/0018_add___status__field_to_user_model.py309
-rw-r--r--askbot/migrations/0019_populate_user_status_field.py309
-rw-r--r--askbot/models/__init__.py102
-rwxr-xr-xaskbot/skins/default/media/style/style.css8
-rw-r--r--askbot/skins/default/templates/ask.html26
-rw-r--r--askbot/skins/default/templates/edit_user_email_feeds_form.html2
-rw-r--r--askbot/skins/default/templates/user.html13
-rw-r--r--askbot/skins/default/templates/user_email_subscriptions.html4
-rw-r--r--askbot/skins/default/templates/user_info.html15
-rw-r--r--askbot/skins/default/templates/user_moderate.html21
-rw-r--r--askbot/skins/default/templates/user_stats.html6
-rw-r--r--askbot/skins/default/templates/user_tabs.html37
-rw-r--r--askbot/templatetags/extra_filters.py6
-rw-r--r--askbot/tests.py158
-rw-r--r--askbot/urls.py5
-rw-r--r--askbot/views/readers.py3
-rw-r--r--askbot/views/users.py424
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" %}" />&nbsp;
+<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?