Statistics
| Branch: | Tag: | Revision:

root / cloudcmsblog / models.py @ master

History | View | Annotate | Download (7.9 kB)

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