1 """Models of Zinnia"""
2 import warnings
3 from datetime import datetime
4
5 from django.db import models
6 from django.db.models import Q
7 from django.utils.html import strip_tags
8 from django.utils.html import linebreaks
9 from django.contrib.auth.models import User
10 from django.contrib.sites.models import Site
11 from django.db.models.signals import post_save
12 from django.utils.importlib import import_module
13 from django.contrib.comments.models import Comment
14 from django.contrib.comments.models import CommentFlag
15 from django.contrib.comments.moderation import moderator
16 from django.utils.translation import ugettext_lazy as _
17
18 import mptt
19 from tagging.fields import TagField
20
21 from zinnia.settings import USE_BITLY
22 from zinnia.settings import UPLOAD_TO
23 from zinnia.settings import ENTRY_TEMPLATES
24 from zinnia.settings import ENTRY_BASE_MODEL
25 from zinnia.managers import entries_published
26 from zinnia.managers import EntryPublishedManager
27 from zinnia.managers import AuthorPublishedManager
28 from zinnia.managers import DRAFT, HIDDEN, PUBLISHED
29 from zinnia.moderator import EntryCommentModerator
30 from zinnia.signals import ping_directories_handler
31 from zinnia.signals import ping_external_urls_handler
52
55 """Category object for Entry"""
56
57 title = models.CharField(_('title'), max_length=255)
58 slug = models.SlugField(help_text=_('used for publication'),
59 unique=True, max_length=255)
60 description = models.TextField(_('description'), blank=True)
61
62 parent = models.ForeignKey('self', null=True, blank=True,
63 verbose_name=_('parent category'),
64 related_name='children')
65
69
70 @property
72 """Return category's tree path, by his ancestors"""
73 if self.parent:
74 return '%s/%s' % (self.parent.tree_path, self.slug)
75 return self.slug
76
79
80 @models.permalink
82 """Return category's URL"""
83 return ('zinnia_category_detail', (self.tree_path,))
84
90
91
92 -class EntryAbstractClass(models.Model):
93 """Base Model design for publishing entries"""
94 STATUS_CHOICES = ((DRAFT, _('draft')),
95 (HIDDEN, _('hidden')),
96 (PUBLISHED, _('published')))
97
98 title = models.CharField(_('title'), max_length=255)
99
100 image = models.ImageField(_('image'), upload_to=UPLOAD_TO,
101 blank=True, help_text=_('used for illustration'))
102 content = models.TextField(_('content'))
103 excerpt = models.TextField(_('excerpt'), blank=True,
104 help_text=_('optional element'))
105
106 tags = TagField(_('tags'))
107 categories = models.ManyToManyField(Category, verbose_name=_('categories'),
108 blank=True, null=True)
109 related = models.ManyToManyField('self', verbose_name=_('related entries'),
110 blank=True, null=True)
111
112 slug = models.SlugField(help_text=_('used for publication'),
113 unique_for_date='creation_date',
114 max_length=255)
115
116 authors = models.ManyToManyField(User, verbose_name=_('authors'),
117 blank=True, null=False)
118 status = models.IntegerField(choices=STATUS_CHOICES, default=DRAFT)
119 featured = models.BooleanField(_('featured'), default=False)
120 comment_enabled = models.BooleanField(_('comment enabled'), default=True)
121 pingback_enabled = models.BooleanField(_('linkback enabled'), default=True)
122
123 creation_date = models.DateTimeField(_('creation date'), default=datetime.now)
124 last_update = models.DateTimeField(_('last update'), default=datetime.now)
125 start_publication = models.DateTimeField(_('start publication'),
126 help_text=_('date start publish'),
127 default=datetime.now)
128 end_publication = models.DateTimeField(_('end publication'),
129 help_text=_('date end publish'),
130 default=datetime(2042, 3, 15))
131
132 sites = models.ManyToManyField(Site, verbose_name=_('sites publication'))
133
134 login_required = models.BooleanField(_('login required'), default=False,
135 help_text=_('only authenticated users can view the entry'))
136 password = models.CharField(_('password'), max_length=50, blank=True,
137 help_text=_('protect the entry with a password'))
138
139 template = models.CharField(_('template'), max_length=250,
140 default='zinnia/entry_detail.html',
141 choices=[('zinnia/entry_detail.html',
142 _('Default template'))] +
143 ENTRY_TEMPLATES,
144 help_text=_('template used to display the entry'))
145
146 objects = models.Manager()
147 published = EntryPublishedManager()
148
149 @property
150 - def html_content(self):
151 """Return the content correctly formatted"""
152 if not '</p>' in self.content:
153 return linebreaks(self.content)
154 return self.content
155
156 @property
157 - def previous_entry(self):
158 """Return the previous entry"""
159 entries = Entry.published.filter(
160 creation_date__lt=self.creation_date)[:1]
161 if entries:
162 return entries[0]
163
164 @property
165 - def next_entry(self):
166 """Return the next entry"""
167 entries = Entry.published.filter(
168 creation_date__gt=self.creation_date).order_by('creation_date')[:1]
169 if entries:
170 return entries[0]
171
172 @property
173 - def word_count(self):
174 """Count the words of an entry"""
175 return len(strip_tags(self.html_content).split())
176
177 @property
178 - def is_actual(self):
179 """Check if an entry is within publication period"""
180 now = datetime.now()
181 return now >= self.start_publication and now < self.end_publication
182
183 @property
184 - def is_visible(self):
185 """Check if an entry is visible on site"""
186 return self.is_actual and self.status == PUBLISHED
187
188 @property
192
193 @property
194 - def discussions(self):
195 """Return published discussions"""
196 return Comment.objects.for_model(self).filter(is_public=True)
197
198 @property
200 """Return published comments"""
201 return self.discussions.filter(Q(flags=None) | Q(
202 flags__flag=CommentFlag.MODERATOR_APPROVAL))
203
204 @property
205 - def pingbacks(self):
206 """Return published pingbacks"""
207 return self.discussions.filter(flags__flag='pingback')
208
209 @property
210 - def trackbacks(self):
211 """Return published trackbacks"""
212 return self.discussions.filter(flags__flag='trackback')
213
214 @property
215 - def short_url(self):
216 """Return the entry's short url"""
217 if not USE_BITLY:
218 return False
219
220 from django_bitly.models import Bittle
221
222 bittle = Bittle.objects.bitlify(self)
223 url = bittle and bittle.shortUrl or self.get_absolute_url()
224 return url
225
226 - def __unicode__(self):
227 return '%s: %s' % (self.title, self.get_status_display())
228
229 @models.permalink
231 """Return entry's URL"""
232 return ('zinnia_entry_detail', (), {
233 'year': self.creation_date.strftime('%Y'),
234 'month': self.creation_date.strftime('%m'),
235 'day': self.creation_date.strftime('%d'),
236 'slug': self.slug})
237
240
256
257
258 -class Entry(get_base_model()):
259 """Final Entry model"""
260
268
269
270 moderator.register(Entry, EntryCommentModerator)
271 mptt.register(Category, order_insertion_by=['title'])
272 post_save.connect(ping_directories_handler, sender=Entry,
273 dispatch_uid='zinnia.entry.post_save.ping_directories')
274 post_save.connect(ping_external_urls_handler, sender=Entry,
275 dispatch_uid='zinnia.entry.post_save.ping_external_urls')
276