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
80 node_edit = django.dispatch.Signal(providing_args=['instance'])
\r
82 class Node(BaseModel, NodeContent, DeletableContent):
\r
83 __metaclass__ = NodeMetaClass
\r
85 node_type = models.CharField(max_length=16, default='node')
\r
86 parent = models.ForeignKey('Node', related_name='children', null=True)
\r
87 abs_parent = models.ForeignKey('Node', related_name='all_children', null=True)
\r
89 added_at = models.DateTimeField(default=datetime.datetime.now)
\r
91 tags = models.ManyToManyField('Tag', related_name='%(class)ss')
\r
93 score = DenormalizedField(default=0)
\r
94 vote_up_count = DenormalizedField(default=0)
\r
95 vote_down_count = models.IntegerField(default=0)
\r
97 comment_count = DenormalizedField(default=0)
\r
98 offensive_flag_count = DenormalizedField(default=0)
\r
100 last_edited_at = models.DateTimeField(null=True, blank=True)
\r
101 last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_%(class)ss')
\r
103 active_revision = models.OneToOneField('NodeRevision', related_name='active', null=True)
\r
107 return NodeMetaClass.types[self.node_type].objects.get(id=self.id)
\r
110 def absolute_parent(self):
\r
111 if not self.abs_parent_id:
\r
114 return self.abs_parent.leaf
\r
118 return strip_tags(self.html)[:300]
\r
120 def create_revision(self, user, **kwargs):
\r
121 revision = NodeRevision(author=user, **kwargs)
\r
126 revision.revision = 1
\r
128 revision.revision = self.revisions.aggregate(last=models.Max('revision'))['last'] + 1
\r
130 revision.node_id = self.id
\r
132 self.activate_revision(user, revision)
\r
134 def activate_revision(self, user, revision):
\r
135 self.title = revision.title
\r
136 self.tagnames = revision.tagnames
\r
137 self.body = revision.body
\r
139 old_revision = self.active_revision
\r
140 self.active_revision = revision
\r
142 if not old_revision:
\r
143 signal = node_create
\r
145 self.last_edited_at = datetime.datetime.now()
\r
146 self.last_edited_by = user
\r
150 signal.send(sender=self.__class__, instance=self)
\r
152 def get_tag_list_if_changed(self):
\r
153 dirty = self.get_dirty_fields()
\r
155 if 'tagnames' in dirty:
\r
156 new_tags = self.tagname_list()
\r
157 old_tags = dirty['tagnames']
\r
159 if old_tags is None or not old_tags:
\r
162 old_tags = [name for name in dirty['tagnames'].split(u' ')]
\r
166 for name in new_tags:
\r
168 tag = Tag.objects.get(name=name)
\r
170 tag = Tag.objects.create(name=name, created_by=self.last_edited_by or self.author)
\r
172 tag_list.append(tag)
\r
174 if not name in old_tags:
\r
175 tag.used_count = tag.used_count + 1
\r
177 tag.unmark_deleted()
\r
180 for name in [n for n in old_tags if not n in new_tags]:
\r
181 tag = Tag.objects.get(name=name)
\r
182 tag.used_count = tag.used_count - 1
\r
183 if tag.used_count == 0:
\r
184 tag.mark_deleted(self.last_edited_by or self.author)
\r
191 def save(self, *args, **kwargs):
\r
193 self.node_type = self.__class__.__name__.lower()
\r
195 if self.parent_id and not self.abs_parent_id:
\r
196 self.abs_parent = self.parent.absolute_parent
\r
198 self.__dict__['score'] = self.__dict__['vote_up_count'] - self.__dict__['vote_down_count']
\r
200 tags = self.get_tag_list_if_changed()
\r
201 super(Node, self).save(*args, **kwargs)
\r
202 if tags is not None: self.tags = tags
\r
205 app_label = 'forum'
\r
208 class NodeRevision(BaseModel, NodeContent):
\r
209 node = models.ForeignKey(Node, related_name='revisions')
\r
210 summary = models.CharField(max_length=300)
\r
211 revision = models.PositiveIntegerField()
\r
212 revised_at = models.DateTimeField(default=datetime.datetime.now)
\r
215 unique_together = ('node', 'revision')
\r
216 app_label = 'forum'
\r
219 from user import ValidationHash
\r
221 class AnonymousNode(Node):
\r
222 validation_hash = models.ForeignKey(Node, related_name='anonymous_content')
\r
223 convertible_to = models.CharField(max_length=16, default='node')
\r