Newer
Older
import sys
from datetime import datetime
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import python_2_unicode_compatible
from django.utils import six
newsbox_models = []
def newsbox_mcls_processor(mcls, class_name, class_bases, class_attrs, base, newsbox_opts):
if 'newsbox_title' not in class_attrs:
class_attrs['newsbox_title'] = models.CharField(
verbose_name=_('title'),
max_length=255)
newsbox_opts['trans_fieldnames'].append('newsbox_title')
if 'newsbox_slug' not in class_attrs:
class_attrs['newsbox_slug'] = models.SlugField(
verbose_name=_('slug'),
help_text=_('The part of the title that is used in the URL'))
newsbox_opts['trans_fieldnames'].append('newsbox_slug')
if 'newsbox_summary' not in class_attrs:
class_attrs['newsbox_summary'] = models.TextField(
verbose_name=_('summary'))
newsbox_opts['trans_fieldnames'].append('newsbox_summary')
if 'newsbox_body' not in class_attrs:
class_attrs['newsbox_body'] = models.TextField(
verbose_name=_('body'),
null=False, blank=True)
newsbox_opts['trans_fieldnames'].append('newsbox_body')
def newsboxseo_mcls_processor(mcls, class_name, class_bases, class_attrs, base, newsbox_opts):
class_attrs['newsbox_meta_description'] = models.TextField(
verbose_name=_('meta description'),
max_length=255, blank=True, null=True)
class_attrs['newsbox_meta_keywords'] = models.CharField(
verbose_name=_('meta keywords'),
max_length=255, blank=True, null=True)
'newsbox_meta_description', 'newsbox_meta_keywords']
###################################################################################################
###################################################################################################
class NewsboxManager(models.Manager):
"""
Filter published news
"""
def published(self):
q = self.model.q_published()
return super(NewsboxManager, self).get_queryset().filter(q)
###################################################################################################
###################################################################################################
class NewsboxModelBase(models.base.ModelBase):
"""
Metaclass used to inject django-newsboxfunctionalities into django
orm.
"""
def __new__(mcls, class_name, class_bases, class_attrs):
super_new = super(NewsboxModelBase, mcls).__new__
# six.with_metaclass() inserts an extra class called 'NewBase' in the
# inheritance tree: Model -> NewBase -> object. But the initialization
# should be executed only once for a given model class.
# attrs will never be empty for classes declared in the standard way
# (ie. with the `class` keyword). This is quite robust.
if class_name == 'NewBase' and class_attrs == {}:
return super_new(mcls, class_name, class_bases, class_attrs)
# Also ensure initialization is only performed for subclasses of Model
# (excluding Model class itself).
# parents = [b for b in class_bases
# if isinstance(b, NewsboxModelBase) and
# not (b.__name__ == 'NewBase' and b.__mro__ == (b, object))]
# retrieve newsbox options in Meta class and delete these from the
# Meta class to avoid django complain about unsupported keys.
# We register our meta options in `_newsbox_meta`
attr_meta = class_attrs.get('Meta', None)
if attr_meta and not getattr(attr_meta, 'ordering', None):
class_attrs['Meta'].ordering = ('-newsbox_publication_start_date',)
abstract = getattr(attr_meta, 'abstract', False)
newsbox_opts = {}
newsbox_opts_keys = [
'detail_url_name',
'metaclass_base_processor',
'metaclass_final_processor',
'trans_fieldnames']
for opt in newsbox_opts_keys:
newsbox_opt = 'newsbox_' + opt
if hasattr(attr_meta, newsbox_opt):
newsbox_opts[opt] = getattr(attr_meta, newsbox_opt)
delattr(attr_meta, newsbox_opt)
if 'trans_fieldnames' not in newsbox_opts:
newsbox_opts['trans_fieldnames'] = []
# register newsbox_objects manager
class_attrs['newsbox_objects'] = NewsboxManager()
# Call specific metaclass processor of all bases
if not abstract:
for base in class_bases:
try:
processor = base._newsbox_meta['metaclass_base_processor']
except (AttributeError, KeyError):
continue
processor = getattr(sys.modules[base.__module__], processor)
processor(mcls, class_name, class_bases, class_attrs, base, newsbox_opts)
for base in class_bases:
try:
processor = base._newsbox_meta['metaclass_final_processor']
except (AttributeError, KeyError):
continue
processor = getattr(sys.modules[base.__module__], processor)
processor(mcls, class_name, class_bases, class_attrs, base, newsbox_opts)
class_attrs['default_manager'] = class_attrs['newsbox_objects']
class_attrs['objects'] = class_attrs['newsbox_objects']
# contructs class
cls = super_new(mcls, class_name, class_bases, class_attrs)
# Add the detail_url_name to _newsbox_meta. if not defined, we build the
# default one with this format : <app_label>:<model_name>_detail
'%s:%s_detail' % (cls._meta.app_label.lower(),
newsbox_opts.update(detail_url_name=detail_url_name,)
newsbox_models.append(cls)
cls.add_to_class('_newsbox_meta', newsbox_opts)
return cls
###################################################################################################
###################################################################################################
@python_2_unicode_compatible
class NewsboxBase(six.with_metaclass(NewsboxModelBase, models.Model)):
"""
Abstract class to build your own news
"""
# Automatic fields
newsbox_creation_date = models.DateTimeField(
verbose_name=_('creation date'),
auto_now_add=True,
editable=False)
newsbox_changed_date = models.DateTimeField(
verbose_name=_('last update date'),
auto_now=True,
editable=False)
newsbox_date = models.DateTimeField(
verbose_name=_('date'),
default=None, db_index=False,
null=True, blank=True,
newsbox_publication_start_date = models.DateTimeField(
verbose_name=_('publication start date'),
default=datetime.now, db_index=True,
help_text=_(
'When the news should go live. '
'Status must be "Published" for news to go live.'),
null=False, blank=False)
newsbox_published = models.BooleanField(
verbose_name=_('published'),
blank=True,
default=True,)
default_manager = NewsboxManager()
objects = default_manager
class Meta:
abstract = True
verbose_name = _('news')
verbose_name_plural = _('news')
ordering = ('-newsbox_publication_start_date', )
newsbox_metaclass_base_processor = 'newsbox_mcls_processor'
permissions = (
('publish', _('Can publish')),
)
@classmethod
def q_published(cls):
# Only published News
q = models.Q(newsbox_published=True)
# exclude news with publication start date in the future
q &= models.Q(newsbox_publication_start_date__lte=now())
@classmethod
def _has_perm(cls, action, user, obj=None):
if user.is_superuser:
return user.has_perm('%s.%s_%s' % (cls._meta.app_label, action, cls._meta.model_name), obj)
@classmethod
def has_publish_permission(cls, user, obj=None):
return cls._has_perm('publish', user, obj) or cls._has_perm('change', user, obj)
@classmethod
def has_change_permission(cls, user, obj=None):
return cls._has_perm('change', user, obj)
def __str__(self):
return six.text_type(self.newsbox_title)
def is_published(self):
if not self.newsbox_published:
return False
return now() >= self.newsbox_publication_start_date
def publish(self, commit=True):
updated = False
if not self.newsbox_published:
self.newsbox_published = True
updated = True
now_datetime = now()
if now_datetime < self.newsbox_publication_start_date:
self.newsbox_publication_start_date = now_datetime
updated = True
if updated and commit:
self.save()
return updated
def unpublish(self, commit=True):
updated = False
if self.newsbox_published:
self.newsbox_published = False
updated = True
if updated and commit:
self.save()
return updated
def get_slug(self, *args, **kwargs):
return self.newsbox_slug
def get_absolute_url(self, *args, **kwargs):
return reverse(self._newsbox_meta['detail_url_name'], args=(
self.newsbox_publication_start_date.strftime("%Y"),
self.newsbox_publication_start_date.strftime("%m"),
self.newsbox_publication_start_date.strftime("%d"),
def save(self, *args, **kwargs):
# be sure to have a publication start date
if not self.newsbox_publication_start_date:
self.newsbox_publication_start_date = self.newsbox_creation_date
super(NewsboxBase, self).save(*args, **kwargs)
###################################################################################################
###################################################################################################
class NewsboxExpiredBase(six.with_metaclass(NewsboxModelBase, models.Model)):
"""
Define News which expires in time and will not be displayed in front
"""
newsbox_publication_end_date = models.DateTimeField(
verbose_name=_('publication end date'),
null=True, blank=True, db_index=True,
help_text=_(
'When to expire the news.'
'Leave empty to never expire.'))
class Meta:
abstract = True
@classmethod
def q_published(cls):
q = super(NewsboxExpiredBase, cls).q_published()
# exclude news which are obsolete
q &= (models.Q(newsbox_publication_end_date__gt=now()) |
models.Q(newsbox_publication_end_date__isnull=True))
@property
def is_published(self):
if not super(NewsboxExpiredBase, self).is_published:
return False
return not self.newsbox_publication_end_date or now() <= self.newsbox_publication_end_date
def publish(self, commit=True):
updated = super(NewsboxExpiredBase, self).publish(commit=False)
now_datetime = now()
if now_datetime > self.newsbox_publication_end_date:
self.newsbox_publication_end_date = None
updated = True
if updated and commit:
self.save()
return updated
###################################################################################################
###################################################################################################
class NewsboxSEOBase(six.with_metaclass(NewsboxModelBase, models.Model)):
"""
Define News which have SEO fields
"""
# SEO Fields
newsbox_indexed = models.BooleanField(
verbose_name=_('indexed'),
default=True,
help_text=_('An unindexed news will not be indexed by search engines.'))
class Meta:
abstract = True
newsbox_metaclass_base_processor = 'newsboxseo_mcls_processor'
def get_meta_informations(self):
if not hasattr(self, '_meta_informations'):
self._meta_informations = {
'description': self.newsbox_meta_description,
'keywords': self.newsbox_meta_keywords,
'robots': [],
'title': '%s' % self,
}
if not self.newsbox_indexed:
self._meta_informations['robots'].append('noindex')
if self.newsbox_meta_description:
self._meta_informations['robots'].append('noodp')
return self._meta_informations