Added base blog app
This commit is contained in:
parent
8ad0e87c18
commit
9e404aeb9b
|
@ -20,3 +20,6 @@ psycopg2==2.5.2
|
|||
|
||||
# Files
|
||||
django-filer==0.9.5
|
||||
|
||||
# Blog
|
||||
django-ckeditor-updated==4.4.0
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
from django.contrib import admin
|
||||
from .models import Entry, Tag
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext as _
|
||||
from django import forms
|
||||
from django.db import models
|
||||
from ckeditor.widgets import CKEditorWidget
|
||||
import reversion
|
||||
|
||||
|
||||
#
|
||||
# ENTRY
|
||||
#
|
||||
class EntryAdmin(reversion.VersionAdmin):
|
||||
list_display = ('title', 'date', 'status', 'tag_list', 'preview_link')
|
||||
list_display_links = ('title', )
|
||||
|
||||
list_filter = ('date', 'draft', )
|
||||
search_fields = ('title', 'content', )
|
||||
|
||||
prepopulated_fields = {"slug": ("title",)}
|
||||
|
||||
suit_form_tabs = (
|
||||
('general', _('General')),
|
||||
('content', _('Content')),
|
||||
)
|
||||
|
||||
fieldsets = [
|
||||
(None, {
|
||||
'classes': ('suit-tab suit-tab-general',),
|
||||
'fields': ('title', 'slug', 'draft', 'date', 'tags', )
|
||||
}),
|
||||
(None, {
|
||||
'classes': ('suit-tab suit-tab-content full-width',),
|
||||
'fields': ('content', )
|
||||
}),
|
||||
]
|
||||
|
||||
def preview_link(self, obj):
|
||||
return '<a href="%s">View »</a>' % (
|
||||
obj.get_absolute_url()
|
||||
)
|
||||
preview_link.allow_tags = True
|
||||
|
||||
def tag_list(self, obj):
|
||||
return ", ".join([x.name for x in obj.tags.all()])
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
if not change:
|
||||
obj.author = request.user
|
||||
super(self.__class__, self).save_model(request, obj, form, change)
|
||||
|
||||
class Media:
|
||||
#css = {
|
||||
# "all": ("ckeditor/redactor.css",)
|
||||
#}
|
||||
# js = (
|
||||
# "ckeditor/ckeditor.js",
|
||||
# "js/wysiwyg.js",
|
||||
# )
|
||||
pass
|
||||
|
||||
admin.site.register(Entry, EntryAdmin)
|
||||
|
||||
|
||||
#
|
||||
# TAG
|
||||
#
|
||||
class TagAdmin(reversion.VersionAdmin):
|
||||
pass
|
||||
|
||||
admin.site.register(Tag, TagAdmin)
|
|
@ -0,0 +1,108 @@
|
|||
# -*- 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 'Entry'
|
||||
db.create_table(u'blog_entry', (
|
||||
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('title', self.gf('django.db.models.fields.CharField')(max_length=128)),
|
||||
('date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2014, 9, 4, 0, 0))),
|
||||
('content', self.gf('ckeditor.fields.RichTextField')()),
|
||||
('slug', self.gf('django.db.models.fields.SlugField')(max_length=128)),
|
||||
('draft', self.gf('django.db.models.fields.BooleanField')(default=True)),
|
||||
('author', self.gf('django.db.models.fields.related.ForeignKey')(related_name='author', to=orm['auth.User'])),
|
||||
))
|
||||
db.send_create_signal('blog', ['Entry'])
|
||||
|
||||
# Adding M2M table for field tags on 'Entry'
|
||||
m2m_table_name = db.shorten_name(u'blog_entry_tags')
|
||||
db.create_table(m2m_table_name, (
|
||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
('entry', models.ForeignKey(orm['blog.entry'], null=False)),
|
||||
('tag', models.ForeignKey(orm['blog.tag'], null=False))
|
||||
))
|
||||
db.create_unique(m2m_table_name, ['entry_id', 'tag_id'])
|
||||
|
||||
# Adding model 'Tag'
|
||||
db.create_table(u'blog_tag', (
|
||||
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(max_length=128)),
|
||||
('color', self.gf('django.db.models.fields.CharField')(max_length=6, blank=True)),
|
||||
))
|
||||
db.send_create_signal('blog', ['Tag'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'Entry'
|
||||
db.delete_table(u'blog_entry')
|
||||
|
||||
# Removing M2M table for field tags on 'Entry'
|
||||
db.delete_table(db.shorten_name(u'blog_entry_tags'))
|
||||
|
||||
# Deleting model 'Tag'
|
||||
db.delete_table(u'blog_tag')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'blog.entry': {
|
||||
'Meta': {'ordering': "['-date']", 'object_name': 'Entry'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'author'", 'to': u"orm['auth.User']"}),
|
||||
'content': ('ckeditor.fields.RichTextField', [], {}),
|
||||
'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 9, 4, 0, 0)'}),
|
||||
'draft': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '128'}),
|
||||
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['blog.Tag']", 'null': 'True', 'blank': 'True'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
|
||||
},
|
||||
'blog.tag': {
|
||||
'Meta': {'ordering': "['name']", 'object_name': 'Tag'},
|
||||
'color': ('django.db.models.fields.CharField', [], {'max_length': '6', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
|
||||
},
|
||||
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'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['blog']
|
|
@ -0,0 +1,71 @@
|
|||
from django.db import models
|
||||
from django.conf import settings
|
||||
from datetime import datetime
|
||||
from django.utils.timezone import utc
|
||||
from ckeditor.fields import RichTextField
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import activate
|
||||
|
||||
|
||||
#
|
||||
# ENTRY
|
||||
#
|
||||
class Entry(models.Model):
|
||||
title = models.CharField(max_length=128)
|
||||
date = models.DateTimeField(default=datetime.now(tz=utc))
|
||||
content = RichTextField()
|
||||
slug = models.SlugField(max_length=128)
|
||||
draft = models.BooleanField(default=True)
|
||||
author = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
editable=False,
|
||||
related_name='author'
|
||||
)
|
||||
tags = models.ManyToManyField('Tag', null=True, blank=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
|
||||
def status(self):
|
||||
status = 'Published'
|
||||
|
||||
if self.date > datetime.now(tz=utc):
|
||||
status = 'Scheduled'
|
||||
|
||||
if self.draft:
|
||||
status = 'Draft'
|
||||
|
||||
return status
|
||||
|
||||
def get_absolute_url(self):
|
||||
kwargs = {
|
||||
'year': self.date.year,
|
||||
'month': self.date.strftime("%m"),
|
||||
'day': self.date.strftime("%d"),
|
||||
'slug': self.slug
|
||||
}
|
||||
url = reverse('blog:item', kwargs=kwargs)
|
||||
|
||||
return url
|
||||
|
||||
class Meta:
|
||||
app_label = 'blog'
|
||||
ordering = ['-date']
|
||||
verbose_name_plural = 'Entries'
|
||||
|
||||
|
||||
#
|
||||
# TAG
|
||||
#
|
||||
class Tag(models.Model):
|
||||
name = models.CharField(max_length=128)
|
||||
color = models.CharField(max_length=6, blank=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
app_label = 'blog'
|
||||
ordering = ['name']
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
from datetime import datetime
|
||||
|
||||
from django.contrib.sitemaps import Sitemap
|
||||
|
||||
from .models import Entry
|
||||
|
||||
|
||||
class BlogSitemap(Sitemap):
|
||||
changefreq = "monthly"
|
||||
priority = 0.5
|
||||
|
||||
def items(self):
|
||||
return Entry.objects.filter(draft=False, date__lte=datetime.now())
|
||||
|
||||
def lastmod(self, obj):
|
||||
return obj.date
|
|
@ -0,0 +1,10 @@
|
|||
from django_jinja import library
|
||||
|
||||
from fmartingrcom.apps.config.models import SiteConfiguration
|
||||
|
||||
|
||||
@library.filter
|
||||
def readmore(content):
|
||||
config = SiteConfiguration.objects.get()
|
||||
summary, rest = content.split(config.readmore_tag, 1)
|
||||
return summary
|
|
@ -0,0 +1,18 @@
|
|||
from pytz import timezone
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.encoding import smart_unicode
|
||||
from django.conf import settings
|
||||
|
||||
from django_jinja import library
|
||||
|
||||
|
||||
@library.filter
|
||||
def dt(t, fmt=None):
|
||||
"""
|
||||
Call ``datetime.strftime`` with the given format string.
|
||||
"""
|
||||
tz = timezone(settings.TIME_ZONE)
|
||||
if fmt is None:
|
||||
fmt = _('%B %e, %Y')
|
||||
return smart_unicode(tz.normalize(t).strftime(fmt)) if t else u''
|
|
@ -0,0 +1,38 @@
|
|||
from django.conf.urls import patterns, url
|
||||
|
||||
from .views import ListView, EntryView, SearchView, RSSView
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
None,
|
||||
# Post list with page
|
||||
url(
|
||||
r'^page/(?P<page_number>\d+)/$',
|
||||
ListView.as_view(),
|
||||
name='list'
|
||||
),
|
||||
# Post list
|
||||
url(
|
||||
r'^$',
|
||||
ListView.as_view(),
|
||||
name='list'
|
||||
),
|
||||
# Single entry
|
||||
url(
|
||||
r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[\w\-]+)/$',
|
||||
EntryView.as_view(),
|
||||
name='item'
|
||||
),
|
||||
# RSS
|
||||
url(
|
||||
r'^rss\.xml$',
|
||||
RSSView.as_view(),
|
||||
name='rss'
|
||||
),
|
||||
# Search
|
||||
url(
|
||||
r'^search/$',
|
||||
SearchView.as_view(),
|
||||
name='search',
|
||||
)
|
||||
)
|
|
@ -0,0 +1,55 @@
|
|||
from datetime import datetime
|
||||
|
||||
from django.core.paginator import Paginator
|
||||
from django.utils import translation
|
||||
from django.conf import settings
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Q
|
||||
|
||||
from .models import Entry
|
||||
|
||||
|
||||
class Config(object):
|
||||
entries_per_page = 10
|
||||
|
||||
config = Config()
|
||||
|
||||
|
||||
def get_posts(query=None, limit=None):
|
||||
items = Entry.objects.filter(
|
||||
draft=False,
|
||||
date__lt=datetime.now()
|
||||
)
|
||||
if query and len(query) > 0:
|
||||
items = items.filter(
|
||||
Q(title__icontains=query) | \
|
||||
Q(content__icontains=query) | \
|
||||
Q(tags__name__iexact=query)
|
||||
).distinct()
|
||||
|
||||
items = items.order_by('-date')
|
||||
|
||||
if limit:
|
||||
items = items[:limit]
|
||||
|
||||
return items
|
||||
|
||||
|
||||
def get_paginator(request, page_number=1, item=None, **kwargs):
|
||||
item_index = None
|
||||
page = None
|
||||
items = get_posts(query=kwargs.get('query', None))
|
||||
entries_per_page = config.entries_per_page
|
||||
paginator = Paginator(items, entries_per_page)
|
||||
if item:
|
||||
for index, obj in enumerate(items):
|
||||
if obj == item:
|
||||
item_index = index
|
||||
break
|
||||
if item_index:
|
||||
page_number = (item_index / entries_per_page) + 1
|
||||
|
||||
if page_number:
|
||||
page = paginator.page(page_number)
|
||||
|
||||
return paginator, page
|
|
@ -0,0 +1,103 @@
|
|||
from datetime import datetime
|
||||
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
|
||||
from shelfzilla.views import View
|
||||
import utils as blog_utils
|
||||
from .models import Entry
|
||||
|
||||
|
||||
class ListView(View):
|
||||
section = 'blog'
|
||||
template = 'blog/list.html'
|
||||
|
||||
def get(self, request, page_number=1):
|
||||
if 'page' in request.GET:
|
||||
page_number = int(request.GET['page'])
|
||||
|
||||
paginator, page = blog_utils.get_paginator(request, page_number)
|
||||
|
||||
context = {}
|
||||
|
||||
context['page'] = page
|
||||
context['page_number'] = page_number
|
||||
context['paginator'] = paginator
|
||||
|
||||
context = RequestContext(request, context)
|
||||
return render_to_response(self.template, context_instance=context)
|
||||
|
||||
|
||||
class EntryView(View):
|
||||
section = 'blog'
|
||||
template = 'blog/entry.jinja'
|
||||
|
||||
def get(self, request, year, month, day, slug):
|
||||
try:
|
||||
filters = {
|
||||
'slug': slug,
|
||||
'date__year': int(year),
|
||||
'date__month': int(month),
|
||||
'date__day': int(day),
|
||||
}
|
||||
|
||||
item = Entry.objects.get(**filters)
|
||||
except Entry.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
paginator, page = blog_utils.get_paginator(request, item=item)
|
||||
|
||||
context = {}
|
||||
context['page'] = page
|
||||
context['paginator'] = paginator
|
||||
context['item'] = item
|
||||
|
||||
context = RequestContext(request, context)
|
||||
return render_to_response(self.template, context_instance=context)
|
||||
|
||||
|
||||
class SearchView(ListView):
|
||||
template = 'blog/search.jinja'
|
||||
|
||||
def post(self, request):
|
||||
page_number = 1
|
||||
if 'page' in request.GET:
|
||||
page_number = int(request.GET['page'])
|
||||
|
||||
search_query = request.POST['query']
|
||||
|
||||
if not search_query:
|
||||
return HttpResponseRedirect(reverse('blog:list'))
|
||||
|
||||
paginator, page = blog_utils.get_paginator(
|
||||
request, page_number, query=search_query
|
||||
)
|
||||
|
||||
context = {}
|
||||
context['page'] = page
|
||||
context['page_number'] = page_number
|
||||
context['paginator'] = paginator
|
||||
context['search_query'] = search_query
|
||||
|
||||
context = RequestContext(request, context)
|
||||
return render_to_response(self.template, context_instance=context)
|
||||
|
||||
|
||||
class RSSView(View):
|
||||
template = 'blog/rss.jinja'
|
||||
|
||||
def get(self, request):
|
||||
limit = 20
|
||||
items = blog_utils.get_posts(limit=limit)
|
||||
context = {}
|
||||
context['items'] = items
|
||||
|
||||
context = RequestContext(request, context)
|
||||
return render_to_response(
|
||||
'blog/rss.jinja',
|
||||
context_instance=context,
|
||||
mimetype='text/xml'
|
||||
)
|
|
@ -58,6 +58,7 @@ INSTALLED_APPS = (
|
|||
|
||||
# Staticfiles
|
||||
"compressor",
|
||||
'ckeditor',
|
||||
|
||||
# Apps
|
||||
'shelfzilla.apps._admin',
|
||||
|
@ -66,6 +67,7 @@ INSTALLED_APPS = (
|
|||
'shelfzilla.apps.homepage',
|
||||
'shelfzilla.apps.landing',
|
||||
'shelfzilla.apps.manga',
|
||||
'shelfzilla.apps.blog',
|
||||
'shelfzilla.apps.pjax',
|
||||
)
|
||||
|
||||
|
@ -261,6 +263,11 @@ SUIT_CONFIG = {
|
|||
'label': 'Settings',
|
||||
'icon': 'icon-cog',
|
||||
},
|
||||
{
|
||||
'app': 'blog',
|
||||
'label': 'Blog',
|
||||
'icon': 'icon-book',
|
||||
},
|
||||
{
|
||||
'app': 'manga',
|
||||
'label': 'Manga',
|
||||
|
@ -273,3 +280,15 @@ SUIT_CONFIG = {
|
|||
},
|
||||
),
|
||||
}
|
||||
|
||||
#
|
||||
# CKEDITOR
|
||||
#
|
||||
CKEDITOR_UPLOAD_PATH = 'ckeditor/'
|
||||
|
||||
CKEDITOR_CONFIGS = {
|
||||
'default': {
|
||||
'toolbar': 'Standard',
|
||||
'width': '100%',
|
||||
},
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{% extends "_layout.html" %}
|
||||
|
||||
{% block page_title %}{{ block.super }} | Blog{% endblock %}
|
|
@ -0,0 +1,24 @@
|
|||
{% extends "blog/_layout.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block main_content %}
|
||||
<div class="container">
|
||||
<ul class="media-list">
|
||||
{% for item in page.object_list %}
|
||||
<li class="media well {% if item.draft %}draft{% endif %}">
|
||||
<a class="pull-left" href="#">
|
||||
<!--<img class="media-object img-rounded" src="{{ item.author.avatar }}">-->
|
||||
</a>
|
||||
<div class="media-body">
|
||||
<h2 class="media-heading">{{ item.title }}</h2>
|
||||
<div>
|
||||
Por <strong>{{ item.author.first_name }}</strong> el
|
||||
<time datetime="{{ item.date }}" pubdate="" data-updated="true">{{ item.date }}</time>
|
||||
</div>
|
||||
<p>{{ item.content|safe }}</p>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -9,10 +9,12 @@ admin.autodiscover()
|
|||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^ckeditor/', include('ckeditor.urls')),
|
||||
url(r'^messages/$', MessagesView.as_view(), name="contrib.messages"),
|
||||
url(r'^$', include('shelfzilla.apps.homepage.urls')),
|
||||
url(r'^', include('shelfzilla.apps.landing.urls')),
|
||||
url(r'^', include('shelfzilla.apps.users.urls')),
|
||||
url(r'^blog/', include('shelfzilla.apps.blog.urls', namespace='blog')),
|
||||
url(r'^series/', include('shelfzilla.apps.manga.urls.series')),
|
||||
url(r'^volumes/', include('shelfzilla.apps.manga.urls.volumes')),
|
||||
url(r'^publishers/', include('shelfzilla.apps.manga.urls.publishers')),
|
||||
|
|
Reference in New Issue