Proper logged in/out message styles
[snf-cloudcms] / cloudcmsblog / models.py
1 # Copyright 2012 GRNET S.A. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6 #
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10 #
11 #   2. Redistributions in binary form must reproduce the above
12 #      copyright notice, this list of conditions and the following
13 #      disclaimer in the documentation and/or other materials
14 #      provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 #
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
33
34
35 from datetime import datetime
36
37 from django.db import models
38 from django.conf import settings
39 from django.contrib.auth.models import User
40 from django.utils.translation import ugettext_lazy as _, ugettext, ungettext
41 from django.template.loader import render_to_string
42 from django.core import urlresolvers
43
44 from feincms import translations
45 from feincms.models import Base
46 from feincms.module.page.models import Page
47 from feincms.content.richtext.models import RichTextContent
48 from feincms.content.section.models import SectionContent
49 from feincms.content.application.models import reverse
50 from feincms.module.medialibrary.fields import MediaFileForeignKey
51 from feincms.module.medialibrary.models import MediaFile
52 from feincms.module.page.extensions.navigation import NavigationExtension
53 from feincms.module.page.extensions.navigation import PagePretender
54 from feincms.content.application.models import ApplicationContent
55
56 from cloudcms.cms_utils import get_app_page
57
58 # monkeypatch django reverse (feincms 1.5+ solves this issue)
59 urlresolvers.reverse = reverse
60
61 class Category(models.Model, translations.TranslatedObjectMixin):
62     """
63     Blog entry category. Each blog post may belong to multiple categories.
64     """
65
66     ordering = models.SmallIntegerField(_('ordering'), default=0)
67     display_on_menu = models.BooleanField(default=False)
68
69     class Meta:
70         verbose_name = _('category')
71         verbose_name_plural = _('categories')
72         ordering = ['-ordering',]
73
74     objects = translations.TranslatedObjectManager()
75
76     def entries_count(self):
77         return self.blogentries.count()
78
79     def __unicode__(self):
80         trans = translations.TranslatedObjectMixin.__unicode__(self)
81         return trans or _('Unnamed category')
82
83
84 class CategoryTranslation(translations.Translation(Category)):
85     """
86     Category translation
87     """
88     title = models.CharField(_('category title'), max_length=100)
89     slug = models.SlugField(_('slug'), unique=True)
90     description = models.CharField(_('description'), max_length=250, blank=True)
91
92     class Meta:
93         verbose_name = _('category translation')
94         verbose_name_plural = _('category translations')
95         ordering = ['title']
96
97     def __unicode__(self):
98         return self.title
99
100     def get_absolute_url(self):
101         return reverse('cloudcmsblog_entries_archive', 'cloudcmsblog.urls', (), {
102             'category': self.slug,
103         })
104
105     def save(self, *args, **kwargs):
106         if not self.slug:
107             self.slug = slugify(self.title)
108
109         super(CategoryTranslation, self).save(*args, **kwargs)
110
111
112 class EntryManager(models.Manager):
113
114     def active(self):
115         return self.filter(is_active=True)
116
117     def latest(self, limit=3):
118         return self.filter()[:limit]
119
120
121 def get_blog_page():
122     """
123     Returns Page model that has been associated with blog application
124     """
125     return get_app_page(Page, "cloudcmsblog")
126
127 class Entry(Base):
128     """
129     Blog post entry
130     """
131     is_active = models.BooleanField(_('is active'), default=True)
132     is_featured = models.BooleanField(_('is featured'), default=False)
133
134     title = models.CharField(_('title'), max_length=100)
135     slug = models.SlugField(_('slug'), max_length=100, unique_for_date='published_on')
136     author = models.ForeignKey(User, related_name='blogentries', verbose_name=_('author'))
137     language = models.CharField(max_length=255, choices=settings.LANGUAGES)
138
139     intro_text = models.TextField(max_length=255,
140             help_text="Displayed in list views", blank=True)
141     image = MediaFileForeignKey(MediaFile, null=True, blank=True)
142     application = models.ManyToManyField('cloudcms.Application',
143             related_name="blogentries",
144             verbose_name=_('application'))
145
146     published_on = models.DateTimeField(_('published on'), blank=True, null=True, default=datetime.now,
147         help_text=_('Will be filled in automatically when entry gets published.'))
148     last_changed = models.DateTimeField(_('last change'), auto_now=True, editable=False)
149
150     categories = models.ManyToManyField(Category, verbose_name=_('categories'),
151         related_name='blogentries', null=True, blank=True)
152
153     objects = EntryManager()
154
155     class Meta:
156         get_latest_by = 'published_on'
157         ordering = ['-published_on']
158         verbose_name = _('entry')
159         verbose_name_plural = _('entries')
160
161     def __unicode__(self):
162         return self.title
163
164     def get_absolute_url(self):
165         try:
166             r = reverse('cloudcmsblog_entry_detail', 'cloudcmsblog.urls', (),
167                     {
168                     'year': self.published_on.strftime('%Y'),
169                     'month': self.published_on.strftime('%m'),
170                     'day': self.published_on.strftime('%d'),
171                     'slug': self.slug,
172                     })
173         except Exception,e:
174             pass
175
176         # ugly hack to fix proper application reverse url
177         BLOG_URL = ""
178         try:
179             BLOG_URL = get_blog_page().get_navigation_url()
180         except Exception, e:
181             print e
182
183         if r.startswith(BLOG_URL):
184             return r
185         else:
186             return BLOG_URL + r.lstrip('/')
187
188     def back_url(self):
189         return get_blog_page().get_navigation_url()
190
191
192 # Feincms navigation extension
193 class BlogCategoriesNavigationExtension(NavigationExtension):
194     """
195     Navigation extension for FeinCMS which lists all categories that user
196     wants to include in global site navigation.
197     """
198
199     name = _('blog categories')
200
201     def children(self, page, **kwargs):
202         for category in Category.objects.filter(display_on_menu=True):
203             url='%scategory/%s/' % (page.get_absolute_url(), category.translation.slug)
204             yield PagePretender(
205                 title=category.translation.title,
206                 tree_id=page.tree_id,
207                 url=url,
208                 lft=0,
209                 rght=0,
210                 slug=category.translation.slug,
211             )
212
213
214 # Feincms content abstract models
215 class LatestEntries(models.Model):
216     title = models.CharField(max_length=255)
217     limit = models.PositiveIntegerField(default=3)
218     display_text = models.BooleanField(default=False)
219
220     class Meta:
221         abstract = True
222         verbose_name = _('Latest blog entries')
223         verbose_name_plural = _('Latest blog entries')
224
225     def render(self, **kwargs):
226         return render_to_string(['content/latest_blog.html'], {'posts':
227             Entry.objects.latest(self.limit), 'content': self})
228