From d106a19bdc212b6540b88d14fa8cb02336908bee Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sat, 4 Jan 2014 17:07:09 +0100 Subject: [PATCH] Initial import --- djangocms_blog/__init__.py | 1 + djangocms_blog/admin.py | 40 +++ djangocms_blog/cms_app.py | 12 + djangocms_blog/cms_plugins.py | 63 ++++ djangocms_blog/cms_toolbar.py | 17 + djangocms_blog/feeds.py | 34 ++ djangocms_blog/fields.py | 32 ++ djangocms_blog/managers.py | 128 +++++++ djangocms_blog/migrations/0001_initial.py | 311 ++++++++++++++++++ djangocms_blog/migrations/__init__.py | 0 djangocms_blog/models.py | 153 +++++++++ djangocms_blog/settings.py | 17 + djangocms_blog/sitemaps/__init__.py | 2 + djangocms_blog/sitemaps/sitemap.py | 15 + djangocms_blog/static/css/djangocms_blog.css | 0 djangocms_blog/static/img/.gitignore | 0 djangocms_blog/static/js/djangocms_blog.js | 0 .../templates/djangocms_blog/base.html | 7 + .../djangocms_blog/includes/blog_item.html | 29 ++ .../djangocms_blog/plugins/archive.html | 24 ++ .../djangocms_blog/plugins/authors.html | 11 + .../plugins/latest_entries.html | 7 + .../djangocms_blog/plugins/tags.html | 12 + .../djangocms_blog/post_archive.html | 1 + .../templates/djangocms_blog/post_detail.html | 32 ++ .../templates/djangocms_blog/post_list.html | 25 ++ djangocms_blog/urls.py | 19 ++ djangocms_blog/views.py | 111 +++++++ 28 files changed, 1103 insertions(+) create mode 100644 djangocms_blog/__init__.py create mode 100755 djangocms_blog/admin.py create mode 100644 djangocms_blog/cms_app.py create mode 100644 djangocms_blog/cms_plugins.py create mode 100644 djangocms_blog/cms_toolbar.py create mode 100644 djangocms_blog/feeds.py create mode 100644 djangocms_blog/fields.py create mode 100644 djangocms_blog/managers.py create mode 100644 djangocms_blog/migrations/0001_initial.py create mode 100644 djangocms_blog/migrations/__init__.py create mode 100644 djangocms_blog/models.py create mode 100644 djangocms_blog/settings.py create mode 100644 djangocms_blog/sitemaps/__init__.py create mode 100644 djangocms_blog/sitemaps/sitemap.py create mode 100644 djangocms_blog/static/css/djangocms_blog.css create mode 100644 djangocms_blog/static/img/.gitignore create mode 100644 djangocms_blog/static/js/djangocms_blog.js create mode 100644 djangocms_blog/templates/djangocms_blog/base.html create mode 100644 djangocms_blog/templates/djangocms_blog/includes/blog_item.html create mode 100644 djangocms_blog/templates/djangocms_blog/plugins/archive.html create mode 100644 djangocms_blog/templates/djangocms_blog/plugins/authors.html create mode 100644 djangocms_blog/templates/djangocms_blog/plugins/latest_entries.html create mode 100644 djangocms_blog/templates/djangocms_blog/plugins/tags.html create mode 100644 djangocms_blog/templates/djangocms_blog/post_archive.html create mode 100644 djangocms_blog/templates/djangocms_blog/post_detail.html create mode 100644 djangocms_blog/templates/djangocms_blog/post_list.html create mode 100644 djangocms_blog/urls.py create mode 100644 djangocms_blog/views.py diff --git a/djangocms_blog/__init__.py b/djangocms_blog/__init__.py new file mode 100644 index 0000000..541f859 --- /dev/null +++ b/djangocms_blog/__init__.py @@ -0,0 +1 @@ +__version__ = '0.1.0' \ No newline at end of file diff --git a/djangocms_blog/admin.py b/djangocms_blog/admin.py new file mode 100755 index 0000000..e032e24 --- /dev/null +++ b/djangocms_blog/admin.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +from admin_enhancer.admin import EnhancedModelAdminMixin +from cms.admin.placeholderadmin import PlaceholderAdmin, FrontendEditableAdmin +from django.contrib import admin +from hvad.admin import TranslatableAdmin + +from .models import Post, BlogCategory + + +class BlogCategoryAdmin(EnhancedModelAdminMixin, TranslatableAdmin): + model = BlogCategory + + +class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdmin, + PlaceholderAdmin, TranslatableAdmin): + list_display = ['title', 'author', 'date_published', 'date_published_end'] + date_hierarchy = 'date_published' + raw_id_fields = ['author'] + frontend_editable_fields = ("title", "abstract") + enhance_exclude = ('main_image', 'tags') + + fieldsets = [ + (None, { + 'fields': [('title', 'slug', 'publish'), + ('categories', 'tags'), + ('date_published', 'date_published_end'), 'author'] + }), + (None, { + 'fields': [('main_image', 'main_image_thumbnail', 'main_image_full'), + 'abstract'] + }), + ] + + def save_model(self, request, obj, form, change): + if not obj.author_id: + obj.author = request.user + super(PostAdmin, self).save_model(request, obj, form, change) + +admin.site.register(BlogCategory, BlogCategoryAdmin) +admin.site.register(Post, PostAdmin) diff --git a/djangocms_blog/cms_app.py b/djangocms_blog/cms_app.py new file mode 100644 index 0000000..85fdc23 --- /dev/null +++ b/djangocms_blog/cms_app.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +from cms.app_base import CMSApp +from cms.apphook_pool import apphook_pool +from django.utils.translation import ugettext_lazy as _ + + +class BlogApp(CMSApp): + name = _("Blog") + urls = ["djangocms_blog.urls"] + app_name = 'djangocms_blog' + +apphook_pool.register(BlogApp) \ No newline at end of file diff --git a/djangocms_blog/cms_plugins.py b/djangocms_blog/cms_plugins.py new file mode 100644 index 0000000..9d4d740 --- /dev/null +++ b/djangocms_blog/cms_plugins.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +from django.utils.translation import ugettext_lazy as _ + +from cms.models.pluginmodel import CMSPlugin +from cms.plugin_base import CMSPluginBase +from cms.plugin_pool import plugin_pool + +from .models import AuthorEntriesPlugin, LatestPostsPlugin, Post + + +class BlogPlugin(CMSPluginBase): + + module = 'Blog' + + +class LatestEntriesPlugin(BlogPlugin): + + render_template = 'djangocms_blog/plugins/latest_entries.html' + name = _('Latest Blog Entries') + model = LatestPostsPlugin + + def render(self, context, instance, placeholder): + context['instance'] = instance + return context + + +class AuthorPostsPlugin(BlogPlugin): + module = _('Blog') + name = _('Author Blog posts') + model = AuthorEntriesPlugin + render_template = 'djangocms_blog/plugins/authors.html' + filter_horizontal = ['authors'] + + def render(self, context, instance, placeholder): + context['instance'] = instance + return context + + +class BlogTagsPlugin(BlogPlugin): + module = _('Blog') + name = _('Tags') + model = CMSPlugin + render_template = 'djangocms_blog/plugins/tags.html' + + def render(self, context, instance, placeholder): + context['tags'] = Post.objects.tag_cloud(queryset=Post.objects.published()) + return context + + +class BlogArchivePlugin(BlogPlugin): + module = _('Blog') + name = _('Archive') + model = CMSPlugin + render_template = 'djangocms_blog/plugins/archive.html' + + def render(self, context, instance, placeholder): + context['dates'] = Post.objects.get_months(queryset=Post.objects.published()) + return context + +plugin_pool.register_plugin(LatestEntriesPlugin) +plugin_pool.register_plugin(AuthorPostsPlugin) +plugin_pool.register_plugin(BlogTagsPlugin) +plugin_pool.register_plugin(BlogArchivePlugin) diff --git a/djangocms_blog/cms_toolbar.py b/djangocms_blog/cms_toolbar.py new file mode 100644 index 0000000..73a34ea --- /dev/null +++ b/djangocms_blog/cms_toolbar.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext_lazy as _ + +from cms.toolbar_base import CMSToolbar +from cms.toolbar_pool import toolbar_pool + + +@toolbar_pool.register +class BlogToolbar(CMSToolbar): + + def populate(self): + admin_menu = self.toolbar.get_or_create_menu("djangocms_blog", _('Blog')) + url = reverse('admin:djangocms_blog_post_changelist') + admin_menu.add_modal_item(_('Post list'), url=url) + url = reverse('admin:djangocms_blog_post_add') + admin_menu.add_modal_item(_('Add post'), url=url) \ No newline at end of file diff --git a/djangocms_blog/feeds.py b/djangocms_blog/feeds.py new file mode 100644 index 0000000..aadc5f8 --- /dev/null +++ b/djangocms_blog/feeds.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from django.contrib.sites.models import Site +from django.contrib.syndication.views import Feed +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext as _ + +from .models import Post + + +class LatestEntriesFeed(Feed): + + def link(self): + return reverse('djangocms_blog:latest-posts') + + def title(self): + return _('Blog posts on %(site_name)s') % {'site_name': Site.objects.get_current().name} + + def items(self, obj): + return Post.objects.published().order_by('-date_published')[:10] + + def item_title(self, item): + return item.lazy_translation_getter('title') + + def item_description(self, item): + return item.lazy_translation_getter('abstract') + + +class TagFeed(LatestEntriesFeed): + + def get_object(self, request, tag): + return tag + + def items(self, obj): + return Post.objects.published().filter(tags__slug=obj)[:10] diff --git a/djangocms_blog/fields.py b/djangocms_blog/fields.py new file mode 100644 index 0000000..41241ca --- /dev/null +++ b/djangocms_blog/fields.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from django.utils.translation import ugettext_lazy as _ + +from django.db import models +from django.contrib.auth.models import User + +from south.modelsinspector import add_introspection_rules +add_introspection_rules([], ["^djangocms_blog\.fields\.UsersWithPermsManyToManyField"]) + + +class UsersWithPermsManyToManyField(models.ManyToManyField): + + def __init__(self, perms, **kwargs): + + (super(UsersWithPermsManyToManyField, self) + .__init__(User, limit_choices_to=self.get_limit_choices_to(perms), + **kwargs)) + + def get_limit_choices_to(self, perms): + return (models.Q(user_permissions__codename__in=perms) + | models.Q(groups__permissions__codename__in=perms) + | models.Q(is_superuser=True)) + + def formfield(self, **kwargs): + db = kwargs.pop('using', None) + defaults = { + 'queryset': (self.rel.to._default_manager.using(db) + .complex_filter(self.rel.limit_choices_to).distinct()) + } + defaults.update(kwargs) + + return super(UsersWithPermsManyToManyField, self).formfield(**defaults) \ No newline at end of file diff --git a/djangocms_blog/managers.py b/djangocms_blog/managers.py new file mode 100644 index 0000000..dd61ab1 --- /dev/null +++ b/djangocms_blog/managers.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +from collections import Counter +import datetime + +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from hvad.manager import TranslationManager + +from .settings import BLOG_TAGCLOUD_MIN, BLOG_TAGCLOUD_MAX + + +class TaggedFilterItem(object): + + def tagged(self, other_model=None, queryset=None): + """ + Restituisce una queryset di elementi del model taggati, + o con gli stessi tag di un model o un queryset + """ + tags = self._taglist(other_model, queryset) + return self.get_query_set().filter(taglist__in=tags) + + def _taglist(self, other_model=None, queryset=None): + """ + Restituisce una lista di id di tag comuni al model corrente e al model + o queryset passati come argomento + """ + from taggit.models import TaggedItem + filtro = None + if queryset is not None: + filtro = set() + for item in queryset.all(): + filtro.update(item.tags.all()) + filtro = set([tag.id for tag in filtro]) + elif other_model is not None: + filtro = set(TaggedItem.objects.filter(content_type__model=other_model.__name__.lower()).values_list('tag_id', flat=True)) + tags = set(TaggedItem.objects.filter(content_type__model=self.model.__name__.lower()).values_list('tag_id', flat=True)) + if filtro is not None: + tags = tags.intersection(filtro) + return list(tags) + + def tag_list(self, other_model=None, queryset=None): + """ + Restituisce un queryset di tag comuni al model corrente e + al model o queryset passati come argomento + """ + from taggit.models import Tag + return Tag.objects.filter(id__in=self._taglist(other_model, queryset)) + + def tag_list_slug(self, other_model=None, queryset=None): + qs = self.tag_list(other_model, queryset) + return qs.values("slug") + + def tag_cloud(self, other_model=None, queryset=None, start=None): + from taggit_templatetags.templatetags.taggit_extras import get_weight_fun + from taggit.models import TaggedItem + tag_ids = self._taglist(other_model, queryset) + tagquery = TaggedItem.tags_for(self.model).filter(id__in=tag_ids) + if start is not None: + tagquery = tagquery.filter(name__istartswith=start) + tagquery = tagquery.annotate(count=models.Count('taggit_taggeditem_items')) + count = tagquery.values_list('count', flat=True) + if len(count) > 0: + weight_fun = get_weight_fun(BLOG_TAGCLOUD_MIN, + BLOG_TAGCLOUD_MAX, + min(count), max(count)) + tagquery = tagquery.order_by('name') + for tag in tagquery: + tag.weight = weight_fun(tag.count) + return tagquery + + +class GenericDateTaggedManager(TaggedFilterItem, TranslationManager): + use_for_related_fields = True + start_date_field = "date_published" + end_date_field = "date_published_end" + publish_field = "publish" + + def published(self, qs=None): + qs = self.published_future(qs) + if self.start_date_field: + return qs.filter( + **{"%s__lte" % self.start_date_field: datetime.datetime.now()}) + else: + return qs + + def published_future(self, qs=None): + if not qs: + qs = self.get_query_set().all() + if self.end_date_field: + qfilter = ( + models.Q(**{"%s__gte" % self.end_date_field: datetime.datetime.now()}) + | models.Q(**{"%s__isnull" % self.end_date_field: True}) + ) + qs = qs.filter(qfilter) + return qs.filter(**{self.publish_field: True}) + + def archived(self, qs=None): + if not qs: + qs = self.get_query_set().all() + if self.end_date_field: + qfilter = ( + models.Q(**{"%s__lte" % self.end_date_field: datetime.datetime.now()}) + | models.Q(**{"%s__isnull" % self.end_date_field: False}) + ) + qs = qs.filter(qfilter) + return qs.filter(**{self.publish_field: True}) + + def available(self, qs=None): + if not qs: + qs = self.get_query_set().all() + return qs.filter(**{self.publish_field: True}) + + def filter_by_language(self, language): + qs = self.get_query_set() + return qs.filter(models.Q(language__isnull=True) | models.Q(language=language)) + + def get_months(self, queryset=None): + """Get months with aggregatet count (how much posts is in the month). Results are ordered by date.""" + # done via naive way as django's having tough time while aggregating on date fields + if not queryset: + queryset = self.get_query_set() + dates = queryset.values_list(self.start_date_field, flat=True) + dates = [(x.year, x.month) for x in dates] + date_counter = Counter(dates) + dates = set(dates) + dates = sorted(dates, reverse=True) + return [{'date': datetime.date(year=year, month=month, day=1), + 'count': date_counter[year, month]} for year, month in dates] diff --git a/djangocms_blog/migrations/0001_initial.py b/djangocms_blog/migrations/0001_initial.py new file mode 100644 index 0000000..ac5d495 --- /dev/null +++ b/djangocms_blog/migrations/0001_initial.py @@ -0,0 +1,311 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'BlogCategoryTranslation' + db.create_table(u'djangocms_blog_blogcategory_translation', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, blank=True)), + ('language_code', self.gf('django.db.models.fields.CharField')(max_length=15, db_index=True)), + ('master', self.gf('django.db.models.fields.related.ForeignKey')(related_name='translations', null=True, to=orm['djangocms_blog.BlogCategory'])), + )) + db.send_create_signal(u'djangocms_blog', ['BlogCategoryTranslation']) + + # Adding unique constraint on 'BlogCategoryTranslation', fields ['language_code', 'master'] + db.create_unique(u'djangocms_blog_blogcategory_translation', ['language_code', 'master_id']) + + # Adding model 'BlogCategory' + db.create_table(u'djangocms_blog_blogcategory', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('parent', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['djangocms_blog.BlogCategory'], null=True, blank=True)), + ('date_created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('date_modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)), + )) + db.send_create_signal(u'djangocms_blog', ['BlogCategory']) + + # Adding model 'PostTranslation' + db.create_table(u'djangocms_blog_post_translation', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, blank=True)), + ('abstract', self.gf('djangocms_text_ckeditor.fields.HTMLField')()), + ('language_code', self.gf('django.db.models.fields.CharField')(max_length=15, db_index=True)), + ('master', self.gf('django.db.models.fields.related.ForeignKey')(related_name='translations', null=True, to=orm['djangocms_blog.Post'])), + )) + db.send_create_signal(u'djangocms_blog', ['PostTranslation']) + + # Adding unique constraint on 'PostTranslation', fields ['language_code', 'master'] + db.create_unique(u'djangocms_blog_post_translation', ['language_code', 'master_id']) + + # Adding model 'Post' + db.create_table(u'djangocms_blog_post', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('date_created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('date_modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)), + ('date_published', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('date_published_end', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + ('publish', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('main_image', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['filer.Image'], null=True, blank=True)), + ('main_image_thumbnail', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='blog_post_thumbnail', null=True, to=orm['cmsplugin_filer_image.ThumbnailOption'])), + ('main_image_full', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='blog_post_full', null=True, to=orm['cmsplugin_filer_image.ThumbnailOption'])), + ('content', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['cms.Placeholder'], null=True)), + )) + db.send_create_signal(u'djangocms_blog', ['Post']) + + # Adding M2M table for field categories on 'Post' + m2m_table_name = db.shorten_name(u'djangocms_blog_post_categories') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('post', models.ForeignKey(orm[u'djangocms_blog.post'], null=False)), + ('blogcategory', models.ForeignKey(orm[u'djangocms_blog.blogcategory'], null=False)) + )) + db.create_unique(m2m_table_name, ['post_id', 'blogcategory_id']) + + # Adding model 'LatestPostsPlugin' + db.create_table(u'cmsplugin_latestpostsplugin', ( + (u'cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)), + ('latest_posts', self.gf('django.db.models.fields.IntegerField')(default=5)), + )) + db.send_create_signal(u'djangocms_blog', ['LatestPostsPlugin']) + + # Adding M2M table for field tags on 'LatestPostsPlugin' + m2m_table_name = db.shorten_name(u'djangocms_blog_latestpostsplugin_tags') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('latestpostsplugin', models.ForeignKey(orm[u'djangocms_blog.latestpostsplugin'], null=False)), + ('tag', models.ForeignKey(orm[u'taggit.tag'], null=False)) + )) + db.create_unique(m2m_table_name, ['latestpostsplugin_id', 'tag_id']) + + # Adding model 'AuthorEntriesPlugin' + db.create_table(u'cmsplugin_authorentriesplugin', ( + (u'cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)), + ('latest_posts', self.gf('django.db.models.fields.IntegerField')(default=5)), + )) + db.send_create_signal(u'djangocms_blog', ['AuthorEntriesPlugin']) + + # Adding M2M table for field authors on 'AuthorEntriesPlugin' + m2m_table_name = db.shorten_name(u'djangocms_blog_authorentriesplugin_authors') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('authorentriesplugin', models.ForeignKey(orm[u'djangocms_blog.authorentriesplugin'], null=False)), + ('user', models.ForeignKey(orm[u'auth.user'], null=False)) + )) + db.create_unique(m2m_table_name, ['authorentriesplugin_id', 'user_id']) + + + def backwards(self, orm): + # Removing unique constraint on 'PostTranslation', fields ['language_code', 'master'] + db.delete_unique(u'djangocms_blog_post_translation', ['language_code', 'master_id']) + + # Removing unique constraint on 'BlogCategoryTranslation', fields ['language_code', 'master'] + db.delete_unique(u'djangocms_blog_blogcategory_translation', ['language_code', 'master_id']) + + # Deleting model 'BlogCategoryTranslation' + db.delete_table(u'djangocms_blog_blogcategory_translation') + + # Deleting model 'BlogCategory' + db.delete_table(u'djangocms_blog_blogcategory') + + # Deleting model 'PostTranslation' + db.delete_table(u'djangocms_blog_post_translation') + + # Deleting model 'Post' + db.delete_table(u'djangocms_blog_post') + + # Removing M2M table for field categories on 'Post' + db.delete_table(db.shorten_name(u'djangocms_blog_post_categories')) + + # Deleting model 'LatestPostsPlugin' + db.delete_table(u'cmsplugin_latestpostsplugin') + + # Removing M2M table for field tags on 'LatestPostsPlugin' + db.delete_table(db.shorten_name(u'djangocms_blog_latestpostsplugin_tags')) + + # Deleting model 'AuthorEntriesPlugin' + db.delete_table(u'cmsplugin_authorentriesplugin') + + # Removing M2M table for field authors on 'AuthorEntriesPlugin' + db.delete_table(db.shorten_name(u'djangocms_blog_authorentriesplugin_authors')) + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'cms.cmsplugin': { + 'Meta': {'object_name': 'CMSPlugin'}, + 'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}), + 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), + 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}) + }, + 'cms.placeholder': { + 'Meta': {'object_name': 'Placeholder'}, + 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}) + }, + u'cmsplugin_filer_image.thumbnailoption': { + 'Meta': {'ordering': "('width', 'height')", 'object_name': 'ThumbnailOption'}, + 'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'height': ('django.db.models.fields.IntegerField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'width': ('django.db.models.fields.IntegerField', [], {}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'djangocms_blog.authorentriesplugin': { + 'Meta': {'object_name': 'AuthorEntriesPlugin', 'db_table': "u'cmsplugin_authorentriesplugin'", '_ormbases': ['cms.CMSPlugin']}, + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'}), + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + u'djangocms_blog.blogcategory': { + 'Meta': {'object_name': 'BlogCategory'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'null': 'True', 'blank': 'True'}) + }, + u'djangocms_blog.blogcategorytranslation': { + 'Meta': {'unique_together': "[('language_code', 'master')]", 'object_name': 'BlogCategoryTranslation', 'db_table': "u'djangocms_blog_blogcategory_translation'"}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + 'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.BlogCategory']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}) + }, + u'djangocms_blog.latestpostsplugin': { + 'Meta': {'object_name': 'LatestPostsPlugin', 'db_table': "u'cmsplugin_latestpostsplugin'", '_ormbases': ['cms.CMSPlugin']}, + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['taggit.Tag']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'djangocms_blog.post': { + 'Meta': {'object_name': 'Post'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'blog_posts'", 'symmetrical': 'False', 'to': u"orm['djangocms_blog.BlogCategory']"}), + 'content': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_published_end': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_image': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['filer.Image']", 'null': 'True', 'blank': 'True'}), + 'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'blog_post_full'", 'null': 'True', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}), + 'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'blog_post_thumbnail'", 'null': 'True', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}), + 'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + u'djangocms_blog.posttranslation': { + 'Meta': {'unique_together': "[('language_code', 'master')]", 'object_name': 'PostTranslation', 'db_table': "u'djangocms_blog_post_translation'"}, + 'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + 'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.Post']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'filer.file': { + 'Meta': {'object_name': 'File'}, + '_file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'folder': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'all_files'", 'null': 'True', 'to': "orm['filer.Folder']"}), + 'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}), + 'original_filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'owned_files'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_filer.file_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}), + 'sha1': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40', 'blank': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'filer.folder': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('parent', 'name'),)", 'object_name': 'Folder'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'filer_owned_folders'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['filer.Folder']"}), + 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'filer.image': { + 'Meta': {'object_name': 'Image', '_ormbases': ['filer.File']}, + '_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + '_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'author': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'date_taken': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'default_alt_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'default_caption': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['filer.File']", 'unique': 'True', 'primary_key': 'True'}), + 'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'related_url': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}), + 'subject_location': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'blank': 'True'}) + }, + u'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'}) + } + } + + complete_apps = ['djangocms_blog'] \ No newline at end of file diff --git a/djangocms_blog/migrations/__init__.py b/djangocms_blog/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py new file mode 100644 index 0000000..41f8e1e --- /dev/null +++ b/djangocms_blog/models.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +from cms.models import PlaceholderField, CMSPlugin +from cmsplugin_filer_image.models import ThumbnailOption +from django.contrib.auth.models import User +from django.core.urlresolvers import reverse +from django.db import models +from django.utils import timezone +from django.utils.text import slugify +from django.utils.translation import ugettext_lazy as _ +from djangocms_text_ckeditor.fields import HTMLField +from filer.fields.image import FilerImageField +from hvad.models import TranslatableModel, TranslatedFields +from taggit_autosuggest.managers import TaggableManager + +from .managers import GenericDateTaggedManager +from . import settings + + +class BlogCategory(TranslatableModel): + """ + Blog category + """ + parent = models.ForeignKey('self', verbose_name=_('parent'), null=True, + blank=True) + date_created = models.DateTimeField(_('created at'), auto_now_add=True) + date_modified = models.DateTimeField(_('modified at'), auto_now=True) + + translations = TranslatedFields( + name=models.CharField(_('name'), max_length=255), + slug=models.SlugField(_('slug'), blank=True), + ) + + class Meta: + verbose_name = _('blog category') + verbose_name_plural = _('blog categories') + + def __unicode__(self): + return self.lazy_translation_getter('name') + + def save(self, *args, **kwargs): + super(BlogCategory, self).save(*args, **kwargs) + for item in self._meta.translations_model.objects.filter(master__pk=self.pk): + title = getattr(item, "name", False) + if title and not item.slug: + item.slug = slugify(title) + item.save() + + +class Post(TranslatableModel): + """ + Blog post + """ + author = models.ForeignKey(User) + + date_created = models.DateTimeField(auto_now_add=True) + date_modified = models.DateTimeField(auto_now=True) + date_published = models.DateTimeField(_('Published Since'), + default=timezone.now) + date_published_end = models.DateTimeField(_('Published Until'), null=True, + blank=True) + publish = models.BooleanField(default=False) + categories = models.ManyToManyField(BlogCategory, verbose_name=_('category'), + related_name='blog_posts',) + main_image = FilerImageField(blank=True, null=True) + main_image_thumbnail = models.ForeignKey(ThumbnailOption, + related_name='blog_post_thumbnail', + blank=True, null=True) + main_image_full = models.ForeignKey(ThumbnailOption, + related_name='blog_post_full', + blank=True, null=True) + + translations = TranslatedFields( + title=models.CharField(max_length=255), + slug=models.SlugField(_('slug'), blank=True), + abstract=HTMLField(), + + ) + content = PlaceholderField("post_content") + + objects = GenericDateTaggedManager() + tags = TaggableManager(blank=True) + + class Meta: + verbose_name = _('blog post') + verbose_name_plural = _('blog post') + + def __unicode__(self): + return self.lazy_translation_getter('title') + + def save(self, *args, **kwargs): + super(Post, self).save(*args, **kwargs) + for item in self._meta.translations_model.objects.filter(master__pk=self.pk): + title = getattr(item, "title", False) + if title and not item.slug: + item.slug = slugify(title) + item.save() + + def get_absolute_url(self): + kwargs = {'year': self.date_published.year, + 'month': self.date_published.month, + 'day': self.date_published.day, + 'slug': self.slug} + return reverse('djangocms_blog:post-detail', kwargs=kwargs) + + def thumbnail_options(self): + if self.main_image_thumbnail_id: + return self.main_image_thumbnail.as_dict() + else: + return settings.BLOG_IMAGE_THUMBNAIL_SIZE + + def full_image_options(self): + if self.main_image_fulll_id: + return self.main_image_full.as_dict() + else: + return settings.BLOG_IMAGE_FULL_SIZE + + +class LatestPostsPlugin(CMSPlugin): + + latest_posts = models.IntegerField(default=5, help_text=_('The number of latests posts to be displayed.')) + tags = models.ManyToManyField('taggit.Tag', blank=True, help_text=_('Show only the blog posts tagged with chosen tags.')) + + def __unicode__(self): + return u"%s latest post by tag" % self.latest_posts + + def copy_relations(self, oldinstance): + self.tags = oldinstance.tags.all() + + def get_posts(self): + posts = Post.objects.published() + tags = list(self.tags.all()) + if tags: + posts = posts.filter(tags__in=tags) + return posts[:self.latest_posts] + + +class AuthorEntriesPlugin(CMSPlugin): + authors = models.ManyToManyField(User, verbose_name=_('Authors')) + latest_posts = models.IntegerField(default=5, help_text=_('The number of author entries to be displayed.')) + + def __unicode__(self): + return u"%s latest post by author" % self.latest_posts + + def copy_relations(self, oldinstance): + self.authors = oldinstance.authors.all() + + def get_posts(self): + posts = (Post.objects.published().filter(author__in=self.authors.all())) + return posts[:self.latest_posts] + + def get_authors(self): + authors = self.authors.all() + return authors diff --git a/djangocms_blog/settings.py b/djangocms_blog/settings.py new file mode 100644 index 0000000..05dbd71 --- /dev/null +++ b/djangocms_blog/settings.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +from django.utils.translation import ugettext_lazy as _ + +BLOG_IMAGE_THUMBNAIL_SIZE = { + 'size': '120x120', + 'crop': True, + 'upscale': False +} + +BLOG_IMAGE_FULL_SIZE = { + 'size': '640x120', + 'crop': True, + 'upscale': False +} + +BLOG_TAGCLOUD_MIN = 1 +BLOG_TAGCLOUD_MAX = 10 \ No newline at end of file diff --git a/djangocms_blog/sitemaps/__init__.py b/djangocms_blog/sitemaps/__init__.py new file mode 100644 index 0000000..2d97367 --- /dev/null +++ b/djangocms_blog/sitemaps/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from sitemap import BlogSitemap diff --git a/djangocms_blog/sitemaps/sitemap.py b/djangocms_blog/sitemaps/sitemap.py new file mode 100644 index 0000000..b23e5f7 --- /dev/null +++ b/djangocms_blog/sitemaps/sitemap.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from django.contrib.sitemaps import Sitemap + +from ..models import Post + + +class BlogSitemap(Sitemap): + changefreq = "never" + priority = 0.5 + + def items(self): + return Post.objects.published() + + def lastmod(self, obj): + return obj.date_modified diff --git a/djangocms_blog/static/css/djangocms_blog.css b/djangocms_blog/static/css/djangocms_blog.css new file mode 100644 index 0000000..e69de29 diff --git a/djangocms_blog/static/img/.gitignore b/djangocms_blog/static/img/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/djangocms_blog/static/js/djangocms_blog.js b/djangocms_blog/static/js/djangocms_blog.js new file mode 100644 index 0000000..e69de29 diff --git a/djangocms_blog/templates/djangocms_blog/base.html b/djangocms_blog/templates/djangocms_blog/base.html new file mode 100644 index 0000000..db08d48 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/base.html @@ -0,0 +1,7 @@ +{% extends "interne.html" %} + +{% block contenuto %} +
+ {% block content_blog %}{% endblock %} +
+{% endblock %} \ No newline at end of file diff --git a/djangocms_blog/templates/djangocms_blog/includes/blog_item.html b/djangocms_blog/templates/djangocms_blog/includes/blog_item.html new file mode 100644 index 0000000..0d8ba7d --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/includes/blog_item.html @@ -0,0 +1,29 @@ +{% load i18n thumbnail cms_tags nephila_django %} +{% load url from future %} + +
+
+

{% show_editable_model post "title" %}

+ {% block blog_meta %} + {% html5time post.date_published "d F Y" "blog-date" %} +
{% trans "by" %} {{ post.author }}
+ {% if post.tags.exists %} + + {% endif %} + {% endblock %} +
+ {% if image and post.main_image_id %} +
+ {% thumbnail post.main_image thumbnail_options.size crop=thumbnail_options.crop upscale=thumbnail_options.upscale as thumb %} + +
+ {% endif %} +
{% show_editable_model post "abstract" "abstract" "" safe %}
+ +
\ No newline at end of file diff --git a/djangocms_blog/templates/djangocms_blog/plugins/archive.html b/djangocms_blog/templates/djangocms_blog/plugins/archive.html new file mode 100644 index 0000000..c9bd3eb --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/plugins/archive.html @@ -0,0 +1,24 @@ +{% load i18n %} +{% load url from future %} + +
+ {% regroup dates by date.year as years %} + {% with current_year=year current_month=month %} + + {% endwith %} +
diff --git a/djangocms_blog/templates/djangocms_blog/plugins/authors.html b/djangocms_blog/templates/djangocms_blog/plugins/authors.html new file mode 100644 index 0000000..495739e --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/plugins/authors.html @@ -0,0 +1,11 @@ +{% load i18n %} +
+ +
\ No newline at end of file diff --git a/djangocms_blog/templates/djangocms_blog/plugins/latest_entries.html b/djangocms_blog/templates/djangocms_blog/plugins/latest_entries.html new file mode 100644 index 0000000..d4f00ab --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/plugins/latest_entries.html @@ -0,0 +1,7 @@ +
+
+ {% for post in post_list %} + {% include "djangocms_blog/includes/blog_item.html" with post=instance.get_posts %} + {% empty %} +
+
\ No newline at end of file diff --git a/djangocms_blog/templates/djangocms_blog/plugins/tags.html b/djangocms_blog/templates/djangocms_blog/plugins/tags.html new file mode 100644 index 0000000..8c17f76 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/plugins/tags.html @@ -0,0 +1,12 @@ +{% load i18n %} +{% load url from future %} + +
+ +
\ No newline at end of file diff --git a/djangocms_blog/templates/djangocms_blog/post_archive.html b/djangocms_blog/templates/djangocms_blog/post_archive.html new file mode 100644 index 0000000..fb8ff1f --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/post_archive.html @@ -0,0 +1 @@ +{% extends "djangocms_blog/post_list.html" %} \ No newline at end of file diff --git a/djangocms_blog/templates/djangocms_blog/post_detail.html b/djangocms_blog/templates/djangocms_blog/post_detail.html new file mode 100644 index 0000000..9ec4bad --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/post_detail.html @@ -0,0 +1,32 @@ +{% extends "djangocms_blog/base.html" %} +{% load i18n thumbnail cms_tags nephila_django %} +{% load url from future %} + +{% block content_blog %} +
+
+

{% show_editable_model post "title" %}

+ {% block blog_meta %} +
+ {% html5time post.date_published "d F Y" "blog-date" %} +
{% trans "by" %} {{ post.author }}
+ {% if post.tags.exists %} + + {% endif %} +
+ {% endblock %} +
+ {% if image and post.main_image_id %} +
+ {% thumbnail post.main_image thumbnail_options.size crop=thumbnail_options.crop upscale=thumbnail_options.upscale as thumb %} + +
+ {% endif %} +
{% show_editable_model post "abstract" "abstract" "" safe %}
+
{% render_placeholder post.content %}
+
+{% endblock content_blog %} \ No newline at end of file diff --git a/djangocms_blog/templates/djangocms_blog/post_list.html b/djangocms_blog/templates/djangocms_blog/post_list.html new file mode 100644 index 0000000..5ba68da --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/post_list.html @@ -0,0 +1,25 @@ +{% extends "djangocms_blog/base.html" %} +{% load i18n thumbnail %}{% spaceless %} + +{% block content_blog %} +
+ {% block blog_title %} +
+

+ {% if author %}{% trans "Entries by" %} {{ author.get_full_name }} + {% elif archive_date %}{% trans "Archive" %} – {% if month %}{{ archive_date|date:'F' }} {% endif %}{{ year }}{% elif tagged_entries %} + {% trans "Tag" %} – {{ tagged_entries|capfirst }}{% endif %} +

+
+ {% endblock %} + {% for post in post_list %} + {% include "djangocms_blog/includes/blog_item.html" with post=post image="true" %} + {% empty %} +

{% trans "No post found." %}

+ {% endfor %} + {% if author or archive_date or tagged_entries %} +

{% trans "Back" %}

+ {% endif %} +
+{% endblock %} +{% endspaceless %} \ No newline at end of file diff --git a/djangocms_blog/urls.py b/djangocms_blog/urls.py new file mode 100644 index 0000000..3afd7ba --- /dev/null +++ b/djangocms_blog/urls.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from django.conf.urls import patterns, url + +from .views import (PostListView, PostDetailView, TaggedListView, + AuthorEntriesView, PostArchiveView) +from .feeds import LatestEntriesFeed, TagFeed + + +urlpatterns = patterns( + '', + url(r'^$', PostListView.as_view(), name='latest-posts'), + url(r'^feed/$', LatestEntriesFeed(), name='latest-posts-feed'), + url(r'^(?P\d{4})/$', PostArchiveView.as_view(), name='archive-year'), + url(r'^(?P\d{4})/(?P\d{1,2})/$', PostArchiveView.as_view(), name='archive-month'), + url(r'^(?P\d{4})/(?P\d{1,2})/(?P\d{1,2})/(?P\w[-\w]*)/$', PostDetailView.as_view(), name='post-detail'), + url(r'^author/(?P[\w.@+-]+)/$', AuthorEntriesView.as_view(), name='author-posts'), + url(r'^tag/(?P[-\w]+)/$', TaggedListView.as_view(), name='tagged-posts'), + url(r'^tag/(?P[-\w]+)/feed/$', TagFeed(), name='tagged-posts-feed'), +) diff --git a/djangocms_blog/views.py b/djangocms_blog/views.py new file mode 100644 index 0000000..d90a4e6 --- /dev/null +++ b/djangocms_blog/views.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +import datetime + +from django.core.urlresolvers import resolve +from django.utils.translation import ugettext_lazy as _ +from django.views.generic import ListView, DetailView, ArchiveIndexView +from hvad.admin import TranslatableModelAdminMixin + +from .models import Post + + +class BaseBlogView(TranslatableModelAdminMixin): + + def _get_object(self, queryset=None): + if not queryset: + queryset = self.get_queryset() + model = self.model + try: + obj = queryset.get(**self.filter_kwargs()) + except self.model.DoesNotExist: + obj = None + if obj: + return obj + queryset = self.model.objects.untranslated() + try: + obj = queryset.get(**self.filter_kwargs()) + except model.DoesNotExist: + return None + new_translation = model._meta.translations_model() + new_translation.language_code = self._language(self.request) + new_translation.master = obj + setattr(obj, model._meta.translations_cache, new_translation) + return obj + + def get_queryset(self): + language = self._language(self.request) + manager = self.model._default_manager.language(language) + if not self.request.user.is_staff: + manager = manager.published() + return manager + + def render_to_response(self, context, **response_kwargs): + response_kwargs['current_app'] = resolve(self.request.path).namespace + return super(BaseBlogView, self).render_to_response(context, **response_kwargs) + + +class PostListView(BaseBlogView, ListView): + model = Post + context_object_name = 'post_list' + template_name = "djangocms_blog/post_list.html" + + +class PostDetailView(BaseBlogView, DetailView): + model = Post + context_object_name = 'post' + template_name = "djangocms_blog/post_detail.html" + + +class PostArchiveView(BaseBlogView, ArchiveIndexView): + model = Post + context_object_name = 'post_list' + template_name = "djangocms_blog/post_list.html" + date_field = 'date_published' + allow_empty = True + allow_future = True + + def get_queryset(self): + qs = super(PostArchiveView, self).get_queryset() + if 'month' in self.kwargs: + qs = qs.filter(date_published__month=self.kwargs['month']) + if 'year' in self.kwargs: + qs = qs.filter(date_published__year=self.kwargs['year']) + return qs + + def get_context_data(self, **kwargs): + kwargs['month'] = int(self.kwargs.get('month')) if 'month' in self.kwargs else None + kwargs['year'] = int(self.kwargs.get('year')) if 'year' in self.kwargs else None + if kwargs['year']: + kwargs['archive_date'] = datetime.date(kwargs['year'], kwargs['month'] or 1, 1) + return super(PostArchiveView, self).get_context_data(**kwargs) + + +class TaggedListView(BaseBlogView, ListView): + model = Post + context_object_name = 'post_list' + template_name = "djangocms_blog/post_list.html" + + def get_queryset(self): + qs = super(TaggedListView, self).get_queryset() + return qs.filter(tags__slug=self.kwargs['tag']) + + def get_context_data(self, **kwargs): + kwargs['tagged_entries'] = (self.kwargs.get('tag') + if 'tag' in self.kwargs else None) + return super(TaggedListView, self).get_context_data(**kwargs) + + +class AuthorEntriesView(BaseBlogView, ListView): + model = Post + context_object_name = 'post_list' + template_name = "djangocms_blog/post_list.html" + + def get_queryset(self): + qs = super(AuthorEntriesView, self).get_queryset() + if 'username' in self.kwargs: + qs = qs.filter(author__username=self.kwargs['username']) + return qs + + def get_context_data(self, **kwargs): + kwargs['author'] = self.kwargs.get('username') + return super(AuthorEntriesView, self).get_context_data(**kwargs)