3
from datetime import datetime
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 _
19
from tagging.fields import TagField
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
35
"""Proxy Model around User"""
37
objects = models.Manager()
38
published = AuthorPublishedManager()
40
def entries_published_set(self):
41
"""Return only the entries published"""
42
return entries_published(self.entry_set)
45
def get_absolute_url(self):
46
"""Return author's URL"""
47
return ('zinnia_author_detail', (self.username,))
54
class Category(models.Model):
55
"""Category object for Entry"""
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)
62
parent = models.ForeignKey('self', null=True, blank=True,
63
verbose_name=_('parent category'),
64
related_name='children')
66
def entries_published_set(self):
67
"""Return only the entries published"""
68
return entries_published(self.entry_set)
72
"""Return category's tree path, by his ancestors"""
74
return '%s/%s' % (self.parent.tree_path, self.slug)
77
def __unicode__(self):
81
def get_absolute_url(self):
82
"""Return category's URL"""
83
return ('zinnia_category_detail', (self.tree_path,))
88
verbose_name = _('category')
89
verbose_name_plural = _('categories')
92
class EntryAbstractClass(models.Model):
93
"""Base Model design for publishing entries"""
94
STATUS_CHOICES = ((DRAFT, _('draft')),
95
(HIDDEN, _('hidden')),
96
(PUBLISHED, _('published')))
98
title = models.CharField(_('title'), max_length=255)
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'))
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)
112
slug = models.SlugField(help_text=_('used for publication'),
113
unique_for_date='creation_date',
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)
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))
132
sites = models.ManyToManyField(Site, verbose_name=_('sites publication'))
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'))
139
template = models.CharField(_('template'), max_length=250,
140
default='zinnia/entry_detail.html',
141
choices=[('zinnia/entry_detail.html',
142
_('Default template'))] +
144
help_text=_('template used to display the entry'))
146
objects = models.Manager()
147
published = EntryPublishedManager()
150
def html_content(self):
151
"""Return the content correctly formatted"""
152
if not '</p>' in self.content:
153
return linebreaks(self.content)
157
def previous_entry(self):
158
"""Return the previous entry"""
159
entries = Entry.published.filter(
160
creation_date__lt=self.creation_date)[:1]
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]
173
def word_count(self):
174
"""Count the words of an entry"""
175
return len(strip_tags(self.html_content).split())
179
"""Check if an entry is within publication period"""
181
return now >= self.start_publication and now < self.end_publication
184
def is_visible(self):
185
"""Check if an entry is visible on site"""
186
return self.is_actual and self.status == PUBLISHED
189
def related_published_set(self):
190
"""Return only related entries published"""
191
return entries_published(self.related)
194
def discussions(self):
195
"""Return published discussions"""
196
return Comment.objects.for_model(self).filter(is_public=True)
200
"""Return published comments"""
201
return self.discussions.filter(Q(flags=None) | Q(
202
flags__flag=CommentFlag.MODERATOR_APPROVAL))
206
"""Return published pingbacks"""
207
return self.discussions.filter(flags__flag='pingback')
210
def trackbacks(self):
211
"""Return published trackbacks"""
212
return self.discussions.filter(flags__flag='trackback')
216
"""Return the entry's short url"""
220
from django_bitly.models import Bittle
222
bittle = Bittle.objects.bitlify(self)
223
url = bittle and bittle.shortUrl or self.get_absolute_url()
226
def __unicode__(self):
227
return '%s: %s' % (self.title, self.get_status_display())
230
def get_absolute_url(self):
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'),
243
"""Determine the base Model to inherit in the
244
Entry Model, this allow to overload it."""
245
if not ENTRY_BASE_MODEL:
246
return EntryAbstractClass
248
dot = ENTRY_BASE_MODEL.rindex('.')
249
module_name, class_name = ENTRY_BASE_MODEL[:dot], ENTRY_BASE_MODEL[dot + 1:]
251
_class = getattr(import_module(module_name), class_name)
253
except (ImportError, AttributeError):
254
warnings.warn('%s cannot be imported' % ENTRY_BASE_MODEL, RuntimeWarning)
255
return EntryAbstractClass
258
class Entry(get_base_model()):
259
"""Final Entry model"""
263
ordering = ['-creation_date']
264
verbose_name = _('entry')
265
verbose_name_plural = _('entries')
266
permissions = (('can_view_all', 'Can view all'),
267
('can_change_author', 'Can change author'), )
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')