Statistics
| Branch: | Tag: | Revision:

root / cloudcmsblog / models.py @ cb88bc8d

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
84 c1468bcc Kostas Papadimitriou
class CategoryTranslation(translations.Translation(Category)):
85 c1468bcc Kostas Papadimitriou
    """
86 c1468bcc Kostas Papadimitriou
    Category translation
87 c1468bcc Kostas Papadimitriou
    """
88 c1468bcc Kostas Papadimitriou
    title = models.CharField(_('category title'), max_length=100)
89 c1468bcc Kostas Papadimitriou
    slug = models.SlugField(_('slug'), unique=True)
90 c1468bcc Kostas Papadimitriou
    description = models.CharField(_('description'), max_length=250, blank=True)
91 c1468bcc Kostas Papadimitriou
92 c1468bcc Kostas Papadimitriou
    class Meta:
93 c1468bcc Kostas Papadimitriou
        verbose_name = _('category translation')
94 c1468bcc Kostas Papadimitriou
        verbose_name_plural = _('category translations')
95 c1468bcc Kostas Papadimitriou
        ordering = ['title']
96 c1468bcc Kostas Papadimitriou
97 c1468bcc Kostas Papadimitriou
    def __unicode__(self):
98 c1468bcc Kostas Papadimitriou
        return self.title
99 c1468bcc Kostas Papadimitriou
100 c1468bcc Kostas Papadimitriou
    def get_absolute_url(self):
101 f6e90a2b Kostas Papadimitriou
        return reverse('cloudcmsblog_entries_archive', 'cloudcmsblog.urls', (), {
102 c1468bcc Kostas Papadimitriou
            'category': self.slug,
103 f6e90a2b Kostas Papadimitriou
        })
104 c1468bcc Kostas Papadimitriou
105 c1468bcc Kostas Papadimitriou
    def save(self, *args, **kwargs):
106 c1468bcc Kostas Papadimitriou
        if not self.slug:
107 c1468bcc Kostas Papadimitriou
            self.slug = slugify(self.title)
108 c1468bcc Kostas Papadimitriou
109 c1468bcc Kostas Papadimitriou
        super(CategoryTranslation, self).save(*args, **kwargs)
110 c1468bcc Kostas Papadimitriou
111 c1468bcc Kostas Papadimitriou
112 c1468bcc Kostas Papadimitriou
class EntryManager(models.Manager):
113 c1468bcc Kostas Papadimitriou
114 c1468bcc Kostas Papadimitriou
    def active(self):
115 c1468bcc Kostas Papadimitriou
        return self.filter(is_active=True)
116 c1468bcc Kostas Papadimitriou
117 f6e90a2b Kostas Papadimitriou
    def latest(self, limit=3):
118 f6e90a2b Kostas Papadimitriou
        return self.filter()[:limit]
119 f6e90a2b Kostas Papadimitriou
120 719d8d08 Kostas Papadimitriou
121 719d8d08 Kostas Papadimitriou
def get_blog_page():
122 719d8d08 Kostas Papadimitriou
    """
123 719d8d08 Kostas Papadimitriou
    Returns Page model that has been associated with blog application
124 719d8d08 Kostas Papadimitriou
    """
125 719d8d08 Kostas Papadimitriou
    return get_app_page(Page, "cloudcmsblog")
126 719d8d08 Kostas Papadimitriou
127 c1468bcc Kostas Papadimitriou
class Entry(Base):
128 c1468bcc Kostas Papadimitriou
    """
129 c1468bcc Kostas Papadimitriou
    Blog post entry
130 c1468bcc Kostas Papadimitriou
    """
131 c1468bcc Kostas Papadimitriou
    is_active = models.BooleanField(_('is active'), default=True)
132 c1468bcc Kostas Papadimitriou
    is_featured = models.BooleanField(_('is featured'), default=False)
133 c1468bcc Kostas Papadimitriou
134 c1468bcc Kostas Papadimitriou
    title = models.CharField(_('title'), max_length=100)
135 c1468bcc Kostas Papadimitriou
    slug = models.SlugField(_('slug'), max_length=100, unique_for_date='published_on')
136 c1468bcc Kostas Papadimitriou
    author = models.ForeignKey(User, related_name='blogentries', verbose_name=_('author'))
137 c1468bcc Kostas Papadimitriou
    language = models.CharField(max_length=255, choices=settings.LANGUAGES)
138 c1468bcc Kostas Papadimitriou
139 c1468bcc Kostas Papadimitriou
    intro_text = models.TextField(max_length=255,
140 c1468bcc Kostas Papadimitriou
            help_text="Displayed in list views", blank=True)
141 c1468bcc Kostas Papadimitriou
    image = MediaFileForeignKey(MediaFile, null=True, blank=True)
142 02419f38 Kostas Papadimitriou
    application = models.ManyToManyField('cloudcms.Application',
143 c1468bcc Kostas Papadimitriou
            related_name="blogentries",
144 c1468bcc Kostas Papadimitriou
            verbose_name=_('application'))
145 c1468bcc Kostas Papadimitriou
146 c1468bcc Kostas Papadimitriou
    published_on = models.DateTimeField(_('published on'), blank=True, null=True, default=datetime.now,
147 c1468bcc Kostas Papadimitriou
        help_text=_('Will be filled in automatically when entry gets published.'))
148 c1468bcc Kostas Papadimitriou
    last_changed = models.DateTimeField(_('last change'), auto_now=True, editable=False)
149 c1468bcc Kostas Papadimitriou
150 c1468bcc Kostas Papadimitriou
    categories = models.ManyToManyField(Category, verbose_name=_('categories'),
151 c1468bcc Kostas Papadimitriou
        related_name='blogentries', null=True, blank=True)
152 c1468bcc Kostas Papadimitriou
153 c1468bcc Kostas Papadimitriou
    objects = EntryManager()
154 c1468bcc Kostas Papadimitriou
155 c1468bcc Kostas Papadimitriou
    class Meta:
156 c1468bcc Kostas Papadimitriou
        get_latest_by = 'published_on'
157 c1468bcc Kostas Papadimitriou
        ordering = ['-published_on']
158 c1468bcc Kostas Papadimitriou
        verbose_name = _('entry')
159 c1468bcc Kostas Papadimitriou
        verbose_name_plural = _('entries')
160 c1468bcc Kostas Papadimitriou
161 c1468bcc Kostas Papadimitriou
    def __unicode__(self):
162 c1468bcc Kostas Papadimitriou
        return self.title
163 c1468bcc Kostas Papadimitriou
164 c1468bcc Kostas Papadimitriou
    def get_absolute_url(self):
165 f6e90a2b Kostas Papadimitriou
        try:
166 f6e90a2b Kostas Papadimitriou
            r = reverse('cloudcmsblog_entry_detail', 'cloudcmsblog.urls', (),
167 f6e90a2b Kostas Papadimitriou
                    {
168 f6e90a2b Kostas Papadimitriou
                    'year': self.published_on.strftime('%Y'),
169 f6e90a2b Kostas Papadimitriou
                    'month': self.published_on.strftime('%m'),
170 f6e90a2b Kostas Papadimitriou
                    'day': self.published_on.strftime('%d'),
171 f6e90a2b Kostas Papadimitriou
                    'slug': self.slug,
172 f6e90a2b Kostas Papadimitriou
                    })
173 f6e90a2b Kostas Papadimitriou
        except Exception,e:
174 f6e90a2b Kostas Papadimitriou
            pass
175 f6e90a2b Kostas Papadimitriou
176 f6e90a2b Kostas Papadimitriou
        # ugly hack to fix proper application reverse url
177 f6e90a2b Kostas Papadimitriou
        BLOG_URL = ""
178 f6e90a2b Kostas Papadimitriou
        try:
179 719d8d08 Kostas Papadimitriou
            BLOG_URL = get_blog_page().get_navigation_url()
180 f6e90a2b Kostas Papadimitriou
        except Exception, e:
181 f6e90a2b Kostas Papadimitriou
            print e
182 64ef316d Kostas Papadimitriou
183 f6e90a2b Kostas Papadimitriou
        if r.startswith(BLOG_URL):
184 f6e90a2b Kostas Papadimitriou
            return r
185 f6e90a2b Kostas Papadimitriou
        else:
186 64ef316d Kostas Papadimitriou
            return BLOG_URL + r.lstrip('/')
187 c1468bcc Kostas Papadimitriou
188 aa8ce0dc Kostas Papadimitriou
    def back_url(self):
189 719d8d08 Kostas Papadimitriou
        return get_blog_page().get_navigation_url()
190 aa8ce0dc Kostas Papadimitriou
191 c1468bcc Kostas Papadimitriou
192 c1468bcc Kostas Papadimitriou
# Feincms navigation extension
193 c1468bcc Kostas Papadimitriou
class BlogCategoriesNavigationExtension(NavigationExtension):
194 c1468bcc Kostas Papadimitriou
    """
195 c1468bcc Kostas Papadimitriou
    Navigation extension for FeinCMS which lists all categories that user
196 c1468bcc Kostas Papadimitriou
    wants to include in global site navigation.
197 c1468bcc Kostas Papadimitriou
    """
198 c1468bcc Kostas Papadimitriou
199 c1468bcc Kostas Papadimitriou
    name = _('blog categories')
200 c1468bcc Kostas Papadimitriou
201 c1468bcc Kostas Papadimitriou
    def children(self, page, **kwargs):
202 c1468bcc Kostas Papadimitriou
        for category in Category.objects.filter(display_on_menu=True):
203 c1468bcc Kostas Papadimitriou
            url='%scategory/%s/' % (page.get_absolute_url(), category.translation.slug)
204 c1468bcc Kostas Papadimitriou
            yield PagePretender(
205 c1468bcc Kostas Papadimitriou
                title=category.translation.title,
206 c1468bcc Kostas Papadimitriou
                tree_id=page.tree_id,
207 c1468bcc Kostas Papadimitriou
                url=url,
208 c1468bcc Kostas Papadimitriou
                lft=0,
209 c1468bcc Kostas Papadimitriou
                rght=0,
210 cb88bc8d Kostas Papadimitriou
                level=page.level,
211 c1468bcc Kostas Papadimitriou
                slug=category.translation.slug,
212 c1468bcc Kostas Papadimitriou
            )
213 c1468bcc Kostas Papadimitriou
214 f6e90a2b Kostas Papadimitriou
215 f6e90a2b Kostas Papadimitriou
# Feincms content abstract models
216 f6e90a2b Kostas Papadimitriou
class LatestEntries(models.Model):
217 f6e90a2b Kostas Papadimitriou
    title = models.CharField(max_length=255)
218 f6e90a2b Kostas Papadimitriou
    limit = models.PositiveIntegerField(default=3)
219 f6e90a2b Kostas Papadimitriou
    display_text = models.BooleanField(default=False)
220 f6e90a2b Kostas Papadimitriou
221 f6e90a2b Kostas Papadimitriou
    class Meta:
222 f6e90a2b Kostas Papadimitriou
        abstract = True
223 f6e90a2b Kostas Papadimitriou
        verbose_name = _('Latest blog entries')
224 f6e90a2b Kostas Papadimitriou
        verbose_name_plural = _('Latest blog entries')
225 f6e90a2b Kostas Papadimitriou
226 f6e90a2b Kostas Papadimitriou
    def render(self, **kwargs):
227 f6e90a2b Kostas Papadimitriou
        return render_to_string(['content/latest_blog.html'], {'posts':
228 f6e90a2b Kostas Papadimitriou
            Entry.objects.latest(self.limit), 'content': self})