]> git.openstreetmap.org Git - osqa.git/blobdiff - forum/models/user.py
Adds a couple of options to manage the "accepting answers" workflow.
[osqa.git] / forum / models / user.py
index 8837ef35836a22d84dd92e248aea51408d0f13d3..cabaabbd156947e6456c3b82f999aa4d5920c18f 100644 (file)
@@ -1,4 +1,5 @@
 from base import *
 from base import *
+from utils import PickledObjectField
 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.auth.models import User as DjangoUser, AnonymousUser as DjangoAnonymousUser
 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.auth.models import User as DjangoUser, AnonymousUser as DjangoAnonymousUser
@@ -20,10 +21,6 @@ QUESTIONS_PER_PAGE_CHOICES = (
 (50, u'50'),
 )
 
 (50, u'50'),
 )
 
-class UserManager(CachedManager):
-    def get_site_owner(self):
-        return self.all().order_by('date_joined')[0]
-
 class AnonymousUser(DjangoAnonymousUser):
     def get_visible_answers(self, question):
         return question.answers.filter_state(deleted=False)
 class AnonymousUser(DjangoAnonymousUser):
     def get_visible_answers(self, question):
         return question.answers.filter_state(deleted=False)
@@ -94,6 +91,16 @@ def true_if_is_super_or_staff(fn):
 
     return decorated
 
 
     return decorated
 
+def false_if_validation_required_to(item):
+    def decorator(fn):
+        def decorated(self, *args, **kwargs):
+            if item in settings.REQUIRE_EMAIL_VALIDATION_TO and not self.email_isvalid:
+                return False
+            else:
+                return fn(self, *args, **kwargs)
+        return decorated
+    return decorator
+
 class User(BaseModel, DjangoUser):
     is_approved = models.BooleanField(default=False)
     email_isvalid = models.BooleanField(default=False)
 class User(BaseModel, DjangoUser):
     is_approved = models.BooleanField(default=False)
     email_isvalid = models.BooleanField(default=False)
@@ -115,11 +122,39 @@ class User(BaseModel, DjangoUser):
     vote_up_count = DenormalizedField("actions", canceled=False, action_type="voteup")
     vote_down_count = DenormalizedField("actions", canceled=False, action_type="votedown")
 
     vote_up_count = DenormalizedField("actions", canceled=False, action_type="voteup")
     vote_down_count = DenormalizedField("actions", canceled=False, action_type="votedown")
 
-    objects = UserManager()
-
     def __unicode__(self):
         return self.username
 
     def __unicode__(self):
         return self.username
 
+    @property
+    def prop(self):
+        prop = self.__dict__.get('_prop', None)
+
+        if prop is None:
+            prop = UserPropertyDict(self)
+            self._prop = prop
+
+        return prop
+
+    @property
+    def is_siteowner(self):
+        #todo: temporary thing, for now lets just assume that the site owner will always be the first user of the application
+        return self.id == 1
+
+    @property
+    def decorated_name(self):
+        if settings.SHOW_STATUS_DIAMONDS:
+            if self.is_superuser:
+                return u"%s \u2666\u2666" % self.username
+
+            if self.is_staff:
+                return u"%s \u2666" % self.username
+
+        return self.username
+
+    @property
+    def last_activity(self):
+        return self.actions.order_by('-action_date')[0].action_date
+
     @property
     def gravatar(self):
         return md5(self.email).hexdigest()
     @property
     def gravatar(self):
         return md5(self.email).hexdigest()
@@ -155,6 +190,18 @@ class User(BaseModel, DjangoUser):
     def get_absolute_url(self):
         return self.get_profile_url()
 
     def get_absolute_url(self):
         return self.get_profile_url()
 
+    @models.permalink
+    def get_asked_url(self):
+        return ('user_questions', (), {'mode': _('asked-by'), 'user': self.id, 'slug': slugify(self.username)})
+
+    @models.permalink
+    def get_answered_url(self):
+        return ('user_questions', (), {'mode': _('answered-by'), 'user': self.id, 'slug': slugify(self.username)})
+
+    @models.permalink
+    def get_subscribed_url(self):
+        return ('user_questions', (), {'mode': _('subscribed-by'), 'user': self.id, 'slug': slugify(self.username)})
+
     def get_profile_link(self):
         profile_link = u'<a href="%s">%s</a>' % (self.get_profile_url(), self.username)
         return mark_safe(profile_link)
     def get_profile_link(self):
         profile_link = u'<a href="%s">%s</a>' % (self.get_profile_url(), self.username)
         return mark_safe(profile_link)
@@ -192,6 +239,7 @@ class User(BaseModel, DjangoUser):
     def can_vote_down(self):
         return self.reputation >= int(settings.REP_TO_VOTE_DOWN)
 
     def can_vote_down(self):
         return self.reputation >= int(settings.REP_TO_VOTE_DOWN)
 
+    @false_if_validation_required_to('flag')
     def can_flag_offensive(self, post=None):
         if post is not None and post.author == self:
             return False
     def can_flag_offensive(self, post=None):
         if post is not None and post.author == self:
             return False
@@ -204,9 +252,10 @@ class User(BaseModel, DjangoUser):
         return self.reputation >= int(settings.REP_TO_VIEW_FLAGS)
 
     @true_if_is_super_or_staff
         return self.reputation >= int(settings.REP_TO_VIEW_FLAGS)
 
     @true_if_is_super_or_staff
+    @false_if_validation_required_to('comment')
     def can_comment(self, post):
         return self == post.author or self.reputation >= int(settings.REP_TO_COMMENT
     def can_comment(self, post):
         return self == post.author or self.reputation >= int(settings.REP_TO_COMMENT
-                                                             ) or (post.__class__.__name__ == "Answer" and self == post.question.author)
+                ) or (post.__class__.__name__ == "Answer" and self == post.question.author)
 
     @true_if_is_super_or_staff
     def can_like_comment(self, comment):
 
     @true_if_is_super_or_staff
     def can_like_comment(self, comment):
@@ -227,7 +276,7 @@ class User(BaseModel, DjangoUser):
 
     @true_if_is_super_or_staff
     def can_accept_answer(self, answer):
 
     @true_if_is_super_or_staff
     def can_accept_answer(self, answer):
-        return self == answer.question.author
+        return self == answer.question.author and (settings.USERS_CAN_ACCEPT_OWN or answer.author != answer.question.author)
 
     @true_if_is_super_or_staff
     def can_create_tags(self):
 
     @true_if_is_super_or_staff
     def can_create_tags(self):
@@ -266,12 +315,18 @@ class User(BaseModel, DjangoUser):
             return self.can_delete_comment(post)
 
         return (self == post.author and (post.__class__.__name__ == "Answer" or
             return self.can_delete_comment(post)
 
         return (self == post.author and (post.__class__.__name__ == "Answer" or
-        not post.answers.exclude(author=self).count()))
+        not post.answers.exclude(author__id=self.id).count()))
 
     @true_if_is_super_or_staff
     def can_upload_files(self):
         return self.reputation >= int(settings.REP_TO_UPLOAD)
 
 
     @true_if_is_super_or_staff
     def can_upload_files(self):
         return self.reputation >= int(settings.REP_TO_UPLOAD)
 
+    def email_valid_and_can_ask(self):
+        return 'ask' not in settings.REQUIRE_EMAIL_VALIDATION_TO or self.email_isvalid
+
+    def email_valid_and_can_answer(self):
+        return 'answer' not in settings.REQUIRE_EMAIL_VALIDATION_TO or self.email_isvalid
+
     def check_password(self, old_passwd):
         self.__dict__.update(self.__class__.objects.filter(id=self.id).values('password')[0])
         return DjangoUser.check_password(self, old_passwd)
     def check_password(self, old_passwd):
         self.__dict__.update(self.__class__.objects.filter(id=self.id).values('password')[0])
         return DjangoUser.check_password(self, old_passwd)
@@ -280,13 +335,13 @@ class User(BaseModel, DjangoUser):
     def suspension(self):
         if self.__dict__.get('_suspension_dencache_', False) != None:
             try:
     def suspension(self):
         if self.__dict__.get('_suspension_dencache_', False) != None:
             try:
-                self.__dict__['_suspension_dencache_'] = self.actions.get(action_type="suspend", canceled=False)
+                self.__dict__['_suspension_dencache_'] = self.reputes.get(action__action_type="suspend", action__canceled=False).action
             except ObjectDoesNotExist:
                 self.__dict__['_suspension_dencache_'] = None
             except MultipleObjectsReturned:
                 logging.error("Multiple suspension actions found for user %s (%s)" % (self.username, self.id))
             except ObjectDoesNotExist:
                 self.__dict__['_suspension_dencache_'] = None
             except MultipleObjectsReturned:
                 logging.error("Multiple suspension actions found for user %s (%s)" % (self.username, self.id))
-                self.__dict__['_suspension_dencache_'] = self.actions.filter(action_type="suspend", canceled=False
-                                                                             ).order_by('-action_date')[0]
+                self.__dict__['_suspension_dencache_'] = self.reputes.filter(action__action_type="suspend", action__canceled=False
+                                                                             ).order_by('-action__action_date')[0]
 
         return self.__dict__['_suspension_dencache_']
 
 
         return self.__dict__['_suspension_dencache_']
 
@@ -309,14 +364,82 @@ class User(BaseModel, DjangoUser):
     class Meta:
         app_label = 'forum'
 
     class Meta:
         app_label = 'forum'
 
+class UserProperty(BaseModel):
+    user = models.ForeignKey(User, related_name='properties')
+    key = models.CharField(max_length=16)
+    value = PickledObjectField()
+
+    class Meta:
+        app_label = 'forum'
+        unique_together = ('user', 'key')
+
+    def cache_key(self):
+        return self._generate_cache_key("%s:%s" % (self.user.id, self.key))
+
+    @classmethod
+    def infer_cache_key(cls, querydict):
+        if 'user' in querydict and 'key' in querydict:
+            return cls._generate_cache_key("%s:%s" % (querydict['user'].id, querydict['key']))
+
+        return None
+
+class UserPropertyDict(object):
+    def __init__(self, user):
+        self.__dict__['_user'] = user
+
+    def __get_property(self, name):
+        if self.__dict__.get('__%s__' % name, None):
+            return self.__dict__['__%s__' % name]
+        try:
+            user = self.__dict__['_user']
+            prop = UserProperty.objects.get(user=user, key=name)
+            self.__dict__['__%s__' % name] = prop
+            self.__dict__[name] = prop.value
+            return prop
+        except:
+            return None
+
+
+    def __getattr__(self, name):
+        if self.__dict__.get(name, None):
+            return self.__dict__[name]
+
+        prop = self.__get_property(name)
+
+        if prop:
+            return prop.value
+        else:
+            return None
+
+    def __setattr__(self, name, value):
+        current = self.__get_property(name)
+
+        if value is not None:
+            if current:
+                current.value = value
+                self.__dict__[name] = value
+                current.save(full_save=True)
+            else:
+                user = self.__dict__['_user']
+                prop = UserProperty(user=user, value=value, key=name)
+                prop.save()
+                self.__dict__[name] = value
+                self.__dict__['__%s__' % name] = prop
+        else:
+            if current:
+                current.delete()
+                del self.__dict__[name]
+                del self.__dict__['__%s__' % name]
+
+
 class SubscriptionSettings(models.Model):
 class SubscriptionSettings(models.Model):
-    user = models.OneToOneField(User, related_name='subscription_settings')
+    user = models.OneToOneField(User, related_name='subscription_settings', editable=False)
 
     enable_notifications = models.BooleanField(default=True)
 
     #notify if
     member_joins = models.CharField(max_length=1, default='n')
 
     enable_notifications = models.BooleanField(default=True)
 
     #notify if
     member_joins = models.CharField(max_length=1, default='n')
-    new_question = models.CharField(max_length=1, default='d')
+    new_question = models.CharField(max_length=1, default='n')
     new_question_watched_tags = models.CharField(max_length=1, default='i')
     subscribed_questions = models.CharField(max_length=1, default='i')
 
     new_question_watched_tags = models.CharField(max_length=1, default='i')
     subscribed_questions = models.CharField(max_length=1, default='i')
 
@@ -335,6 +458,8 @@ class SubscriptionSettings(models.Model):
     notify_comments = models.BooleanField(default=False)
     notify_accepted = models.BooleanField(default=False)
 
     notify_comments = models.BooleanField(default=False)
     notify_accepted = models.BooleanField(default=False)
 
+    send_digest = models.BooleanField(default=True)
+
     class Meta:
         app_label = 'forum'
 
     class Meta:
         app_label = 'forum'