/
models.py
282 lines (234 loc) · 10.9 KB
/
models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
#-*- coding: utf-8 -*-
from django.db import models
from django.db.models import permalink, Q
from django.db.models.signals import post_delete
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.core.cache import cache
from django.utils.safestring import mark_safe
try:
from django.utils.timezone import now
except ImportError:
from easy_blog.utils import now
from django.utils.translation import ugettext_lazy as _
from django_markup.fields import MarkupField
from django_markup.markup import formatter
from inline_media.fields import TextFieldWithInlines
from inline_media.utils import unescape_inline
from tagging.fields import TagField
from tagging.models import TaggedItem
from tagging.utils import get_tag_list
from easy_blog.utils import create_cache_key
class DefaultConfig(object):
def __init__(self, data=None):
self.data = data
def __getattr__(self, name):
try:
return self.data[name]
except KeyError:
return None
default_config = DefaultConfig(
getattr(
settings, "EASY_BLOG_DEFAULT_CONFIG",
{ "site": 1,
"title": u"Your Easy Blog",
"stories_in_index": 5,
"comments_in_index": 2,
"email_subscribe_url": u"",
"show_author": True,
"ping_google": False,
"excerpt_length": 500,
"meta_author": u"",
"meta_keywords": u"",
"meta_description": u""}))
STATUS_CHOICES = ((1, _("Draft")), (2, _("Review")), (3, _("Public")),)
class Config(models.Model):
"""Django-easy-blog configuration"""
site = models.ForeignKey(Site, unique=True, related_name="+")
title = models.CharField(max_length=100, help_text=_(
"Blog's name or title"))
stories_in_index = models.IntegerField(default=5, help_text=_(
"List of stories in the front page."))
comments_in_index = models.IntegerField(default=5, help_text=_(
"List of last comments in the front page."))
email_subscribe_url = models.URLField(_("subscribe via email url"),
blank=True, null=True)
show_author = models.BooleanField(default=False, help_text=_(
"Show author's full name along in posts"))
ping_google = models.BooleanField(default=False, help_text=_(
"Notify Google on new submissions"))
excerpt_length = models.IntegerField(default=500, help_text=_(
"The character length of the post body field displayed in RSS "
"and preview templates."))
meta_author = models.CharField(max_length=255, help_text=_(
"List of authors or company/organization's name"))
meta_keywords = models.CharField(max_length=255, help_text=_(
"List of keywords to help improve quality of search results"))
meta_description = models.TextField(blank=True, help_text=_(
"What the blog is about, topics, editorial line..."))
class Meta:
db_table = "easy_blog_config"
verbose_name = _("app config")
verbose_name_plural = _("app config")
def __unicode__(self):
return "%s easy-blog config" % self.site.name
def delete(self, *args, **kwargs):
if settings.SITE_ID != self.site.id:
super(Config, self).delete(*args, **kwargs)
def save(self, *args, **kwargs):
super(Config, self).save(*args, **kwargs)
self.site_name = self.site.name
key = create_cache_key(Config, field="site__id",
field_value=self.site.id)
cache.set(key, self)
@staticmethod
def get_current():
site = Site.objects.get_current()
key = create_cache_key(Config, field="site__id",
field_value=site.id)
config = cache.get(key, None)
if config is None:
try:
config = Config.objects.get(site=site)
cache.add(key, config)
except Config.DoesNotExist:
return default_config
return config
class StoryManager(models.Manager):
"""Returns published posts that are not in the future."""
def drafts(self, author=None):
if not author:
return self.get_queryset().filter(
status=1).order_by("-mod_date")
else:
return self.get_queryset().filter(
status=1, author=author).order_by("-mod_date")
def reviews(self, author):
if author.has_perm("easy_blog.can_review_posts"):
return self.get_queryset().filter(status=2).order_by("-mod_date")
else:
return []
def upcoming(self, author=None):
if not author:
return self.get_queryset().filter(
status=3, pub_date__gt=now()).order_by("-mod_date")
else:
return self.get_queryset().filter(
status=3, author=author, pub_date__gt=now()).order_by("-mod_date")
def published(self):
return self.get_queryset().filter(status=3, pub_date__lte=now())
def select(self, status=[3], author=None):
if min(status) < 3 and author: # show drafts anf reviews for the author
qs = self.get_queryset().filter(
status__in=status).exclude(~Q(author=author), status=1)
if not author.has_perm("dress_blog.can_review_posts"):
return qs.exclude(~Q(author=author), status=2)
else:
return qs
else:
return self.get_queryset().filter(
status__in=status, pub_date__lte=now())
_htxt = {'markup_text': u'Check <a href="http://markable.in/file/aa191728-9dc7-11e1-91c7-984be164924a/" target=_new">Markdown syntax</a>, <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html" target=_new">reStructuredText syntax</a>',
'site': _("Site in which the entry is published"),
'hint_on_markdown': _("Look at the body field for a quick Markdown cheatsheet"),
'markdown_cheatsheet': u'''
In case you need to write headers to highlight different sections along the story, you can use headers like the following.
### Header Level 3
This is a paragraph with simple content, not too long and too short, just enough to make it look like a paragraph. **This sentence has a font in bold**. *And this is in italics*. A markdown link looks like this: [Link to Google](http://www.google.com). But use HTML links at will too, specially when you want them to be opened in a different window: <A href="http://www.google.com" target="_new">Google in a new window</a>.
<div style="margin:10px auto;text-align:center">Paste here your youtube or google docs content</div>
This is a regular list:
* The first element of the list
* The second element of the list
* The third element of the list
And this a numbered list:
1. The first
1. The second
1. The third
Remember, you can separate paragraphs with horizontal rules, by simply typing three asterisks.
***
So this paragraph will be below a horizontal rule.
If you want to quote a text, as when you make a citation, use the following:
> A small step for man a giant leap for mankind.
Enjoy markdown!
'''}
class Story(models.Model):
"""A generic story."""
title = models.CharField(max_length=200)
slug = models.SlugField(unique_for_date="pub_date")
markup = MarkupField(default="markdown")
abstract = TextFieldWithInlines(help_text=_htxt["markup_text"],
default=_htxt["hint_on_markdown"])
abstract_markup = models.TextField(editable=True, blank=True, null=True)
body = TextFieldWithInlines(help_text=_htxt["markup_text"],
default=_htxt["markdown_cheatsheet"])
body_markup = models.TextField(editable=True, blank=True, null=True)
tags = TagField()
status = models.IntegerField(choices=STATUS_CHOICES, default=1)
author = models.ForeignKey(User, blank=True, null=True)
allow_comments = models.BooleanField(default=True)
pub_date = models.DateTimeField(_("Publication date"), default=now)
mod_date = models.DateTimeField(_("Modification date"), auto_now=True)
visits = models.IntegerField(default=0, editable=False)
site = models.ForeignKey(Site, help_text=_htxt['site'])
objects = StoryManager()
class Meta:
verbose_name = _("story")
verbose_name_plural = _("stories")
db_table = "easy_blog_stories"
ordering = ("-pub_date",)
get_latest_by = "pub_date"
permissions = (("can_review_stories", "Can review stories"),
("can_see_unpublished_stories", "Can see unpublished stories"))
def __unicode__(self):
return u"%s" % self.title
def save(self, *args, **kwargs):
self.site_id = settings.SITE_ID
self.abstract_markup = mark_safe(
formatter(self.abstract, filter_name=self.markup))
self.body_markup = mark_safe(
formatter(self.body, filter_name=self.markup))
if self.markup == "restructuredtext":
self.abstract_markup = unescape_inline(self.abstract_markup)
self.body_markup = unescape_inline(self.body_markup)
super(Story, self).save(*args, **kwargs)
if self.status == 3: # public
blog_config = Config.get_current()
ping_google = getattr(blog_config, "ping_google", False)
if ping_google:
try:
ping_google()
except:
pass
@permalink
def get_absolute_url(self):
kwargs = { "year": self.pub_date.year,
"month": self.pub_date.strftime("%b").lower(),
"day": self.pub_date.day,
"slug": self.slug }
if self.status == 1:
return ("blog-story-detail-draft", None, kwargs)
if self.status == 2:
return ("blog-story-detail-review", None, kwargs)
elif self.pub_date > now():
return ("blog-story-detail-upcoming", None, kwargs)
else:
return ("blog-story-detail", None, kwargs)
@property
def in_the_future(self):
return self.pub_date > now()
def delete_story_tags(sender, instance, **kwargs):
try:
ctype = ContentType.objects.get_for_model(instance)
tags = get_tag_list(instance.tags)
TaggedItem._default_manager.filter(content_type__pk=ctype.pk,
object_id=instance.pk,
tag__in=tags).delete()
for tag in tags:
if not tag.items.count():
tag.delete()
except Exception, e:
# let 'django.request' logger handle the exception
raise e
post_delete.connect(delete_story_tags, sender=Story)