1 from akismet import *
\r
6 from django.utils.safestring import mark_safe
\r
7 from django.utils.html import strip_tags
\r
8 from forum.utils.html import sanitize_html
\r
10 class NodeContent(models.Model):
\r
11 title = models.CharField(max_length=300)
\r
12 tagnames = models.CharField(max_length=125)
\r
13 author = models.ForeignKey(User, related_name='%(class)ss')
\r
14 body = models.TextField()
\r
22 return mark_safe(sanitize_html(markdown.markdown(self.body)))
\r
28 def tagname_list(self):
\r
30 return [name for name in self.tagnames.split(u' ')]
\r
34 def tagname_meta_generator(self):
\r
35 return u','.join([unicode(tag) for tag in self.tagname_list()])
\r
41 class NodeMetaClass(models.Model.__metaclass__):
\r
44 def __new__(cls, *args, **kwargs):
\r
45 new_cls = super(NodeMetaClass, cls).__new__(cls, *args, **kwargs)
\r
47 if not new_cls._meta.abstract and new_cls.__name__ is not 'Node':
\r
48 NodeMetaClass.types[new_cls.__name__.lower()] = new_cls
\r
53 def setup_relations(cls):
\r
54 for node_cls in NodeMetaClass.types.values():
\r
55 NodeMetaClass.setup_relation(node_cls)
\r
58 def setup_relation(cls, node_cls):
\r
59 name = node_cls.__name__.lower()
\r
62 if node_cls._meta.proxy:
\r
63 return node_cls.objects.filter(node_type=name, parent=self)
\r
65 return node_cls.objects.filter(parent=self)
\r
68 p = self.__dict__.get('_%s_cache' % name, None)
\r
70 if p is None and (self.parent is not None) and self.parent.node_type == name:
\r
71 p = self.parent.leaf
\r
72 self.__dict__['_%s_cache' % name] = p
\r
76 Node.add_to_class(name + 's', property(children))
\r
77 Node.add_to_class(name, property(parent))
\r
80 node_create = django.dispatch.Signal(providing_args=['instance'])
\r
81 node_edit = django.dispatch.Signal(providing_args=['instance'])
\r
83 class Node(BaseModel, NodeContent, DeletableContent):
\r
84 __metaclass__ = NodeMetaClass
\r
86 node_type = models.CharField(max_length=16, default='node')
\r
87 parent = models.ForeignKey('Node', related_name='children', null=True)
\r
88 abs_parent = models.ForeignKey('Node', related_name='all_children', null=True)
\r
90 added_at = models.DateTimeField(default=datetime.datetime.now)
\r
92 tags = models.ManyToManyField('Tag', related_name='%(class)ss')
\r
94 score = DenormalizedField(default=0)
\r
95 vote_up_count = DenormalizedField(default=0)
\r
96 vote_down_count = models.IntegerField(default=0)
\r
98 comment_count = DenormalizedField(default=0)
\r
99 offensive_flag_count = DenormalizedField(default=0)
\r
101 last_edited_at = models.DateTimeField(null=True, blank=True)
\r
102 last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_%(class)ss')
\r
104 active_revision = models.OneToOneField('NodeRevision', related_name='active', null=True)
\r
108 return NodeMetaClass.types[self.node_type].objects.get(id=self.id)
\r
111 def absolute_parent(self):
\r
112 if not self.abs_parent_id:
\r
115 return self.abs_parent.leaf
\r
119 return strip_tags(self.html)[:300]
\r
121 def create_revision(self, user, **kwargs):
\r
122 revision = NodeRevision(author=user, **kwargs)
\r
127 revision.revision = 1
\r
129 revision.revision = self.revisions.aggregate(last=models.Max('revision'))['last'] + 1
\r
131 revision.node_id = self.id
\r
133 self.activate_revision(user, revision)
\r
135 def activate_revision(self, user, revision):
\r
136 self.title = revision.title
\r
137 self.tagnames = revision.tagnames
\r
138 self.body = revision.body
\r
140 old_revision = self.active_revision
\r
141 self.active_revision = revision
\r
143 if not old_revision:
\r
144 signal = node_create
\r
146 self.last_edited_at = datetime.datetime.now()
\r
147 self.last_edited_by = user
\r
151 signal.send(sender=self.__class__, instance=self)
\r
153 def get_tag_list_if_changed(self):
\r
154 dirty = self.get_dirty_fields()
\r
156 if 'tagnames' in dirty:
\r
157 new_tags = self.tagname_list()
\r
158 old_tags = dirty['tagnames']
\r
160 if old_tags is None or not old_tags:
\r
163 old_tags = [name for name in dirty['tagnames'].split(u' ')]
\r
167 for name in new_tags:
\r
169 tag = Tag.objects.get(name=name)
\r
171 tag = Tag.objects.create(name=name, created_by=self.last_edited_by or self.author)
\r
173 tag_list.append(tag)
\r
175 if not name in old_tags:
\r
176 tag.used_count = tag.used_count + 1
\r
178 tag.unmark_deleted()
\r
181 for name in [n for n in old_tags if not n in new_tags]:
\r
182 tag = Tag.objects.get(name=name)
\r
183 tag.used_count = tag.used_count - 1
\r
184 if tag.used_count == 0:
\r
185 tag.mark_deleted(self.last_edited_by or self.author)
\r
192 def save(self, *args, **kwargs):
\r
194 self.node_type = self.__class__.__name__.lower()
\r
196 if self.parent_id and not self.abs_parent_id:
\r
197 self.abs_parent = self.parent.absolute_parent
\r
199 self.__dict__['score'] = self.__dict__['vote_up_count'] - self.__dict__['vote_down_count']
\r
201 tags = self.get_tag_list_if_changed()
\r
202 super(Node, self).save(*args, **kwargs)
\r
203 if tags is not None: self.tags = tags
\r
206 def isSpam(comment, data):
\r
208 if api.key is None:
\r
209 print "problem" # raise APIKeyError
\r
211 if api.comment_check(comment, data):
\r
218 app_label = 'forum'
\r
221 class NodeRevision(BaseModel, NodeContent):
\r
222 node = models.ForeignKey(Node, related_name='revisions')
\r
223 summary = models.CharField(max_length=300)
\r
224 revision = models.PositiveIntegerField()
\r
225 revised_at = models.DateTimeField(default=datetime.datetime.now)
\r
228 unique_together = ('node', 'revision')
\r
229 app_label = 'forum'
\r
232 from user import ValidationHash
\r
234 class AnonymousNode(Node):
\r
235 validation_hash = models.ForeignKey(Node, related_name='anonymous_content')
\r
236 convertible_to = models.CharField(max_length=16, default='node')
\r