5 from django.utils.safestring import mark_safe
\r
6 from django.utils.html import strip_tags
\r
7 from forum.utils.html import sanitize_html
\r
9 class NodeContent(models.Model):
\r
10 title = models.CharField(max_length=300)
\r
11 tagnames = models.CharField(max_length=125)
\r
12 author = models.ForeignKey(User, related_name='%(class)ss')
\r
13 body = models.TextField()
\r
21 return mark_safe(sanitize_html(markdown.markdown(self.body)))
\r
27 def tagname_list(self):
\r
29 return [name for name in self.tagnames.split(u' ')]
\r
33 def tagname_meta_generator(self):
\r
34 return u','.join([unicode(tag) for tag in self.tagname_list()])
\r
40 class NodeMetaClass(models.Model.__metaclass__):
\r
43 def __new__(cls, *args, **kwargs):
\r
44 new_cls = super(NodeMetaClass, cls).__new__(cls, *args, **kwargs)
\r
46 if not new_cls._meta.abstract and new_cls.__name__ is not 'Node':
\r
47 NodeMetaClass.types[new_cls.__name__.lower()] = new_cls
\r
52 def setup_relations(cls):
\r
53 for node_cls in NodeMetaClass.types.values():
\r
54 NodeMetaClass.setup_relation(node_cls)
\r
57 def setup_relation(cls, node_cls):
\r
58 name = node_cls.__name__.lower()
\r
61 if node_cls._meta.proxy:
\r
62 return node_cls.objects.filter(node_type=name, parent=self)
\r
64 return node_cls.objects.filter(parent=self)
\r
67 p = self.__dict__.get('_%s_cache' % name, None)
\r
69 if p is None and (self.parent is not None) and self.parent.node_type == name:
\r
70 p = self.parent.leaf
\r
71 self.__dict__['_%s_cache' % name] = p
\r
75 Node.add_to_class(name + 's', property(children))
\r
76 Node.add_to_class(name, property(parent))
\r
79 node_create = django.dispatch.Signal(providing_args=['instance'])
\r
81 class Node(BaseModel, NodeContent, DeletableContent):
\r
82 __metaclass__ = NodeMetaClass
\r
84 node_type = models.CharField(max_length=16, default='node')
\r
85 parent = models.ForeignKey('Node', related_name='children', null=True)
\r
86 abs_parent = models.ForeignKey('Node', related_name='all_children', null=True)
\r
88 added_at = models.DateTimeField(default=datetime.datetime.now)
\r
90 tags = models.ManyToManyField('Tag', related_name='%(class)ss')
\r
92 score = models.IntegerField(default=0)
\r
93 vote_up_count = models.IntegerField(default=0)
\r
94 vote_down_count = models.IntegerField(default=0)
\r
96 comment_count = models.PositiveIntegerField(default=0)
\r
97 offensive_flag_count = models.SmallIntegerField(default=0)
\r
99 last_edited_at = models.DateTimeField(null=True, blank=True)
\r
100 last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_%(class)ss')
\r
102 active_revision = models.OneToOneField('NodeRevision', related_name='active', null=True)
\r
106 return NodeMetaClass.types[self.node_type].objects.get(id=self.id)
\r
109 def absolute_parent(self):
\r
110 if not self.abs_parent_id:
\r
113 return self.abs_parent.leaf
\r
117 return strip_tags(self.html)[:300]
\r
119 def create_revision(self, user, **kwargs):
\r
120 revision = NodeRevision(author=user, **kwargs)
\r
125 revision.revision = 1
\r
127 revision.revision = self.revisions.aggregate(last=models.Max('revision'))['last'] + 1
\r
129 revision.node_id = self.id
\r
131 self.activate_revision(user, revision)
\r
133 def activate_revision(self, user, revision):
\r
134 self.title = revision.title
\r
135 self.tagnames = revision.tagnames
\r
136 self.body = revision.body
\r
138 old_revision = self.active_revision
\r
140 self.active_revision = revision
\r
143 if not old_revision:
\r
144 self.last_edited_at = datetime.datetime.now()
\r
145 self.last_edited_by = user
\r
146 node_create.send(sender=self.__class__, instance=self)
\r
148 def get_tag_list_if_changed(self):
\r
149 dirty = self.get_dirty_fields()
\r
151 if 'tagnames' in dirty:
\r
152 new_tags = self.tagname_list()
\r
153 old_tags = dirty['tagnames']
\r
155 if old_tags is None or not old_tags:
\r
158 old_tags = [name for name in dirty['tagnames'].split(u' ')]
\r
162 for name in new_tags:
\r
164 tag = Tag.objects.get(name=name)
\r
166 tag = Tag.objects.create(name=name, created_by=self.last_edited_by or self.author)
\r
168 tag_list.append(tag)
\r
170 if not name in old_tags:
\r
171 tag.used_count = tag.used_count + 1
\r
173 tag.unmark_deleted()
\r
176 for name in [n for n in old_tags if not n in new_tags]:
\r
177 tag = Tag.objects.get(name=name)
\r
178 tag.used_count = tag.used_count - 1
\r
179 if tag.used_count == 0:
\r
180 tag.mark_deleted(self.last_edited_by or self.author)
\r
187 def save(self, *args, **kwargs):
\r
189 self.node_type = self.__class__.__name__.lower()
\r
191 if self.parent_id and not self.abs_parent_id:
\r
192 self.abs_parent = self.parent.absolute_parent
\r
194 tags = self.get_tag_list_if_changed()
\r
195 super(Node, self).save(*args, **kwargs)
\r
196 if tags is not None: self.tags = tags
\r
199 app_label = 'forum'
\r
202 class NodeRevision(NodeContent):
\r
203 node = models.ForeignKey(Node, related_name='revisions')
\r
204 summary = models.CharField(max_length=300)
\r
205 revision = models.PositiveIntegerField()
\r
206 revised_at = models.DateTimeField(default=datetime.datetime.now)
\r
209 unique_together = ('node', 'revision')
\r
210 app_label = 'forum'
\r
213 from user import ValidationHash
\r
215 class AnonymousNode(Node):
\r
216 validation_hash = models.ForeignKey(Node, related_name='anonymous_content')
\r
217 convertible_to = models.CharField(max_length=16, default='node')
\r