]> git.openstreetmap.org Git - osqa.git/blobdiff - forum/models/node.py
Fix error reported at http://meta.osqa.net/question/1660/just-been-awarded-student...
[osqa.git] / forum / models / node.py
index 932452d9c064ceae0a7235434b24c193843edb53..3150e992e0d915e7836a3c90707ad87998573394 100644 (file)
@@ -1,7 +1,9 @@
+from forum.akismet import *\r
 from base import *\r
 from tag import Tag\r
 \r
 import markdown\r
+from django.utils.translation import ugettext as _\r
 from django.utils.safestring import mark_safe\r
 from django.utils.html import strip_tags\r
 from forum.utils.html import sanitize_html\r
@@ -12,18 +14,21 @@ class NodeContent(models.Model):
     author     = models.ForeignKey(User, related_name='%(class)ss')\r
     body       = models.TextField()\r
 \r
+    @property\r
+    def user(self):\r
+        return self.author\r
+\r
     @property\r
     def html(self):\r
-        return mark_safe(sanitize_html(markdown.markdown(self.body)))\r
+        return self.as_markdown()\r
+\r
+    def as_markdown(self, *extensions):\r
+        return mark_safe(sanitize_html(markdown.markdown(self.body, extensions=extensions)))\r
 \r
     @property\r
     def headline(self):\r
         return self.title\r
 \r
-    @property\r
-    def summary(self):\r
-        return strip_tags(self.html)[:300]\r
-\r
     def tagname_list(self):\r
         if self.tagnames:\r
             return [name for name in self.tagnames.split(u' ')]\r
@@ -37,14 +42,14 @@ class NodeContent(models.Model):
         abstract = True\r
         app_label = 'forum'\r
 \r
-class NodeMetaClass(models.Model.__metaclass__):\r
+class NodeMetaClass(BaseMetaClass):\r
     types = {}\r
 \r
     def __new__(cls, *args, **kwargs):\r
         new_cls = super(NodeMetaClass, cls).__new__(cls, *args, **kwargs)\r
 \r
         if not new_cls._meta.abstract and new_cls.__name__ is not 'Node':\r
-            NodeMetaClass.types[new_cls.__name__.lower()] = new_cls\r
+            NodeMetaClass.types[new_cls.get_type()] = new_cls\r
 \r
         return new_cls\r
 \r
@@ -63,12 +68,9 @@ class NodeMetaClass(models.Model.__metaclass__):
         def parent(self):\r
             p = self.__dict__.get('_%s_cache' % name, None)\r
 \r
-            if p is None:\r
-                try:\r
-                    p = self.parent.leaf\r
-                    self.__dict__['_%s_cache' % name] = p\r
-                except Exception, e:\r
-                    pass\r
+            if p is None and (self.parent is not None) and self.parent.node_type == name:\r
+                p = self.parent.leaf\r
+                self.__dict__['_%s_cache' % name] = p\r
 \r
             return p\r
 \r
@@ -76,59 +78,131 @@ class NodeMetaClass(models.Model.__metaclass__):
         Node.add_to_class(name, property(parent))\r
 \r
 \r
-class Node(BaseModel, NodeContent, DeletableContent):\r
+class NodeManager(CachedManager):\r
+    use_for_related_fields = True\r
+\r
+    def get_query_set(self):\r
+        qs = super(NodeManager, self).get_query_set()\r
+\r
+        if self.model is not Node:\r
+            return qs.filter(node_type=self.model.get_type())\r
+        else:\r
+            return qs\r
+\r
+    def get(self, *args, **kwargs):\r
+        node = super(NodeManager, self).get(*args, **kwargs)\r
+        cls = NodeMetaClass.types.get(node.node_type, None)\r
+\r
+        if cls and node.__class__ is not cls:\r
+            return node.leaf\r
+        return node\r
+\r
+    def get_for_types(self, types, *args, **kwargs):\r
+        kwargs['node_type__in'] = [t.get_type() for t in types]\r
+        return self.get(*args, **kwargs)\r
+\r
+\r
+class Node(BaseModel, NodeContent):\r
     __metaclass__ = NodeMetaClass\r
 \r
     node_type            = models.CharField(max_length=16, default='node')\r
     parent               = models.ForeignKey('Node', related_name='children', null=True)\r
+    abs_parent           = models.ForeignKey('Node', related_name='all_children', null=True)\r
 \r
     added_at             = models.DateTimeField(default=datetime.datetime.now)\r
+    score                 = models.IntegerField(default=0)\r
+\r
+    deleted               = models.ForeignKey('Action', null=True, unique=True, related_name="deleted_node")\r
+    in_moderation         = models.ForeignKey('Action', null=True, unique=True, related_name="moderated_node")\r
+    last_edited           = models.ForeignKey('Action', null=True, unique=True, related_name="edited_node")\r
+\r
+    last_activity_by       = models.ForeignKey(User, null=True)\r
+    last_activity_at       = models.DateTimeField(null=True, blank=True)\r
 \r
     tags                 = models.ManyToManyField('Tag', related_name='%(class)ss')\r
+    active_revision       = models.OneToOneField('NodeRevision', related_name='active', null=True)\r
 \r
-    score                 = models.IntegerField(default=0)\r
-    vote_up_count         = models.IntegerField(default=0)\r
-    vote_down_count       = models.IntegerField(default=0)\r
+    extra_ref = models.ForeignKey('Node', null=True)\r
+    extra_count = models.IntegerField(default=0)\r
+    extra_action = models.ForeignKey('Action', null=True, related_name="extra_node")\r
+    \r
+    marked = models.BooleanField(default=False)\r
+    wiki = models.BooleanField(default=False)\r
 \r
-    comment_count        = models.PositiveIntegerField(default=0)\r
-    offensive_flag_count  = models.SmallIntegerField(default=0)\r
+    comment_count = DenormalizedField("children", node_type="comment", canceled=False)\r
+    flag_count = DenormalizedField("flags")\r
 \r
-    last_edited_at        = models.DateTimeField(null=True, blank=True)\r
-    last_edited_by        = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_%(class)ss')\r
+    friendly_name = _("post")\r
 \r
-    active_revision       = models.OneToOneField('NodeRevision', related_name='active', null=True)\r
+    objects = NodeManager()\r
+\r
+    @classmethod\r
+    def cache_key(cls, pk):\r
+        return '%s.node:%s' % (settings.APP_URL, pk)\r
+\r
+    @classmethod\r
+    def get_type(cls):\r
+        return cls.__name__.lower()\r
 \r
     @property\r
     def leaf(self):\r
-        return NodeMetaClass.types[self.node_type].objects.get(id=self.id)\r
+        leaf_cls = NodeMetaClass.types.get(self.node_type, None)\r
 \r
-    def create_revision(self, user, **kwargs):\r
-        revision = NodeRevision(author=user, **kwargs)\r
-        \r
-        if not self.id:\r
-            self.author = user\r
+        if leaf_cls is None:\r
+            return self\r
+\r
+        leaf = leaf_cls()\r
+        leaf.__dict__ = self.__dict__\r
+        return leaf\r
+\r
+    @property    \r
+    def absolute_parent(self):\r
+        if not self.abs_parent_id:\r
+            return self.leaf\r
+\r
+        return self.abs_parent.leaf\r
+\r
+    @property\r
+    def summary(self):\r
+        return strip_tags(self.html)[:300]\r
+\r
+    def update_last_activity(self, user, save=False):\r
+        self.last_activity_by = user\r
+        self.last_activity_at = datetime.datetime.now()\r
+\r
+        if self.parent:\r
+            self.parent.update_last_activity(user, save=True)\r
+\r
+        if save:\r
             self.save()\r
-            revision.revision = 1\r
-        else:\r
-            revision.revision = self.revisions.aggregate(last=models.Max('revision'))['last'] + 1\r
 \r
-        revision.node_id = self.id\r
+    def _create_revision(self, user, number, **kwargs):\r
+        revision = NodeRevision(author=user, revision=number, node=self, **kwargs)\r
         revision.save()\r
-        self.activate_revision(user, revision)\r
+        return revision\r
 \r
-    def activate_revision(self, user, revision):\r
+    def create_revision(self, user, action=None, **kwargs):\r
+        number = self.revisions.aggregate(last=models.Max('revision'))['last'] + 1\r
+        revision = self._create_revision(user, number, **kwargs)\r
+        self.activate_revision(user, revision, action)\r
+        return revision\r
+\r
+    def activate_revision(self, user, revision, action=None):\r
         self.title = revision.title\r
         self.tagnames = revision.tagnames\r
         self.body = revision.body\r
-        \r
-        self.last_edited_at = datetime.datetime.now()\r
-        self.last_edited_by = user\r
 \r
         self.active_revision = revision\r
+        self.update_last_activity(user)\r
+\r
+        if action:\r
+            self.last_edited = action\r
+\r
         self.save()\r
 \r
     def get_tag_list_if_changed(self):\r
         dirty = self.get_dirty_fields()\r
+        active_user = self.last_edited and self.last_edited.by or self.author\r
 \r
         if 'tagnames' in dirty:\r
             new_tags = self.tagname_list()\r
@@ -145,7 +219,7 @@ class Node(BaseModel, NodeContent, DeletableContent):
                 try:\r
                     tag = Tag.objects.get(name=name)\r
                 except:\r
-                    tag = Tag.objects.create(name=name, created_by=self.last_edited_by or self.author)\r
+                    tag = Tag.objects.create(name=name, created_by=active_user or self.author)\r
 \r
                 tag_list.append(tag)\r
 \r
@@ -159,7 +233,7 @@ class Node(BaseModel, NodeContent, DeletableContent):
                 tag = Tag.objects.get(name=name)\r
                 tag.used_count = tag.used_count - 1\r
                 if tag.used_count == 0:\r
-                    tag.mark_deleted(self.last_edited_by or self.author)\r
+                    tag.mark_deleted(active_user)\r
                 tag.save()\r
 \r
             return tag_list\r
@@ -168,17 +242,36 @@ class Node(BaseModel, NodeContent, DeletableContent):
 \r
     def save(self, *args, **kwargs):\r
         if not self.id:\r
-            self.node_type = self.__class__.__name__.lower()\r
-            \r
+            self.node_type = self.get_type()\r
+            super(BaseModel, self).save(*args, **kwargs)\r
+            self.active_revision = self._create_revision(self.author, 1, title=self.title, tagnames=self.tagnames, body=self.body)\r
+            self.update_last_activity(self.author)\r
+\r
+        if self.parent_id and not self.abs_parent_id:\r
+            self.abs_parent = self.parent.absolute_parent\r
+        \r
         tags = self.get_tag_list_if_changed()\r
         super(Node, self).save(*args, **kwargs)\r
         if tags is not None: self.tags = tags\r
 \r
+    @staticmethod\r
+    def isSpam(comment, data):\r
+        api = Akismet()\r
+\r
+        if not api.key:\r
+            return False\r
+        else:\r
+            if api.comment_check(comment, data):\r
+                return True\r
+            else:\r
+                return False\r
+        return data\r
+\r
     class Meta:\r
         app_label = 'forum'\r
 \r
 \r
-class NodeRevision(NodeContent):\r
+class NodeRevision(BaseModel, NodeContent):\r
     node       = models.ForeignKey(Node, related_name='revisions')\r
     summary    = models.CharField(max_length=300)\r
     revision   = models.PositiveIntegerField()\r
@@ -189,11 +282,3 @@ class NodeRevision(NodeContent):
         app_label = 'forum'\r
 \r
 \r
-from user import ValidationHash\r
-\r
-class AnonymousNode(Node):\r
-    validation_hash = models.ForeignKey(Node, related_name='anonymous_content')\r
-    convertible_to = models.CharField(max_length=16, default='node')\r
-\r
-    class Meta:\r
-        app_label = 'forum'
\ No newline at end of file