1 | """Django page CMS ``models``.""" |
||
2 | |||
3 | from pages.cache import cache |
||
4 | from pages.utils import get_placeholders, normalize_url, get_now |
||
5 | from pages.managers import PageManager, ContentManager |
||
6 | from pages.managers import PageAliasManager |
||
7 | from pages import settings |
||
8 | from pages.utils import slugify |
||
9 | # checks |
||
10 | from pages import checks |
||
11 | |||
12 | from django.db import models |
||
13 | from django.conf import settings as django_settings |
||
14 | from django.utils.translation import ugettext_lazy as _ |
||
15 | from django.utils.safestring import mark_safe |
||
16 | from django.core.urlresolvers import reverse |
||
17 | from django.conf import settings as global_settings |
||
18 | from django.utils.encoding import python_2_unicode_compatible |
||
19 | |||
20 | |||
21 | from mptt.models import MPTTModel |
||
22 | import uuid |
||
23 | import os |
||
24 | |||
25 | PAGE_CONTENT_DICT_KEY = ContentManager.PAGE_CONTENT_DICT_KEY |
||
26 | |||
27 | if settings.PAGE_USE_SITE_ID: |
||
28 | from django.contrib.sites.models import Site |
||
29 | |||
30 | |||
31 | @python_2_unicode_compatible |
||
32 | class Page(MPTTModel): |
||
33 | """ |
||
34 | This model contain the status, dates, author, template. |
||
35 | The real content of the page can be found in the |
||
36 | :class:`Content <pages.models.Content>` model. |
||
37 | |||
38 | .. attribute:: creation_date |
||
39 | When the page has been created. |
||
40 | |||
41 | .. attribute:: publication_date |
||
42 | When the page should be visible. |
||
43 | |||
44 | .. attribute:: publication_end_date |
||
45 | When the publication of this page end. |
||
46 | |||
47 | .. attribute:: last_modification_date |
||
48 | Last time this page has been modified. |
||
49 | |||
50 | .. attribute:: status |
||
51 | The current status of the page. Could be DRAFT, PUBLISHED, |
||
52 | EXPIRED or HIDDEN. You should the property :attr:`calculated_status` if |
||
53 | you want that the dates are taken in account. |
||
54 | |||
55 | .. attribute:: template |
||
56 | A string containing the name of the template file for this page. |
||
57 | """ |
||
58 | |||
59 | # some class constants to refer to, e.g. Page.DRAFT |
||
60 | DRAFT = 0 |
||
61 | PUBLISHED = 1 |
||
62 | EXPIRED = 2 |
||
63 | HIDDEN = 3 |
||
64 | STATUSES = ( |
||
65 | (PUBLISHED, _('Published')), |
||
66 | (HIDDEN, _('Hidden')), |
||
67 | (DRAFT, _('Draft')), |
||
68 | ) |
||
69 | |||
70 | PAGE_LANGUAGES_KEY = "page_%d_languages" |
||
71 | PAGE_URL_KEY = "page_%d_url" |
||
72 | ANCESTORS_KEY = 'ancestors_%d' |
||
73 | CHILDREN_KEY = 'children_%d' |
||
74 | PUB_CHILDREN_KEY = 'pub_children_%d' |
||
75 | |||
76 | # used to identify pages across different databases |
||
77 | uuid = models.UUIDField(default=uuid.uuid4, editable=False) |
||
78 | |||
79 | author = models.ForeignKey(django_settings.AUTH_USER_MODEL, |
||
80 | verbose_name=_('author')) |
||
81 | |||
82 | parent = models.ForeignKey('self', null=True, blank=True, |
||
83 | related_name='children', verbose_name=_('parent')) |
||
84 | creation_date = models.DateTimeField(_('creation date'), editable=False, |
||
85 | default=get_now) |
||
86 | publication_date = models.DateTimeField(_('publication date'), |
||
87 | null=True, blank=True, help_text=_('''When the page should go |
||
88 | live. Status must be "Published" for page to go live.''')) |
||
89 | publication_end_date = models.DateTimeField(_('publication end date'), |
||
90 | null=True, blank=True, help_text=_('''When to expire the page. |
||
91 | Leave empty to never expire.''')) |
||
92 | |||
93 | last_modification_date = models.DateTimeField(_('last modification date')) |
||
94 | |||
95 | status = models.IntegerField(_('status'), choices=STATUSES, default=DRAFT) |
||
96 | template = models.CharField(_('template'), max_length=100, null=True, |
||
97 | blank=True) |
||
98 | |||
99 | delegate_to = models.CharField(_('delegate to'), max_length=100, null=True, |
||
100 | blank=True) |
||
101 | |||
102 | freeze_date = models.DateTimeField(_('freeze date'), |
||
103 | null=True, blank=True, help_text=_('''Don't publish any content |
||
104 | after this date.''')) |
||
105 | |||
106 | if settings.PAGE_USE_SITE_ID: |
||
107 | sites = models.ManyToManyField(Site, |
||
108 | default=[global_settings.SITE_ID], |
||
109 | help_text=_('The site(s) the page is accessible at.'), |
||
110 | verbose_name=_('sites')) |
||
111 | |||
112 | redirect_to_url = models.CharField(_('redirect to url'), max_length=200, null=True, blank=True) |
||
113 | |||
114 | redirect_to = models.ForeignKey('self', null=True, blank=True, |
||
115 | related_name='redirected_pages', verbose_name=_('redirect to')) |
||
116 | |||
117 | # Managers |
||
118 | objects = PageManager() |
||
119 | |||
120 | if settings.PAGE_TAGGING: |
||
121 | tags = settings.PAGE_TAGGING_FIELD() |
||
122 | |||
123 | class Meta: |
||
124 | """Make sure the default page ordering is correct.""" |
||
125 | ordering = ['tree_id', 'lft'] |
||
126 | get_latest_by = "publication_date" |
||
127 | verbose_name = _('page') |
||
128 | verbose_name_plural = _('pages') |
||
129 | permissions = settings.PAGE_EXTRA_PERMISSIONS |
||
130 | |||
131 | def __init__(self, *args, **kwargs): |
||
132 | """Instanciate the page object.""" |
||
133 | # per instance cache |
||
134 | self._languages = None |
||
135 | self._content_dict = None |
||
136 | self._is_first_root = None |
||
137 | self._complete_slug = None |
||
138 | super(Page, self).__init__(*args, **kwargs) |
||
139 | |||
140 | def save(self, *args, **kwargs): |
||
141 | """Override the default ``save`` method.""" |
||
142 | if not self.status: |
||
143 | self.status = self.DRAFT |
||
144 | # Published pages should always have a publication date |
||
145 | if self.publication_date is None and self.status == self.PUBLISHED: |
||
146 | self.publication_date = get_now() |
||
147 | # Drafts should not, unless they have been set to the future |
||
148 | if self.status == self.DRAFT: |
||
149 | if settings.PAGE_SHOW_START_DATE: |
||
150 | if (self.publication_date and |
||
151 | self.publication_date <= get_now()): |
||
152 | self.publication_date = None |
||
153 | else: |
||
154 | self.publication_date = None |
||
155 | self.last_modification_date = get_now() |
||
156 | super(Page, self).save(*args, **kwargs) |
||
157 | # fix sites many-to-many link when the're hidden from the form |
||
158 | if settings.PAGE_HIDE_SITES and self.sites.count() == 0: |
||
159 | self.sites.add(Site.objects.get(pk=global_settings.SITE_ID)) |
||
160 | |||
161 | def _get_calculated_status(self): |
||
162 | """Get the calculated status of the page based on |
||
163 | :attr:`Page.publication_date`, |
||
164 | :attr:`Page.publication_end_date`, |
||
165 | and :attr:`Page.status`.""" |
||
166 | if settings.PAGE_SHOW_START_DATE and self.publication_date: |
||
167 | if self.publication_date > get_now(): |
||
168 | return self.DRAFT |
||
169 | |||
170 | if settings.PAGE_SHOW_END_DATE and self.publication_end_date: |
||
171 | if self.publication_end_date < get_now(): |
||
172 | return self.EXPIRED |
||
173 | |||
174 | return self.status |
||
175 | calculated_status = property(_get_calculated_status) |
||
176 | |||
177 | def _visible(self): |
||
178 | """Return True if the page is visible on the frontend.""" |
||
179 | return self.calculated_status in (self.PUBLISHED, self.HIDDEN) |
||
180 | visible = property(_visible) |
||
181 | |||
182 | def get_children(self): |
||
183 | """Cache superclass result""" |
||
184 | key = self.CHILDREN_KEY % self.id |
||
185 | #children = cache.get(key, None) |
||
186 | # if children is None: |
||
187 | children = super(Page, self).get_children() |
||
188 | #cache.set(key, children) |
||
189 | return children |
||
190 | |||
191 | def published_children(self): |
||
192 | """Return a :class:`QuerySet` of published children page""" |
||
193 | key = self.PUB_CHILDREN_KEY % self.id |
||
194 | #children = cache.get(key, None) |
||
195 | # if children is None: |
||
196 | children = Page.objects.filter_published(self.get_children()).all() |
||
197 | #cache.set(key, children) |
||
198 | return children |
||
199 | |||
200 | def get_children_for_frontend(self): |
||
201 | """Return a :class:`QuerySet` of published children page""" |
||
202 | return self.published_children() |
||
203 | |||
204 | def get_date_ordered_children_for_frontend(self): |
||
205 | """Return a :class:`QuerySet` of published children page ordered |
||
206 | by publication date.""" |
||
207 | return self.published_children().order_by('-publication_date') |
||
208 | |||
209 | def move_to(self, target, position='first-child'): |
||
210 | """Invalidate cache when moving""" |
||
211 | |||
212 | # Invalidate both in case position matters, |
||
213 | # otherwise only target is needed. |
||
214 | self.invalidate() |
||
215 | target.invalidate() |
||
216 | super(Page, self).move_to(target, position=position) |
||
217 | |||
218 | def invalidate(self): |
||
219 | """Invalidate cached data for this page.""" |
||
220 | |||
221 | cache.delete(self.PAGE_LANGUAGES_KEY % (self.id)) |
||
222 | cache.delete('PAGE_FIRST_ROOT_ID') |
||
223 | cache.delete(self.CHILDREN_KEY % self.id) |
||
224 | cache.delete(self.PUB_CHILDREN_KEY % self.id) |
||
225 | # XXX: Should this have a depth limit? |
||
226 | if self.parent_id: |
||
227 | self.parent.invalidate() |
||
228 | self._languages = None |
||
229 | self._complete_slug = None |
||
230 | self._content_dict = dict() |
||
231 | |||
232 | placeholders = get_placeholders(self.get_template()) |
||
233 | |||
234 | p_names = [p.ctype for p in placeholders] |
||
235 | if 'slug' not in p_names: |
||
236 | p_names.append('slug') |
||
237 | if 'title' not in p_names: |
||
238 | p_names.append('title') |
||
239 | |||
240 | from pages.managers import fake_page |
||
241 | shared = [p for p in placeholders if p.shared] |
||
242 | for share in shared: |
||
243 | fake_page.invalidate(share.ctype) |
||
244 | |||
245 | # delete content cache, frozen or not |
||
246 | for name in p_names: |
||
247 | # frozen |
||
248 | cache.delete( |
||
249 | PAGE_CONTENT_DICT_KEY % |
||
250 | (self.id, name, 1)) |
||
251 | # not frozen |
||
252 | cache.delete( |
||
253 | PAGE_CONTENT_DICT_KEY % |
||
254 | (self.id, name, 0)) |
||
255 | |||
256 | cache.delete(self.PAGE_URL_KEY % (self.id)) |
||
257 | |||
258 | def get_languages(self): |
||
259 | """ |
||
260 | Return a list of all used languages for this page. |
||
261 | """ |
||
262 | if self._languages: |
||
263 | return self._languages |
||
264 | self._languages = cache.get(self.PAGE_LANGUAGES_KEY % (self.id)) |
||
265 | if self._languages is not None: |
||
266 | return self._languages |
||
267 | |||
268 | languages = [c['language'] for |
||
269 | c in Content.objects.filter(page=self, |
||
270 | type="slug").values('language')] |
||
271 | # remove duplicates |
||
272 | languages = sorted(set(languages)) |
||
273 | cache.set(self.PAGE_LANGUAGES_KEY % (self.id), languages) |
||
274 | self._languages = languages |
||
275 | return languages |
||
276 | |||
277 | def is_first_root(self): |
||
278 | """Return ``True`` if this page is the first root pages.""" |
||
279 | parent_cache_key = 'PARENT_FOR_%d' % self.id |
||
280 | has_parent = cache.get(parent_cache_key, None) |
||
281 | if has_parent is None: |
||
282 | has_parent = not not self.parent |
||
283 | cache.set(parent_cache_key, has_parent) |
||
284 | |||
285 | if has_parent: |
||
286 | return False |
||
287 | if self._is_first_root is not None: |
||
288 | return self._is_first_root |
||
289 | first_root_id = cache.get('PAGE_FIRST_ROOT_ID') |
||
290 | if first_root_id is not None: |
||
291 | self._is_first_root = first_root_id == self.id |
||
292 | return self._is_first_root |
||
293 | try: |
||
294 | first_root_id = Page.objects.root().values('id')[0]['id'] |
||
295 | except IndexError: |
||
296 | first_root_id = None |
||
297 | if first_root_id is not None: |
||
298 | cache.set('PAGE_FIRST_ROOT_ID', first_root_id) |
||
299 | self._is_first_root = self.id == first_root_id |
||
300 | return self._is_first_root |
||
301 | |||
302 | def get_template(self): |
||
303 | """ |
||
304 | Get the :attr:`template <Page.template>` of this page if |
||
305 | defined or the closer parent's one if defined |
||
306 | or :attr:`pages.settings.PAGE_DEFAULT_TEMPLATE` otherwise. |
||
307 | """ |
||
308 | if self.template: |
||
309 | return self.template |
||
310 | |||
311 | template = None |
||
312 | for p in self.get_ancestors(ascending=True): |
||
313 | if p.template: |
||
314 | template = p.template |
||
315 | break |
||
316 | |||
317 | if not template: |
||
318 | template = settings.PAGE_DEFAULT_TEMPLATE |
||
319 | |||
320 | return template |
||
321 | |||
322 | def get_template_name(self): |
||
323 | """ |
||
324 | Get the template name of this page if defined or if a closer |
||
325 | parent has a defined template or |
||
326 | :data:`pages.settings.PAGE_DEFAULT_TEMPLATE` otherwise. |
||
327 | """ |
||
328 | template = self.get_template() |
||
329 | page_templates = settings.get_page_templates() |
||
330 | for t in page_templates: |
||
331 | if t[0] == template: |
||
332 | return t[1] |
||
333 | return template |
||
334 | |||
335 | def valid_targets(self): |
||
336 | """Return a :class:`QuerySet` of valid targets for moving a page |
||
337 | into the tree. |
||
338 | |||
339 | :param perms: the level of permission of the concerned user. |
||
340 | """ |
||
341 | exclude_list = [self.id] |
||
342 | for p in self.get_descendants(): |
||
343 | exclude_list.append(p.id) |
||
344 | return Page.objects.exclude(id__in=exclude_list) |
||
345 | |||
346 | # Content methods |
||
347 | |||
348 | def get_content(self, language, ctype, language_fallback=False): |
||
349 | """Shortcut method for retrieving a piece of page content |
||
350 | |||
351 | :param language: wanted language, if not defined default is used. |
||
352 | :param ctype: the type of content. |
||
353 | :param fallback: if ``True``, the content will also be searched in \ |
||
354 | other languages. |
||
355 | """ |
||
356 | return Content.objects.get_content(self, language, ctype, |
||
357 | language_fallback) |
||
358 | |||
359 | def expose_content(self): |
||
360 | """Return all the current content of this page into a `string`. |
||
361 | |||
362 | This is used by the haystack framework to build the search index.""" |
||
363 | placeholders = get_placeholders(self.get_template()) |
||
364 | exposed_content = [] |
||
365 | for lang in self.get_languages(): |
||
366 | for p in placeholders: |
||
367 | content = self.get_content(lang, p.ctype, False) |
||
368 | if content: |
||
369 | exposed_content.append(content) |
||
370 | return "\r\n".join(exposed_content) |
||
371 | |||
372 | def content_by_language(self, language): |
||
373 | """ |
||
374 | Return a list of latest published |
||
375 | :class:`Content <pages.models.Content>` |
||
376 | for a particluar language. |
||
377 | |||
378 | :param language: wanted language, |
||
379 | """ |
||
380 | placeholders = get_placeholders(self.get_template()) |
||
381 | content_list = [] |
||
382 | for p in placeholders: |
||
383 | try: |
||
384 | content = Content.objects.get_content_object(self, |
||
385 | language, p.ctype) |
||
386 | content_list.append(content) |
||
387 | except Content.DoesNotExist: |
||
388 | pass |
||
389 | return content_list |
||
390 | |||
391 | ### Title and slug |
||
392 | |||
393 | def get_url_path(self, language=None): |
||
394 | """Return the URL's path component. Add the language prefix if |
||
395 | ``PAGE_USE_LANGUAGE_PREFIX`` setting is set to ``True``. |
||
396 | |||
397 | :param language: the wanted url language. |
||
398 | """ |
||
399 | if self.is_first_root(): |
||
400 | # this is used to allow users to change URL of the root |
||
401 | # page. The language prefix is not usable here. |
||
402 | try: |
||
403 | return reverse('pages-root') |
||
404 | except Exception: |
||
405 | pass |
||
406 | url = self.get_complete_slug(language) |
||
407 | if not language: |
||
408 | language = settings.PAGE_DEFAULT_LANGUAGE |
||
409 | if settings.PAGE_USE_LANGUAGE_PREFIX: |
||
410 | return reverse('pages-details-by-path', |
||
411 | args=[language, url]) |
||
412 | else: |
||
413 | return reverse('pages-details-by-path', args=[url]) |
||
414 | |||
415 | def get_absolute_url(self, language=None): |
||
416 | """Alias for `get_url_path`. |
||
417 | |||
418 | :param language: the wanted url language. |
||
419 | """ |
||
420 | return self.get_url_path(language=language) |
||
421 | |||
422 | def get_complete_slug(self, language=None, hideroot=True): |
||
423 | """Return the complete slug of this page by concatenating |
||
424 | all parent's slugs. |
||
425 | |||
426 | :param language: the wanted slug language.""" |
||
427 | if not language: |
||
428 | language = settings.PAGE_DEFAULT_LANGUAGE |
||
429 | |||
430 | if self._complete_slug and language in self._complete_slug: |
||
431 | return self._complete_slug[language] |
||
432 | |||
433 | self._complete_slug = cache.get(self.PAGE_URL_KEY % (self.id)) |
||
434 | if self._complete_slug is None: |
||
435 | self._complete_slug = {} |
||
436 | elif language in self._complete_slug: |
||
437 | return self._complete_slug[language] |
||
438 | |||
439 | if hideroot and settings.PAGE_HIDE_ROOT_SLUG and self.is_first_root(): |
||
440 | url = '' |
||
441 | else: |
||
442 | url = '%s' % self.slug(language) |
||
443 | |||
444 | key = self.ANCESTORS_KEY % self.id |
||
445 | ancestors = cache.get(key, None) |
||
446 | if ancestors is None: |
||
447 | ancestors = self.get_ancestors(ascending=True) |
||
448 | cache.set(key, ancestors) |
||
449 | |||
450 | for ancestor in ancestors: |
||
451 | url = ancestor.slug(language) + '/' + url |
||
452 | |||
453 | self._complete_slug[language] = url |
||
454 | cache.set(self.PAGE_URL_KEY % (self.id), self._complete_slug) |
||
455 | return url |
||
456 | |||
457 | def slug_with_level(self, language=None): |
||
458 | """Display the slug of the page prepended with insecable |
||
459 | spaces to simluate the level of page in the hierarchy.""" |
||
460 | level = '' |
||
461 | if self.level: |
||
462 | for n in range(0, self.level): |
||
463 | level += ' ' |
||
464 | return mark_safe(level + self.slug(language)) |
||
465 | |||
466 | def slug(self, language=None, fallback=True): |
||
467 | """ |
||
468 | Return the slug of the page depending on the given language. |
||
469 | |||
470 | :param language: wanted language, if not defined default is used. |
||
471 | :param fallback: if ``True``, the slug will also be searched in other \ |
||
472 | languages. |
||
473 | """ |
||
474 | |||
475 | slug = self.get_content(language, 'slug', language_fallback=fallback) |
||
476 | if slug == '': |
||
477 | return "Page {0}".format(self.id) |
||
478 | |||
479 | return slug |
||
480 | |||
481 | def title(self, language=None, fallback=True): |
||
482 | """ |
||
483 | Return the title of the page depending on the given language. |
||
484 | |||
485 | :param language: wanted language, if not defined default is used. |
||
486 | :param fallback: if ``True``, the slug will also be searched in \ |
||
487 | other languages. |
||
488 | """ |
||
489 | if not language: |
||
490 | language = settings.PAGE_DEFAULT_LANGUAGE |
||
491 | |||
492 | return self.get_content(language, 'title', language_fallback=fallback) |
||
493 | |||
494 | # Formating methods |
||
495 | |||
496 | def margin_level(self): |
||
497 | """Used in the admin menu to create the left margin.""" |
||
498 | return self.level * 2 |
||
499 | |||
500 | def __str__(self): |
||
501 | """Representation of the page, saved or not.""" |
||
502 | if self.id: |
||
503 | # without ID a slug cannot be retrieved |
||
504 | return self.slug() |
||
505 | return "Page without id" |
||
506 | |||
507 | |||
508 | @python_2_unicode_compatible |
||
509 | class Content(models.Model): |
||
510 | """A block of content, tied to a :class:`Page <pages.models.Page>`, |
||
511 | for a particular language""" |
||
512 | |||
513 | # languages could have seven characters : Simplified Chinese is zh-hans |
||
514 | language = models.CharField(_('language'), max_length=7, blank=False) |
||
515 | body = models.TextField(_('body'), blank=True) |
||
516 | type = models.CharField( |
||
517 | _('type'), max_length=100, blank=False, |
||
518 | db_index=True) |
||
519 | page = models.ForeignKey(Page, verbose_name=_('page'), null=True) |
||
520 | |||
521 | creation_date = models.DateTimeField( |
||
522 | _('creation date'), editable=False, |
||
523 | default=get_now) |
||
524 | objects = ContentManager() |
||
525 | |||
526 | class Meta: |
||
527 | get_latest_by = 'creation_date' |
||
528 | verbose_name = _('content') |
||
529 | verbose_name_plural = _('contents') |
||
530 | |||
531 | def __str__(self): |
||
532 | return u"{0} :: {1}".format(self.page.slug(), self.body[0:15]) |
||
533 | |||
534 | |||
535 | @python_2_unicode_compatible |
||
536 | class PageAlias(models.Model): |
||
537 | """URL alias for a :class:`Page <pages.models.Page>`""" |
||
538 | page = models.ForeignKey(Page, null=True, blank=True, |
||
539 | verbose_name=_('page')) |
||
540 | url = models.CharField(max_length=255, unique=True) |
||
541 | objects = PageAliasManager() |
||
542 | |||
543 | class Meta: |
||
544 | verbose_name_plural = _('Aliases') |
||
545 | |||
546 | def save(self, *args, **kwargs): |
||
547 | # normalize url |
||
548 | self.url = normalize_url(self.url) |
||
549 | super(PageAlias, self).save(*args, **kwargs) |
||
550 | |||
551 | def __str__(self): |
||
552 | return "{0} :: {1}".format(self.url, self.page.get_complete_slug()) |
||
553 | |||
554 | |||
555 | View Code Duplication | def media_filename(instance, filename): |
|
0 ignored issues
–
show
Duplication
introduced
by
![]() |
|||
556 | avoid_collision = uuid.uuid4().hex[:8] |
||
557 | name_parts = filename.split('.') |
||
558 | if len(name_parts) > 1: |
||
559 | name = slugify('.'.join(name_parts[:-1]), allow_unicode=True) |
||
560 | ext = slugify(name_parts[-1]) |
||
561 | name = name + '.' + ext |
||
562 | else: |
||
563 | name = slugify(data.name) |
||
564 | filename = os.path.join( |
||
565 | settings.PAGE_UPLOAD_ROOT, |
||
566 | 'medias', |
||
567 | name |
||
568 | ) |
||
569 | return filename |
||
570 | |||
571 | |||
572 | @python_2_unicode_compatible |
||
573 | class Media(models.Model): |
||
574 | """Media model :class:`Media <pages.models.Media>`""" |
||
575 | title = models.CharField(max_length=255, blank=True) |
||
576 | description = models.TextField(blank=True) |
||
577 | url = models.FileField(upload_to=media_filename) |
||
578 | extension = models.CharField(max_length=32, blank=True, editable=False) |
||
579 | creation_date = models.DateTimeField(_('creation date'), editable=False, |
||
580 | default=get_now) |
||
581 | |||
582 | def image(self): |
||
583 | if self.extension in ['png', 'jpg', 'jpeg']: |
||
584 | return u'<img width="60" src="%s" />' % os.path.join( |
||
585 | settings.PAGES_MEDIA_URL, self.url.name) |
||
586 | if self.extension == 'pdf': |
||
587 | return u'<i class="fa fa-file-pdf-o" aria-hidden="true"></i>' |
||
588 | if self.extension in ['doc', 'docx']: |
||
589 | return u'<i class="fa fa-file-word-o" aria-hidden="true"></i>' |
||
590 | if self.extension in ['zip', 'gzip', 'rar']: |
||
591 | return u'<i class="fa fa-file-archive-o" aria-hidden="true"></i> ' |
||
592 | return u'<i class="fa fa-file-o" aria-hidden="true"></i>' |
||
593 | image.short_description = _('Thumbnail') |
||
594 | image.allow_tags = True |
||
595 | |||
596 | class Meta: |
||
597 | verbose_name_plural = _('Medias') |
||
598 | |||
599 | def save(self, *args, **kwargs): |
||
600 | parts = self.url.name.split('.') |
||
601 | if len(parts) > 1: |
||
602 | self.extension = parts[-1].lower() |
||
603 | if not self.title: |
||
604 | parts = self.url.name.split('/') |
||
605 | self.title = parts[-1] |
||
606 | |||
607 | super(Media, self).save(*args, **kwargs) |
||
608 | |||
609 | def __str__(self): |
||
610 | return self.url.name |
||
611 |