|
1 import datetime |
|
2 from django.contrib.auth.models import User |
|
3 from django.contrib.comments.managers import CommentManager |
|
4 from django.contrib.contenttypes import generic |
|
5 from django.contrib.contenttypes.models import ContentType |
|
6 from django.contrib.sites.models import Site |
|
7 from django.db import models |
|
8 from django.core import urlresolvers |
|
9 from django.utils.translation import ugettext_lazy as _ |
|
10 from django.conf import settings |
|
11 |
|
12 COMMENT_MAX_LENGTH = getattr(settings,'COMMENT_MAX_LENGTH',3000) |
|
13 |
|
14 class BaseCommentAbstractModel(models.Model): |
|
15 """ |
|
16 An abstract base class that any custom comment models probably should |
|
17 subclass. |
|
18 """ |
|
19 |
|
20 # Content-object field |
|
21 content_type = models.ForeignKey(ContentType, |
|
22 verbose_name=_('content type'), |
|
23 related_name="content_type_set_for_%(class)s") |
|
24 object_pk = models.TextField(_('object ID')) |
|
25 content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk") |
|
26 |
|
27 # Metadata about the comment |
|
28 site = models.ForeignKey(Site) |
|
29 |
|
30 class Meta: |
|
31 abstract = True |
|
32 |
|
33 def get_content_object_url(self): |
|
34 """ |
|
35 Get a URL suitable for redirecting to the content object. |
|
36 """ |
|
37 return urlresolvers.reverse( |
|
38 "comments-url-redirect", |
|
39 args=(self.content_type_id, self.object_pk) |
|
40 ) |
|
41 |
|
42 class Comment(BaseCommentAbstractModel): |
|
43 """ |
|
44 A user comment about some object. |
|
45 """ |
|
46 |
|
47 # Who posted this comment? If ``user`` is set then it was an authenticated |
|
48 # user; otherwise at least user_name should have been set and the comment |
|
49 # was posted by a non-authenticated user. |
|
50 user = models.ForeignKey(User, verbose_name=_('user'), |
|
51 blank=True, null=True, related_name="%(class)s_comments") |
|
52 user_name = models.CharField(_("user's name"), max_length=50, blank=True) |
|
53 user_email = models.EmailField(_("user's email address"), blank=True) |
|
54 user_url = models.URLField(_("user's URL"), blank=True) |
|
55 |
|
56 comment = models.TextField(_('comment'), max_length=COMMENT_MAX_LENGTH) |
|
57 |
|
58 # Metadata about the comment |
|
59 submit_date = models.DateTimeField(_('date/time submitted'), default=None) |
|
60 ip_address = models.IPAddressField(_('IP address'), blank=True, null=True) |
|
61 is_public = models.BooleanField(_('is public'), default=True, |
|
62 help_text=_('Uncheck this box to make the comment effectively ' \ |
|
63 'disappear from the site.')) |
|
64 is_removed = models.BooleanField(_('is removed'), default=False, |
|
65 help_text=_('Check this box if the comment is inappropriate. ' \ |
|
66 'A "This comment has been removed" message will ' \ |
|
67 'be displayed instead.')) |
|
68 |
|
69 # Manager |
|
70 objects = CommentManager() |
|
71 |
|
72 class Meta: |
|
73 db_table = "django_comments" |
|
74 ordering = ('submit_date',) |
|
75 permissions = [("can_moderate", "Can moderate comments")] |
|
76 verbose_name = _('comment') |
|
77 verbose_name_plural = _('comments') |
|
78 |
|
79 def __unicode__(self): |
|
80 return "%s: %s..." % (self.name, self.comment[:50]) |
|
81 |
|
82 def save(self, force_insert=False, force_update=False): |
|
83 if self.submit_date is None: |
|
84 self.submit_date = datetime.datetime.now() |
|
85 super(Comment, self).save(force_insert, force_update) |
|
86 |
|
87 def _get_userinfo(self): |
|
88 """ |
|
89 Get a dictionary that pulls together information about the poster |
|
90 safely for both authenticated and non-authenticated comments. |
|
91 |
|
92 This dict will have ``name``, ``email``, and ``url`` fields. |
|
93 """ |
|
94 if not hasattr(self, "_userinfo"): |
|
95 self._userinfo = { |
|
96 "name" : self.user_name, |
|
97 "email" : self.user_email, |
|
98 "url" : self.user_url |
|
99 } |
|
100 if self.user_id: |
|
101 u = self.user |
|
102 if u.email: |
|
103 self._userinfo["email"] = u.email |
|
104 |
|
105 # If the user has a full name, use that for the user name. |
|
106 # However, a given user_name overrides the raw user.username, |
|
107 # so only use that if this comment has no associated name. |
|
108 if u.get_full_name(): |
|
109 self._userinfo["name"] = self.user.get_full_name() |
|
110 elif not self.user_name: |
|
111 self._userinfo["name"] = u.username |
|
112 return self._userinfo |
|
113 userinfo = property(_get_userinfo, doc=_get_userinfo.__doc__) |
|
114 |
|
115 def _get_name(self): |
|
116 return self.userinfo["name"] |
|
117 def _set_name(self, val): |
|
118 if self.user_id: |
|
119 raise AttributeError(_("This comment was posted by an authenticated "\ |
|
120 "user and thus the name is read-only.")) |
|
121 self.user_name = val |
|
122 name = property(_get_name, _set_name, doc="The name of the user who posted this comment") |
|
123 |
|
124 def _get_email(self): |
|
125 return self.userinfo["email"] |
|
126 def _set_email(self, val): |
|
127 if self.user_id: |
|
128 raise AttributeError(_("This comment was posted by an authenticated "\ |
|
129 "user and thus the email is read-only.")) |
|
130 self.user_email = val |
|
131 email = property(_get_email, _set_email, doc="The email of the user who posted this comment") |
|
132 |
|
133 def _get_url(self): |
|
134 return self.userinfo["url"] |
|
135 def _set_url(self, val): |
|
136 self.user_url = val |
|
137 url = property(_get_url, _set_url, doc="The URL given by the user who posted this comment") |
|
138 |
|
139 def get_absolute_url(self, anchor_pattern="#c%(id)s"): |
|
140 return self.get_content_object_url() + (anchor_pattern % self.__dict__) |
|
141 |
|
142 def get_as_text(self): |
|
143 """ |
|
144 Return this comment as plain text. Useful for emails. |
|
145 """ |
|
146 d = { |
|
147 'user': self.user or self.name, |
|
148 'date': self.submit_date, |
|
149 'comment': self.comment, |
|
150 'domain': self.site.domain, |
|
151 'url': self.get_absolute_url() |
|
152 } |
|
153 return _('Posted by %(user)s at %(date)s\n\n%(comment)s\n\nhttp://%(domain)s%(url)s') % d |
|
154 |
|
155 class CommentFlag(models.Model): |
|
156 """ |
|
157 Records a flag on a comment. This is intentionally flexible; right now, a |
|
158 flag could be: |
|
159 |
|
160 * A "removal suggestion" -- where a user suggests a comment for (potential) removal. |
|
161 |
|
162 * A "moderator deletion" -- used when a moderator deletes a comment. |
|
163 |
|
164 You can (ab)use this model to add other flags, if needed. However, by |
|
165 design users are only allowed to flag a comment with a given flag once; |
|
166 if you want rating look elsewhere. |
|
167 """ |
|
168 user = models.ForeignKey(User, verbose_name=_('user'), related_name="comment_flags") |
|
169 comment = models.ForeignKey(Comment, verbose_name=_('comment'), related_name="flags") |
|
170 flag = models.CharField(_('flag'), max_length=30, db_index=True) |
|
171 flag_date = models.DateTimeField(_('date'), default=None) |
|
172 |
|
173 # Constants for flag types |
|
174 SUGGEST_REMOVAL = "removal suggestion" |
|
175 MODERATOR_DELETION = "moderator deletion" |
|
176 MODERATOR_APPROVAL = "moderator approval" |
|
177 |
|
178 class Meta: |
|
179 db_table = 'django_comment_flags' |
|
180 unique_together = [('user', 'comment', 'flag')] |
|
181 verbose_name = _('comment flag') |
|
182 verbose_name_plural = _('comment flags') |
|
183 |
|
184 def __unicode__(self): |
|
185 return "%s flag of comment ID %s by %s" % \ |
|
186 (self.flag, self.comment_id, self.user.username) |
|
187 |
|
188 def save(self, force_insert=False, force_update=False): |
|
189 if self.flag_date is None: |
|
190 self.flag_date = datetime.datetime.now() |
|
191 super(CommentFlag, self).save(force_insert, force_update) |