diff options
Diffstat (limited to 'weblog')
34 files changed, 1610 insertions, 0 deletions
diff --git a/weblog/__init__.py b/weblog/__init__.py new file mode 100644 index 0000000..a97f18a --- /dev/null +++ b/weblog/__init__.py @@ -0,0 +1,79 @@ +from . import apps +from django.conf import settings + +try: + apps.SETTINGS['enable_comments'] = settings.WEBLOG_ENABLE_COMMENTS +except AttributeError: + pass +except NameError: + pass + +try: + apps.SETTINGS['allow_anon_comments'] = settings.WEBLOG_ALLOW_ANON_COMMENTS +except AttributeError: + pass +except NameError: + pass + +try: + apps.SETTINGS['multilingual'] = settings.WEBLOG_MULTILINGUAL +except AttributeError: + pass +except NameError: + pass + +try: + apps.SETTINGS['blog_title'] = settings.WEBLOG_TITLE +except AttributeError: + pass +except NameError: + pass + +try: + apps.SETTINGS['base_template'] = settings.WEBLOG_BASE_TEMPLATE +except AttributeError: + pass +except NameError: + pass + +try: + apps.SETTINGS['show_author'] = settings.WEBLOG_SHOW_AUTHOR +except AttributeError: + pass +except NameError: + pass + +try: + apps.SETTINGS['use_authors_username'] = settings.WEBLOG_USE_AUTHORS_USERNAME +except AttributeError: + pass +except NameError: + pass + +try: + apps.SETTINGS['show_sidebar'] = settings.WEBLOG_SHOW_SIDEBAR +except AttributeError: + pass +except NameError: + pass + +try: + apps.SETTINGS['show_categories'] = settings.WEBLOG_SHOW_CATEGORIES +except AttributeError: + pass +except NameError: + pass + +try: + apps.SETTINGS['show_archive'] = settings.WEBLOG_SHOW_ARCHIVE +except AttributeError: + pass +except NameError: + pass + +try: + apps.SETTINGS['posts_per_page'] = settings.WEBLOG_POSTS_PER_PAGE +except AttributeError: + pass +except NameError: + pass
\ No newline at end of file diff --git a/weblog/admin.py b/weblog/admin.py new file mode 100644 index 0000000..1b4e128 --- /dev/null +++ b/weblog/admin.py @@ -0,0 +1,46 @@ +from django.contrib import admin +from django_summernote.admin import SummernoteModelAdmin, SummernoteInlineModelAdmin +from .apps import SETTINGS as blog_settings +from .models import BlogPost, Translation, PostComment, Category, CategoryTranslation + + +blogPostInlines = [] +categoryInlines = [] + +class TranslationInline(admin.StackedInline, SummernoteInlineModelAdmin): + model = Translation + extra = 1 + +class CategoryTranslationInline(admin.StackedInline): + model = CategoryTranslation + extra = 1 + +class PostCommentInline(admin.StackedInline): + model = PostComment + extra = 0 + +if blog_settings['multilingual']: + blogPostInlines.append(TranslationInline) + categoryInlines.append(CategoryTranslationInline) + +if blog_settings['enable_comments']: + blogPostInlines.append(PostCommentInline) + +class BlogPostAdmin(SummernoteModelAdmin): + list_display = ['title', 'author', 'publish_date'] + list_filter = ['publish_date', 'categories'] + inlines = blogPostInlines + summer_note_fields = '__all__' + + def get_form(self, request, obj=None, **kwargs): + if not blog_settings['multilingual']: + self.exclude = ('original_language', ) + form = super(BlogPostAdmin, self).get_form(request, obj, **kwargs) + return form + +class CategoryAdmin(admin.ModelAdmin): + list_display = ['name'] + inlines = categoryInlines + +admin.site.register(BlogPost, BlogPostAdmin) +admin.site.register(Category, CategoryAdmin) diff --git a/weblog/apps.py b/weblog/apps.py new file mode 100644 index 0000000..3389f92 --- /dev/null +++ b/weblog/apps.py @@ -0,0 +1,18 @@ +from django.apps import AppConfig + +SETTINGS = { + 'enable_comments': False, + 'allow_anon_comments': True, + 'multilingual': True, + 'blog_title': 'Django-Weblog', + 'base_template': 'base.html', + 'show_author': True, + 'use_authors_username': True, + 'show_sidebar': True, + 'show_categories': False, + 'show_archive': True, + 'posts_per_page': 10, +} + +class WeblogConfig(AppConfig): + name = 'weblog' diff --git a/weblog/locale/es/LC_MESSAGES/django.mo b/weblog/locale/es/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 0000000..9558ef1 --- /dev/null +++ b/weblog/locale/es/LC_MESSAGES/django.mo diff --git a/weblog/locale/es/LC_MESSAGES/django.po b/weblog/locale/es/LC_MESSAGES/django.po new file mode 100644 index 0000000..5afb637 --- /dev/null +++ b/weblog/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,274 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-01-31 08:00+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: weblog/models.py:8 weblog/models.py:23 +msgctxt "Noun, not personal name" +msgid "Name" +msgstr "Nombre" + +#: weblog/models.py:9 weblog/models.py:45 +msgid "Slug (URL)" +msgstr "" + +#: weblog/models.py:10 +msgid "Parent category" +msgstr "Categoría raíz" + +#: weblog/models.py:19 weblog/models.py:25 +msgctxt "Post category" +msgid "Category" +msgstr "Categoría" + +#: weblog/models.py:20 weblog/models.py:46 weblog/templates/weblog/post.html:16 +#: weblog/templates/weblog/sidebar_categories.html:3 +msgctxt "Post categories" +msgid "Categories" +msgstr "Categorías" + +#: weblog/models.py:24 weblog/models.py:67 +msgid "Language (ISO)" +msgstr "Idioma (ISO)" + +#: weblog/models.py:34 +msgid "Category name translation" +msgstr "Traducción de la categoría" + +#: weblog/models.py:35 +msgid "Category name translations" +msgstr "Traducciones de categorías" + +#: weblog/models.py:39 weblog/models.py:78 +msgid "Author" +msgstr "Autor" + +#: weblog/models.py:40 weblog/models.py:68 +msgctxt "As in name" +msgid "Title" +msgstr "Título" + +#: weblog/models.py:41 weblog/models.py:69 weblog/models.py:80 +msgctxt "Of post, comment, article, etc." +msgid "Content" +msgstr "Contenido" + +#: weblog/models.py:42 weblog/models.py:70 +msgid "Preview image" +msgstr "Imágen de vista previa" + +#: weblog/models.py:43 weblog/models.py:71 +msgid "Preview Text" +msgstr "Texto de vista previa" + +#: weblog/models.py:44 +msgid "Original language (ISO)" +msgstr "Idioma original" + +#: weblog/models.py:47 +msgctxt "Make post viewable" +msgid "Published" +msgstr "Publicado" + +#: weblog/models.py:48 +msgid "Publish date" +msgstr "Fecha de publicación" + +#: weblog/models.py:62 +msgid "Blog Post" +msgstr "Entrada de blog" + +#: weblog/models.py:63 +msgid "Blog Posts" +msgstr "Entradas de blog" + +#: weblog/models.py:66 weblog/models.py:79 +#, fuzzy +#| msgid "Blog Post" +msgctxt "Noun, as in blog post" +msgid "Post" +msgstr "Entrada" + +#: weblog/models.py:74 +msgid "Translation" +msgstr "Traducción" + +#: weblog/models.py:75 +msgid "Translations" +msgstr "Traducciones" + +#: weblog/models.py:83 +msgctxt "Noun" +msgid "Comment" +msgstr "Comentario" + +#: weblog/models.py:84 +msgctxt "Noun" +msgid "Comments" +msgstr "Comentarios" + +#: weblog/templates/weblog/index.html:6 weblog/templatetags/weblog_extras.py:43 +#: weblog/views.py:75 weblog/views.py:150 +msgctxt "Posts without category" +msgid "Uncategorized" +msgstr "Sin categoría" + +#: weblog/templates/weblog/index.html:17 +msgctxt "Uncategorized page title" +msgid "Uncategorized posts" +msgstr "Entradas sin categoría" + +#: weblog/templates/weblog/index.html:19 +#, python-format +msgctxt "Posts in category" +msgid "Posts in %(category_name)s" +msgstr "Entradas en %(category_name)s" + +#: weblog/templates/weblog/index.html:28 weblog/templates/weblog/post.html:7 +#, python-format +msgid "Published on %(publish_date)s" +msgstr "Publicado el %(publish_date)s" + +#: weblog/templates/weblog/index.html:28 weblog/templates/weblog/post.html:7 +#, python-format +msgctxt "Written by (Author)" +msgid ", by %(author)s" +msgstr ", por %(author)s" + +#: weblog/templates/weblog/index.html:34 +msgid "Read more..." +msgstr "Leer más..." + +#: weblog/templates/weblog/index.html:41 +msgctxt "Page" +msgid "First" +msgstr "Primera" + +#: weblog/templates/weblog/index.html:42 +msgctxt "Page" +msgid "Previous" +msgstr "Anterior" + +#: weblog/templates/weblog/index.html:70 +msgctxt "Page" +msgid "Next" +msgstr "Siguiente" + +#: weblog/templates/weblog/index.html:71 +msgctxt "Page" +msgid "Last" +msgstr "Última" + +#: weblog/templates/weblog/index.html:76 +msgid "Nothing has been posted yet." +msgstr "No hay ninguna publicación." + +#: weblog/templates/weblog/post.html:24 +msgid "Leave a comment" +msgstr "Dejar un comentario" + +#: weblog/templates/weblog/post.html:31 +msgid "Submit comment" +msgstr "Enviar comentario" + +#: weblog/templates/weblog/post.html:35 +msgid "To leave a comment you need to sign in" +msgstr "Para poder comentar, necesitar iniciar sesión." + +#: weblog/templates/weblog/post.html:47 +msgid "Comment submited successfully" +msgstr "Comentario enviado exitosamente" + +#: weblog/templates/weblog/post.html:54 +msgctxt "Unauthenticated comment poster" +msgid "Anonymous" +msgstr "Anónimo" + +#: weblog/templates/weblog/post.html:62 +msgid "Nobody has left a comment on this post yet" +msgstr "No hay ningún comentario relacionado a esta entrada" + +#: weblog/templates/weblog/sidebar_archive.html:2 +msgctxt "Blog archive" +msgid "Archive" +msgstr "Archivo" + +#: weblog/templatetags/weblog_extras.py:12 +msgid "January" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:13 +msgid "February" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:14 +msgid "March" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:15 +msgid "April" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:16 +msgid "May" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:17 +msgid "June" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:18 +msgid "July" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:19 +msgid "August" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:20 +msgid "September" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:21 +msgid "October" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:22 +msgid "November" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:23 +msgid "December" +msgstr "" + +#: weblog/views.py:171 +msgid "You need to sign in to submit a comment" +msgstr "Para poder comentar necesita iniciar sesión" + +#: weblog/views.py:173 +msgid "Error submitting comment: Invalid data" +msgstr "Error al intentar enviar el comentario: Información invalida" + +#~ msgid "English" +#~ msgstr "Inglés" + +#~ msgid "Spanish" +#~ msgstr "Español" + +#~ msgid "Russian" +#~ msgstr "Ruso" diff --git a/weblog/locale/ru/LC_MESSAGES/django.mo b/weblog/locale/ru/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 0000000..a419335 --- /dev/null +++ b/weblog/locale/ru/LC_MESSAGES/django.mo diff --git a/weblog/locale/ru/LC_MESSAGES/django.po b/weblog/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000..e586fbd --- /dev/null +++ b/weblog/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,274 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-01-31 08:00+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" +"%100>=11 && n%100<=14)? 2 : 3);\n" + +#: weblog/models.py:8 weblog/models.py:23 +msgctxt "Noun, not personal name" +msgid "Name" +msgstr "Название" + +#: weblog/models.py:9 weblog/models.py:45 +msgid "Slug (URL)" +msgstr "" + +#: weblog/models.py:10 +msgid "Parent category" +msgstr "Корневая категория" + +#: weblog/models.py:19 weblog/models.py:25 +msgctxt "Post category" +msgid "Category" +msgstr "Категория" + +#: weblog/models.py:20 weblog/models.py:46 weblog/templates/weblog/post.html:16 +#: weblog/templates/weblog/sidebar_categories.html:3 +msgctxt "Post categories" +msgid "Categories" +msgstr "Категории" + +#: weblog/models.py:24 weblog/models.py:67 +msgid "Language (ISO)" +msgstr "Язык (В стандарте ISO)" + +#: weblog/models.py:34 +msgid "Category name translation" +msgstr "Перевод названия категории" + +#: weblog/models.py:35 +msgid "Category name translations" +msgstr "Переводы названия категории" + +#: weblog/models.py:39 weblog/models.py:78 +msgid "Author" +msgstr "Автор" + +#: weblog/models.py:40 weblog/models.py:68 +msgctxt "As in name" +msgid "Title" +msgstr "Название" + +#: weblog/models.py:41 weblog/models.py:69 weblog/models.py:80 +msgctxt "Of post, comment, article, etc." +msgid "Content" +msgstr "Содержание" + +#: weblog/models.py:42 weblog/models.py:70 +msgid "Preview image" +msgstr "Картинка предпросмотра" + +#: weblog/models.py:43 weblog/models.py:71 +msgid "Preview Text" +msgstr "Текст предпросмотра" + +#: weblog/models.py:44 +msgid "Original language (ISO)" +msgstr "Язык оригинала (в стандарте ISO)" + +#: weblog/models.py:47 +msgctxt "Make post viewable" +msgid "Published" +msgstr "Опубликовать" + +#: weblog/models.py:48 +msgid "Publish date" +msgstr "Дата публикации" + +#: weblog/models.py:62 +msgid "Blog Post" +msgstr "Запись блога" + +#: weblog/models.py:63 +msgid "Blog Posts" +msgstr "Записи блога" + +#: weblog/models.py:66 weblog/models.py:79 +msgctxt "Noun, as in blog post" +msgid "Post" +msgstr "Запись" + +#: weblog/models.py:74 +msgid "Translation" +msgstr "Перевод" + +#: weblog/models.py:75 +msgid "Translations" +msgstr "Переводы" + +#: weblog/models.py:83 +msgctxt "Noun" +msgid "Comment" +msgstr "Комментарий" + +#: weblog/models.py:84 +msgctxt "Noun" +msgid "Comments" +msgstr "Комментарии" + +#: weblog/templates/weblog/index.html:6 weblog/templatetags/weblog_extras.py:43 +#: weblog/views.py:75 weblog/views.py:150 +msgctxt "Posts without category" +msgid "Uncategorized" +msgstr "Без категории" + +#: weblog/templates/weblog/index.html:17 +msgctxt "Uncategorized page title" +msgid "Uncategorized posts" +msgstr "Записи без категории" + +#: weblog/templates/weblog/index.html:19 +#, python-format +msgctxt "Posts in category" +msgid "Posts in %(category_name)s" +msgstr "Записи в %(category_name)s" + +#: weblog/templates/weblog/index.html:28 weblog/templates/weblog/post.html:7 +#, python-format +msgid "Published on %(publish_date)s" +msgstr "Опубликовано %(publish_date)s" + +#: weblog/templates/weblog/index.html:28 weblog/templates/weblog/post.html:7 +#, python-format +msgctxt "Written by (Author)" +msgid ", by %(author)s" +msgstr ". %(author)s" + +#: weblog/templates/weblog/index.html:34 +msgid "Read more..." +msgstr "Читать далее..." + +#: weblog/templates/weblog/index.html:41 +msgctxt "Page" +msgid "First" +msgstr "Первая" + +#: weblog/templates/weblog/index.html:42 +msgctxt "Page" +msgid "Previous" +msgstr "Предыдущая" + +#: weblog/templates/weblog/index.html:70 +msgctxt "Page" +msgid "Next" +msgstr "Следующая" + +#: weblog/templates/weblog/index.html:71 +msgctxt "Page" +msgid "Last" +msgstr "Последняя" + +#: weblog/templates/weblog/index.html:76 +msgid "Nothing has been posted yet." +msgstr "Нет записи на данный момент." + +#: weblog/templates/weblog/post.html:24 +msgid "Leave a comment" +msgstr "Оставьте комментарий" + +#: weblog/templates/weblog/post.html:31 +msgid "Submit comment" +msgstr "Отправить комметарий" + +#: weblog/templates/weblog/post.html:35 +msgid "To leave a comment you need to sign in" +msgstr "Для того чтобы оставить комментарий, зайдите в свою учетную запись" + +#: weblog/templates/weblog/post.html:47 +msgid "Comment submited successfully" +msgstr "Ваш комментарий был успешно отправлен" + +#: weblog/templates/weblog/post.html:54 +msgctxt "Unauthenticated comment poster" +msgid "Anonymous" +msgstr "Анонимный" + +#: weblog/templates/weblog/post.html:62 +msgid "Nobody has left a comment on this post yet" +msgstr "У этой записи нет комментарии." + +#: weblog/templates/weblog/sidebar_archive.html:2 +msgctxt "Blog archive" +msgid "Archive" +msgstr "Архив" + +#: weblog/templatetags/weblog_extras.py:12 +msgid "January" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:13 +msgid "February" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:14 +msgid "March" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:15 +msgid "April" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:16 +msgid "May" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:17 +msgid "June" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:18 +msgid "July" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:19 +msgid "August" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:20 +msgid "September" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:21 +msgid "October" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:22 +msgid "November" +msgstr "" + +#: weblog/templatetags/weblog_extras.py:23 +msgid "December" +msgstr "" + +#: weblog/views.py:171 +msgid "You need to sign in to submit a comment" +msgstr "Для того чтобы оставить комментарий, зайдите в свою учетную запись" + +#: weblog/views.py:173 +msgid "Error submitting comment: Invalid data" +msgstr "Ошибка: неправильный формат данных" + +#~ msgid "English" +#~ msgstr "Английский" + +#~ msgid "Spanish" +#~ msgstr "Испанский" + +#~ msgid "Russian" +#~ msgstr "Русский" diff --git a/weblog/migrations/0001_initial.py b/weblog/migrations/0001_initial.py new file mode 100644 index 0000000..2ab0238 --- /dev/null +++ b/weblog/migrations/0001_initial.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.8 on 2018-01-09 08:02 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='BlogPost', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=100, verbose_name='Title')), + ('content', models.TextField(verbose_name='Content')), + ('preview_image', models.ImageField(blank=True, upload_to='weblog/preview_images/%Y/%m/%d/', verbose_name='Preview image')), + ('preview_text', models.CharField(blank=True, max_length=250, verbose_name='Preview Text')), + ('original_language', models.CharField(max_length=5, verbose_name='Original language (ISO)')), + ('publish_date', models.DateTimeField(verbose_name='Publish date')), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Author')), + ], + options={ + 'verbose_name': 'Blog Post', + 'verbose_name_plural': 'Blog Posts', + }, + ), + migrations.CreateModel( + name='PostComment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.TextField(verbose_name='Content')), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Author')), + ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='weblog.BlogPost', verbose_name='Post')), + ], + options={ + 'verbose_name': 'Comment', + 'verbose_name_plural': 'Comments', + }, + ), + migrations.CreateModel( + name='Translation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('language', models.CharField(max_length=5, verbose_name='Language (ISO)')), + ('title', models.CharField(max_length=100, verbose_name='Title')), + ('content', models.TextField(verbose_name='Content')), + ('preview_image', models.ImageField(blank=True, upload_to='weblog/preview_images/%Y/%m/%d/', verbose_name='Preview image')), + ('preview_text', models.CharField(blank=True, max_length=250, verbose_name='Preview Text')), + ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='weblog.BlogPost', verbose_name='Post')), + ], + options={ + 'verbose_name': 'Translation', + 'verbose_name_plural': 'Translations', + }, + ), + ] diff --git a/weblog/migrations/0002_auto_20180113_1606.py b/weblog/migrations/0002_auto_20180113_1606.py new file mode 100644 index 0000000..8eb3789 --- /dev/null +++ b/weblog/migrations/0002_auto_20180113_1606.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.8 on 2018-01-13 13:06 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('weblog', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Category', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=250, verbose_name='Name')), + ('parent_category', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='weblog.Category', verbose_name='Parent category')), + ], + options={ + 'verbose_name': 'Category', + 'verbose_name_plural': 'Categories', + }, + ), + migrations.CreateModel( + name='CategoryTranslation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=250, verbose_name='Name')), + ('language', models.CharField(max_length=5, verbose_name='Language (ISO)')), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='weblog.Category', verbose_name='Category')), + ], + options={ + 'verbose_name': 'Category name translation', + 'verbose_name_plural': 'Category name translations', + }, + ), + migrations.AddField( + model_name='blogpost', + name='published', + field=models.BooleanField(default=False, verbose_name='Published'), + preserve_default=False, + ), + migrations.AlterField( + model_name='blogpost', + name='original_language', + field=models.CharField(blank=True, max_length=5, verbose_name='Original language (ISO)'), + ), + migrations.AddField( + model_name='blogpost', + name='category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='weblog.Category', verbose_name='Category'), + ), + ] diff --git a/weblog/migrations/0003_auto_20180119_0156.py b/weblog/migrations/0003_auto_20180119_0156.py new file mode 100644 index 0000000..73ca4e1 --- /dev/null +++ b/weblog/migrations/0003_auto_20180119_0156.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.8 on 2018-01-18 22:56 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('weblog', '0002_auto_20180113_1606'), + ] + + operations = [ + migrations.AlterModelOptions( + name='blogpost', + options={'ordering': ['-publish_date', 'title'], 'verbose_name': 'Blog Post', 'verbose_name_plural': 'Blog Posts'}, + ), + migrations.RemoveField( + model_name='blogpost', + name='category', + ), + migrations.AddField( + model_name='blogpost', + name='categories', + field=models.ManyToManyField(blank=True, to='weblog.Category', verbose_name='Categories'), + ), + ] diff --git a/weblog/migrations/0004_auto_20180119_0156.py b/weblog/migrations/0004_auto_20180119_0156.py new file mode 100644 index 0000000..937b7a0 --- /dev/null +++ b/weblog/migrations/0004_auto_20180119_0156.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.8 on 2018-01-18 22:56 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('weblog', '0003_auto_20180119_0156'), + ] + + operations = [ + migrations.AlterField( + model_name='category', + name='parent_category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='weblog.Category', verbose_name='Parent category'), + ), + ] diff --git a/weblog/migrations/0005_auto_20180119_0231.py b/weblog/migrations/0005_auto_20180119_0231.py new file mode 100644 index 0000000..42b1bd0 --- /dev/null +++ b/weblog/migrations/0005_auto_20180119_0231.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.8 on 2018-01-18 23:31 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('weblog', '0004_auto_20180119_0156'), + ] + + operations = [ + migrations.AlterField( + model_name='category', + name='name', + field=models.CharField(max_length=250, unique=True, verbose_name='Name'), + ), + ] diff --git a/weblog/migrations/0006_auto_20180121_1002.py b/weblog/migrations/0006_auto_20180121_1002.py new file mode 100644 index 0000000..db56a3f --- /dev/null +++ b/weblog/migrations/0006_auto_20180121_1002.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.8 on 2018-01-21 07:02 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('weblog', '0005_auto_20180119_0231'), + ] + + operations = [ + migrations.AddField( + model_name='blogpost', + name='slug', + field=models.SlugField(default='Test', max_length=100, unique=True, verbose_name='Slug (URL)'), + preserve_default=False, + ), + migrations.AddField( + model_name='category', + name='slug', + field=models.SlugField(default='Test', max_length=60, unique=True, verbose_name='Slug (URL)'), + preserve_default=False, + ), + ] diff --git a/weblog/migrations/0007_auto_20180122_1943.py b/weblog/migrations/0007_auto_20180122_1943.py new file mode 100644 index 0000000..cae0a4f --- /dev/null +++ b/weblog/migrations/0007_auto_20180122_1943.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.8 on 2018-01-22 16:43 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('weblog', '0006_auto_20180121_1002'), + ] + + operations = [ + migrations.AlterField( + model_name='postcomment', + name='author', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Author'), + ), + ] diff --git a/weblog/migrations/__init__.py b/weblog/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/weblog/migrations/__init__.py diff --git a/weblog/models.py b/weblog/models.py new file mode 100644 index 0000000..80e917e --- /dev/null +++ b/weblog/models.py @@ -0,0 +1,93 @@ +from django.db import models +from django.shortcuts import reverse +from django.contrib.auth.models import User +from django.forms import ModelForm, Textarea +from django.utils.translation import ugettext_lazy as _, pgettext_lazy + +class Category(models.Model): + name = models.CharField(max_length=250, verbose_name=pgettext_lazy('Noun, not personal name', 'Name'), blank=False, unique=True) + slug = models.SlugField(max_length=60, verbose_name=_('Slug (URL)'), db_index=True, unique=True) + parent_category = models.ForeignKey('self', verbose_name=_('Parent category'), null=True, blank=True) + + def get_absolute_url(self): + return reverse('weblog:CategoryIndex', kwargs={'category_slug': self.slug}) + + def __str__(self): + return self.name + + class Meta: + verbose_name = pgettext_lazy('Post category', 'Category') + verbose_name_plural = pgettext_lazy('Post categories', 'Categories') + +class CategoryTranslation(models.Model): + name = models.CharField(max_length=250, verbose_name=pgettext_lazy('Noun, not personal name', 'Name'), blank=False) + language = models.CharField(max_length=5, verbose_name=_('Language (ISO)'), blank=False) + category = models.ForeignKey(Category, verbose_name = pgettext_lazy('Post category', 'Category'), blank=False) + + def __str__(self): + return self.name + + def slug(self): + return self.category.slug + + class Meta: + verbose_name = _('Category name translation') + verbose_name_plural = _('Category name translations') + + +class BlogPost(models.Model): + author = models.ForeignKey(User, verbose_name=_('Author')) + title = models.CharField(max_length=100, verbose_name=pgettext_lazy('As in name', 'Title'), blank=False) + content = models.TextField(verbose_name=pgettext_lazy('Of post, comment, article, etc.', 'Content'), blank=False) + preview_image = models.ImageField(upload_to='weblog/preview_images/%Y/%m/%d/', blank=True, verbose_name=_('Preview image')) + preview_text = models.CharField(max_length=250, blank=True, verbose_name=_('Preview Text')) + original_language = models.CharField(max_length=5, verbose_name=_('Original language (ISO)'), blank=True) + slug = models.SlugField(max_length=100, verbose_name=_('Slug (URL)'), db_index=True, unique=True) + categories = models.ManyToManyField(Category, verbose_name=pgettext_lazy('Post categories', 'Categories'), blank=True) + published = models.BooleanField(verbose_name=pgettext_lazy('Make post viewable', 'Published')) + publish_date = models.DateTimeField(verbose_name=_('Publish date')) + + def get_absolute_url(self): + if self.categories.all().count() > 0: + category = self.categories.all()[0].slug + return reverse('weblog:PostView', kwargs={'category_slug': category, 'post_slug': self.slug}) + else: + return reverse('weblog:PostView', kwargs={'category_slug': 'misc', 'post_slug': self.slug}) + + def __str__(self): + return self.title + + class Meta: + ordering = ['-publish_date', 'title'] + verbose_name = _('Blog Post') + verbose_name_plural = _('Blog Posts') + +class Translation(models.Model): + post = models.ForeignKey(BlogPost, verbose_name=pgettext_lazy('Noun, as in blog post', 'Post')) + language = models.CharField(max_length=5, verbose_name=_('Language (ISO)'), blank=False) + title = models.CharField(max_length=100, verbose_name=pgettext_lazy('As in name', 'Title'), blank=False) + content = models.TextField(verbose_name=pgettext_lazy('Of post, comment, article, etc.', 'Content'), blank=False) + preview_image = models.ImageField(upload_to='weblog/preview_images/%Y/%m/%d/', blank=True, verbose_name=_('Preview image')) + preview_text = models.CharField(max_length=250, blank=True, verbose_name=_('Preview Text')) + + class Meta: + verbose_name = _('Translation') + verbose_name_plural = _('Translations') + +class PostComment(models.Model): + author = models.ForeignKey(User, verbose_name=_('Author'), null=True, blank=True) + post = models.ForeignKey(BlogPost, verbose_name=pgettext_lazy('Noun, as in blog post', 'Post')) + content = models.TextField(verbose_name=pgettext_lazy('Of post, comment, article, etc.', 'Content'), blank=False) + + class Meta: + verbose_name = pgettext_lazy('Noun', 'Comment') + verbose_name_plural = pgettext_lazy('Noun', 'Comments') + +class PostCommentForm(ModelForm): + class Meta: + model = PostComment + fields = ('content',) + labels = {'content': ''} + widgets = { + 'content': Textarea(attrs={'class': 'form-control', 'rows': '5'}), + }
\ No newline at end of file diff --git a/weblog/static/weblog/css/weblog.css b/weblog/static/weblog/css/weblog.css new file mode 100644 index 0000000..cb1facc --- /dev/null +++ b/weblog/static/weblog/css/weblog.css @@ -0,0 +1,7 @@ +.archive-list{ + list-style-type: none; +} + +.archive-child-list{ + display: none; +}
\ No newline at end of file diff --git a/weblog/static/weblog/js/weblog.js b/weblog/static/weblog/js/weblog.js new file mode 100644 index 0000000..b802841 --- /dev/null +++ b/weblog/static/weblog/js/weblog.js @@ -0,0 +1,13 @@ +function toggleNode(caller){ + state = $(caller).attr('node-state'); + target = $(caller).attr('node-target'); + if(state=='closed'){ + $(caller).html('—'); + $(caller).attr('node-state', 'open'); + } + else{ + $(caller).html('+'); + $(caller).attr('node-state', 'closed'); + } + $('#'+target).toggle(); +}
\ No newline at end of file diff --git a/weblog/templates/weblog/index.html b/weblog/templates/weblog/index.html new file mode 100644 index 0000000..c46da38 --- /dev/null +++ b/weblog/templates/weblog/index.html @@ -0,0 +1,78 @@ +{% extends 'weblog/weblog.html' %}
+{% load i18n %}
+{% block title_block %}
+{% if category %}
+{% if category == 'misc' %}
+{% trans 'Uncategorized' context 'Posts without category' %}
+{% else %}
+{{ category.name }}
+{% endif %}
+{% else %}
+{{ blog_title }}
+{% endif %}
+{% endblock %}
+{% block blog_content_block %}
+{% if category %}
+{% if category == 'misc' %}
+<h1>{% trans 'Uncategorized posts' context 'Uncategorized page title' %}</h1>
+{% else %}
+<h1>{% blocktrans with category_name=category.name context 'Posts in category' %}Posts in {{ category_name }}{% endblocktrans %}</h1>
+{% endif %}
+{% else %}
+<h1>{{ blog_title }}</h1>
+{% endif %}
+ {% if posts %}
+ {% for post in posts %}
+ <div class="container-fluid blogpost">
+ <h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
+ <p class="publish-info">{% blocktrans with publish_date=post.publish_date %}Published on {{ publish_date }}{% endblocktrans %}{% if post.author %}{% blocktrans with author=post.author context 'Written by (Author)' %}, by {{ author }}{% endblocktrans %}{% endif %}</p>
+ <hr>
+ {% if post.preview_image %}<img class="img-responsive preview-img" src="{{ post.preview_image.url }}">{% endif %}
+ {{ post.preview_text|safe }}
+ <hr>
+ <div class="text-right">
+ <a href="{{ post.url }}">{% trans 'Read more...' %}</a>
+ </div>
+ </div>
+ {% endfor %}
+ {% if last_page > 1 %}
+ <ul class="pagination">
+ {% if current_page != 1 %}
+ <li title="{% trans 'First' context 'Page' %}"><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}{% endif %}{% else %}{% url 'weblog:Index' %}{% endif %}">«</a></li>
+ <li title="{% trans 'Previous' context 'Page' %}"><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}?page={{ current_page|add:'-1' }}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}?page={{ current_page|add:'-1' }}{% endif %}{% else %}{% url 'weblog:Index' %}?page={{ current_page|add:'-1' }}{% endif %}">←</a></li>
+ {% if current_page == last_page and current_page|add:'-4' >= 1 %}
+ <li><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}?page={{ current_page|add:'-4' }}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}?page={{ current_page|add:'-4' }}{% endif %}{% else %}{% url 'weblog:Index' %}?page={{ current_page|add:'-4' }}{% endif %}">{{ current_page|add:'-4' }}</a></li>
+ {% endif %}
+ {% if current_page|add:'1' >= last_page and current_page|add:'-3' >= 1 %}
+ <li><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}?page={{ current_page|add:'-3' }}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}?page={{ current_page|add:'-3' }}{% endif %}{% else %}{% url 'weblog:Index' %}?page={{ current_page|add:'-3' }}{% endif %}">{{ current_page|add:'-3' }}</a></li>
+ {% endif %}
+ {% if current_page|add:'-2' >= 1 %}
+ <li><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}?page={{ current_page|add:'-2' }}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}?page={{ current_page|add:'-2' }}{% endif %}{% else %}{% url 'weblog:Index' %}?page={{ current_page|add:'-2' }}{% endif %}">{{ current_page|add:'-2' }}</a></li>
+ {% endif %}
+ {% if current_page|add:'-1' >= 1 %}
+ <li><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}?page={{ current_page|add:'-1' }}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}?page={{ current_page|add:'-1' }}{% endif %}{% else %}{% url 'weblog:Index' %}?page={{ current_page|add:'-1' }}{% endif %}">{{ current_page|add:'-1' }}</a></li>
+ {% endif %}
+ {% endif %}
+ <li class="active"><a href="#">{{ current_page }}</a></li>
+ {% if current_page != last_page %}
+ {% if current_page|add:'1' <= last_page %}
+ <li><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}?page={{ current_page|add:'1' }}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}?page={{ current_page|add:'1' }}{% endif %}{% else %}{% url 'weblog:Index' %}?page={{ current_page|add:'1' }}{% endif %}">{{ current_page|add:'1' }}</a></li>
+ {% endif %}
+ {% if current_page|add:'2' <= last_page %}
+ <li><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}?page={{ current_page|add:'2' }}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}?page={{ current_page|add:'2' }}{% endif %}{% else %}{% url 'weblog:Index' %}?page={{ current_page|add:'2' }}{% endif %}">{{ current_page|add:'2' }}</a></li>
+ {% endif %}
+ {% if current_page|add:'-1' <= 1 and current_page|add:'3' <= last_page %}
+ <li><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}?page={{ current_page|add:'3' }}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}?page={{ current_page|add:'3' }}{% endif %}{% else %}{% url 'weblog:Index' %}?page={{ current_page|add:'3' }}{% endif %}">{{ current_page|add:'3' }}</a></li>
+ {% endif %}
+ {% if current_page == 1 and current_page|add:'4' <= last_page %}
+ <li><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}?page={{ current_page|add:'4' }}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}?page={{ current_page|add:'4' }}{% endif %}{% else %}{% url 'weblog:Index' %}?page={{ current_page|add:'4' }}{% endif %}">{{ current_page|add:'4' }}</a></li>
+ {% endif %}
+ <li title="{% trans 'Next' context 'Page' %}"><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}?page={{ current_page|add:'1' }}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}?page={{ current_page|add:'1' }}{% endif %}{% else %}{% url 'weblog:Index' %}?page={{ current_page|add:'1' }}{% endif %}">→</a></li>
+ <li title="{% trans 'Last' context 'Page' %}"><a href="{% if category %}{% if category == 'misc' %}{% url 'weblog:CategoryIndex' category_slug='misc' %}?page={{ last_page }}{% else %}{% url 'weblog:CategoryIndex' category_slug=category.slug %}?page={{ last_page }}{% endif %}{% else %}{% url 'weblog:Index' %}?page={{ last_page }}{% endif %}">»</a></li>
+ {% endif %}
+ </ul>
+ {% endif %}
+ {% else %}
+ <div class="text-center"><h2>{% trans 'Nothing has been posted yet.' %}</h2></div>
+ {% endif %}
+{% endblock %}
\ No newline at end of file diff --git a/weblog/templates/weblog/post.html b/weblog/templates/weblog/post.html new file mode 100644 index 0000000..364e17c --- /dev/null +++ b/weblog/templates/weblog/post.html @@ -0,0 +1,67 @@ +{% extends 'weblog/weblog.html' %} +{% load i18n %} +{% block title_block %}{% if post_translation %}{{ post_translation.title }}{% else %}{{ post.title }}{% endif %}{% endblock %} +{% block blog_content_block %} +<div class="container-fluid blogpost"> + <h2>{% if post_translation %}{{ post_translation.title }}{% else %}{{ post.title }}{% endif %}</h2> + <p class="publish-info">{% blocktrans with publish_date=post.publish_date %}Published on {{ publish_date }}{% endblocktrans %}{% if post_author %}{% blocktrans with author=post_author context 'Written by (Author)' %}, by {{ author }}{% endblocktrans %}{% endif %}</p> + <hr> + {% if post_translation %} + {{ post_translation.content|safe }} + {% else %} + {{ post.content|safe }} + {% endif %} + {% if post_categories %} + <hr> + <p>{% trans 'Categories' context 'Post categories' %}: + {% for post_category in post_categories %} + <a class="label label-info" href="{% url 'weblog:CategoryIndex' category_slug=post_category.slug %}">{{ post_category.name }}</a> + {% endfor %} + </p>{% endif %} + {% if enable_comments %} + <hr id="#comment-section"> + {% if user.is_authenticated or allow_anon_comments %} + <h3>{% trans 'Leave a comment' %}</h3> + <form id="comment_form" method="POST" action="{{ post.get_absolute_url }}#comment-section"> + {% csrf_token %} + <div class="form-group"> + {{ comment_form }} + </div> + <div class="form-group text-right"> + <button class="btn btn-primary" type="submit" value="Submit">{% trans 'Submit comment' %}</button> + </div> + </form> + {% else %} + <h4>{% trans 'To leave a comment you need to sign in' %}</h4> + {% endif %} + {% if comments %} + {% if comment_submission %} + {% if comment_submission_error %} + <div class="alert alert-danger alert-dismissable"> + <a href="#" class="close" data-dismiss="alert" aria-label="close">×</a> + {{ comment_submission_error }} + </div> + {% else %} + <div class="alert alert-success alert-dismissable"> + <a href="#" class="close" data-dismiss="alert" aria-label="close">×</a> + {% trans 'Comment submited successfully' %} + </div> + {% endif %} + {% endif %} + {% for comment in comments %} + <div class="media"> + <div class="media-body"> + <h4 class="media-heading">{% if comment.author %}{{ comment.author.get_username }}{% else %}{% trans 'Anonymous' context 'Unauthenticated comment poster' %}{% endif %}</h4> + <p>{{ comment.content }}</p> + </div> + </div> + {% endfor %} + {% else %} + <div class="text-center"> + <br> + <h3>{% trans 'Nobody has left a comment on this post yet' %}</h3> + </div> + {% endif %} + {% endif %} +</div> +{% endblock %}
\ No newline at end of file diff --git a/weblog/templates/weblog/sidebar_archive.html b/weblog/templates/weblog/sidebar_archive.html new file mode 100644 index 0000000..5953bfe --- /dev/null +++ b/weblog/templates/weblog/sidebar_archive.html @@ -0,0 +1,13 @@ +{% load i18n %} +<h3>{% trans 'Archive' context 'Blog archive' %}</h3> +<ul class='archive-list'> +{% for a_year in archive %} +<li><a class="node-toggle" node-target="{{ a_year.0 }}-list" node-state="closed" href="javascript:void(0)" onclick="toggleNode(this);">+</a> <a href="{% url 'weblog:ArchiveIndex' year=a_year.0 %}">{{ a_year.0 }}</a> + <ul id="{{ a_year.0 }}-list" class='archive-child-list'> + {% for a_month in a_year.1 %} + <li><a href="{% url 'weblog:ArchiveIndex' year=a_year.0 month=a_month.0 %}">{{ a_month.1 }}</a></li> + {% endfor %} + </ul> +</li> +{% endfor %} +</ul>
\ No newline at end of file diff --git a/weblog/templates/weblog/sidebar_categories.html b/weblog/templates/weblog/sidebar_categories.html new file mode 100644 index 0000000..7e7fa5c --- /dev/null +++ b/weblog/templates/weblog/sidebar_categories.html @@ -0,0 +1,11 @@ +{% load i18n %} +{% if categories %} +<h3>{% trans 'Categories' context 'Post categories' %}</h3> +<div class="list-group weblog-categories"> + {% for category in categories %} + <a href="{% url 'weblog:CategoryIndex' category_slug=category.slug %}" class="list-group-item{% if category.slug == selected_cat_slug %} active{% endif %}"> + {{ category.name }} + </a> + {% endfor %} +</div> +{% endif %}
\ No newline at end of file diff --git a/weblog/templates/weblog/weblog.html b/weblog/templates/weblog/weblog.html new file mode 100644 index 0000000..9980b54 --- /dev/null +++ b/weblog/templates/weblog/weblog.html @@ -0,0 +1,42 @@ +{% extends base_template %} +{% load i18n %} +{% load weblog_extras %} +{% block title_block %}{% endblock %} +{% block blog_block %} +<ol class="breadcrumb"> + {% if breadcrumbs %} + <li><a href="{% url 'weblog:Index' %}">{{ blog_title }}</a></li> + {% for crumb in breadcrumbs %} + {% if forloop.last %} + <li class="active">{{ crumb.name }}</li> + {% else %}<li><a href="{{ crumb.url }}">{{ crumb.name }}</a></li>{% endif %} + {% endfor %} + {% else %} + <li class="active"><a href="{% url 'weblog:Index' %}">{{ blog_title }}</a></li> + {% endif %} +</ol> +<div class="row"> + <div class="{% if show_sidebar %}col-sm-9{% else %}container-fluid{% endif %}"> + {% block blog_content_block %} + {% endblock %} + </div> + {% if show_sidebar %} + <div class="col-sm-3 weblog-sidebar"> + {% if show_categories %} + {% if category %} + {% if category == 'misc' %} + {% get_sidebar_categories category %} + {% else %} + {% get_sidebar_categories category.slug %} + {% endif %} + {% else %} + {% get_sidebar_categories %} + {% endif %} + {% endif %} + {% if show_archive %} + {% get_sidebar_archive %} + {% endif %} + </div> + {% endif %} +</div> +{% endblock %}
\ No newline at end of file diff --git a/weblog/templates/weblog_base.html b/weblog/templates/weblog_base.html new file mode 100644 index 0000000..536e736 --- /dev/null +++ b/weblog/templates/weblog_base.html @@ -0,0 +1,28 @@ +<!DOCTYPE html>
+{% load static %}
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Simple blog - {% block title_block %} Home {% endblock %}</title>
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
+ <link rel="stylesheet" href="{% static '/weblog/css/weblog.css' %}">
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js" defer></script>
+ <script src="{% static '/weblog/js/weblog.js' %}" defer></script>
+ <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" defer></script>
+ </head>
+ <body>
+ <nav class="navbar navbar-inverse">
+ <div class="container nav-container">
+ <div class="navbar-header">
+ <a class="navbar-brand" href="{% url 'weblog:Index' %}">{{ blog_title }}</a>
+ </div>
+ </div>
+ </nav>
+ <div class="container">
+ {% block content_block %}
+ {% block blog_block %}
+ {% endblock %}
+ {% endblock %}
+ </div>
+ </body>
+</html>
\ No newline at end of file diff --git a/weblog/templates/weblog_base_old.html b/weblog/templates/weblog_base_old.html new file mode 100644 index 0000000..a4dbd30 --- /dev/null +++ b/weblog/templates/weblog_base_old.html @@ -0,0 +1,35 @@ +<!DOCTYPE html>
+{% load static %}
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Simple blog - {% block title_block %} Home {% endblock %}</title>
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
+ <link rel="stylesheet" href="{% static '/weblog/css/weblog.css' %}">
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js" defer></script>
+ <script src="{% static '/weblog/js/weblog.js' %}" defer></script>
+ <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" defer></script>
+ </head>
+ <body>
+ <nav class="navbar navbar-inverse">
+ <div class="container nav-container">
+ <div class="navbar-header">
+ <a class="navbar-brand" href="{% url 'weblog:Index' %}">{{ blog_title }}</a>
+ </div>
+ <div class="collapse navbar-collapse">
+ <ul class="nav navbar-nav navbar-right">
+ <li><a href="{% url 'weblog:ChangeLanguage' language='en' %}?next={{ request.path }}">EN</a></li>
+ <li><a href="{% url 'weblog:ChangeLanguage' language='es' %}?next={{ request.path }}">ES</a></li>
+ <li><a href="{% url 'weblog:ChangeLanguage' language='ru' %}?next={{ request.path }}">RU</a></li>
+ </ul>
+ </div>
+ </div>
+ </nav>
+ <div class="container">
+ {% block content_block %}
+ {% block blog_block %}
+ {% endblock %}
+ {% endblock %}
+ </div>
+ </body>
+</html>
\ No newline at end of file diff --git a/weblog/templatetags/__init__.py b/weblog/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/weblog/templatetags/__init__.py diff --git a/weblog/templatetags/__pycache__/__init__.cpython-35.pyc b/weblog/templatetags/__pycache__/__init__.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..8b2440b --- /dev/null +++ b/weblog/templatetags/__pycache__/__init__.cpython-35.pyc diff --git a/weblog/templatetags/__pycache__/__init__.cpython-36.pyc b/weblog/templatetags/__pycache__/__init__.cpython-36.pyc Binary files differnew file mode 100644 index 0000000..8ec5021 --- /dev/null +++ b/weblog/templatetags/__pycache__/__init__.cpython-36.pyc diff --git a/weblog/templatetags/__pycache__/weblog_extras.cpython-35.pyc b/weblog/templatetags/__pycache__/weblog_extras.cpython-35.pyc Binary files differnew file mode 100644 index 0000000..bcc8327 --- /dev/null +++ b/weblog/templatetags/__pycache__/weblog_extras.cpython-35.pyc diff --git a/weblog/templatetags/__pycache__/weblog_extras.cpython-36.pyc b/weblog/templatetags/__pycache__/weblog_extras.cpython-36.pyc Binary files differnew file mode 100644 index 0000000..eb1b1e6 --- /dev/null +++ b/weblog/templatetags/__pycache__/weblog_extras.cpython-36.pyc diff --git a/weblog/templatetags/weblog_extras.py b/weblog/templatetags/weblog_extras.py new file mode 100644 index 0000000..1f72a86 --- /dev/null +++ b/weblog/templatetags/weblog_extras.py @@ -0,0 +1,70 @@ +from django import template +from django.utils import translation +from django.utils.translation import ugettext_lazy as _, pgettext_lazy +from django.conf import settings +from weblog.apps import SETTINGS as blog_settings +from weblog.models import Category, CategoryTranslation, BlogPost +import datetime + +IS_MULTILINGUAL = blog_settings['multilingual'] + +MONTHS = ( + _('January'), + _('February'), + _('March'), + _('April'), + _('May'), + _('June'), + _('July'), + _('August'), + _('September'), + _('October'), + _('November'), + _('December'), +) + +register = template.Library() + +@register.inclusion_tag('weblog/sidebar_categories.html') +def get_sidebar_categories(selected_cat_slug=None): + now = datetime.datetime.now() + current_language = translation.get_language() + if current_language is None: + current_language = settings.LANGUAGE_CODE + context_dict = {'categories': [], 'selected_cat_slug': selected_cat_slug} + for raw_category in Category.objects.all(): + next_category = {'name': raw_category.name, 'slug': raw_category.slug} + if CategoryTranslation.objects.filter(category=raw_category).count() > 0 and IS_MULTILINGUAL: + for category_translation in CategoryTranslation.objects.filter(category=raw_category): + if current_language[0:2] == category_translation.language[0:2]: + next_category['name'] = category_translation.name + context_dict['categories'].append(next_category) + if BlogPost.objects.filter(published=True, publish_date__lte=now, categories=None).count() > 0: + context_dict['categories'].append({'name': pgettext_lazy('Posts without category', 'Uncategorized'), 'slug': 'misc'}) + return context_dict + + +@register.inclusion_tag('weblog/sidebar_archive.html') +def get_sidebar_archive(): + now = datetime.datetime.now() + oldest_post = BlogPost.objects.filter(published=True).reverse()[0] + first_year = oldest_post.publish_date.year + first_month = oldest_post.publish_date.month + newest_post = BlogPost.objects.filter(published=True, publish_date__lte=now)[0] + latest_year = newest_post.publish_date.year + latest_month = newest_post.publish_date.month + c_month = first_month + c_year = first_year + archive = [] + while c_year <= latest_year: + if BlogPost.objects.filter(publish_date__year=c_year, publish_date__lte=now, published=True).count() > 0: + this_years_months = [] + while (c_year < latest_year or c_month <= latest_month) and c_month <= 12: + if BlogPost.objects.filter(publish_date__month=c_month, publish_date__lte=now, published=True).count() > 0: + this_years_months.append((c_month, MONTHS[c_month-1])) + c_month+=1 + archive.append((c_year, this_years_months)) + c_year+=1 + c_month=1 + archive.reverse() + return {'archive': archive} diff --git a/weblog/tests.py b/weblog/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/weblog/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/weblog/urls.py b/weblog/urls.py new file mode 100644 index 0000000..643e8c7 --- /dev/null +++ b/weblog/urls.py @@ -0,0 +1,12 @@ +from django.conf.urls import url
+from . import views
+
+app_name = 'weblog'
+urlpatterns = [
+ url(r'^$', views.Index, name='Index'),
+ url(r'^change-language/(?P<language>[-\w]+)/$', views.ChangeLanguage, name='ChangeLanguage'),
+ url(r'^(?P<year>[0-9]{4})/$', views.Index, name='ArchiveIndex'),
+ url(r'^(?P<year>[0-9]{4})/(?P<month>[0-9]{1,2})/$', views.Index, name='ArchiveIndex'),
+ url(r'^(?P<category_slug>[-\w]+)/$', views.Index, name='CategoryIndex'),
+ url(r'^(?P<category_slug>[-\w]+)/(?P<post_slug>[-\w]+)/$', views.PostView, name='PostView'),
+]
\ No newline at end of file diff --git a/weblog/views.py b/weblog/views.py new file mode 100644 index 0000000..8a05bcc --- /dev/null +++ b/weblog/views.py @@ -0,0 +1,207 @@ +from django.shortcuts import render, get_object_or_404, redirect, reverse +from django.http import Http404, HttpResponseRedirect +from django.conf import settings +from django.utils import translation +from django.utils.translation import ugettext_lazy as _, pgettext_lazy +from .apps import SETTINGS as blog_settings +from .models import BlogPost, Translation, PostComment, Category, CategoryTranslation, PostCommentForm +import datetime + +#Why the hell didn't I just pass the variables to the context_dict in the first place?? +#Need to remove this later +IS_MULTILINGUAL = blog_settings['multilingual'] +BASE_TEMPLATE = blog_settings['base_template'] +BLOG_TITLE = blog_settings['blog_title'] +SHOW_SIDEBAR = blog_settings['show_sidebar'] +POSTS_PER_PAGE = blog_settings['posts_per_page'] +SHOW_AUTHOR = blog_settings['show_author'] +USE_AUTHORS_USERNAME = blog_settings['use_authors_username'] +ENABLE_COMMENTS = blog_settings['enable_comments'] +ALLOW_ANON_COMMENTS = blog_settings['allow_anon_comments'] + +def Index(request, **kwargs): + context_dict = blog_settings.copy() + now = datetime.datetime.now() + all_pages = BlogPost.objects.filter(published=True, publish_date__lte=now) + category = None + if kwargs is not None: + category_slug = kwargs.get('category_slug') + year = kwargs.get('year') + month = kwargs.get('month') + if category_slug: + if category_slug == 'misc': + all_pages = BlogPost.objects.filter(published=True, publish_date__lte=now, categories=None) + context_dict['category'] = 'misc' + else: + category = get_object_or_404(Category, slug=category_slug) + context_dict['category'] = category + all_pages = BlogPost.objects.filter(published=True, publish_date__lte=now, categories__slug=category_slug) + if year: + all_pages = BlogPost.objects.filter(published=True, publish_date__lte=now, publish_date__year=year) + if month: + all_pages = BlogPost.objects.filter(published=True, publish_date__lte=now, publish_date__month=month) + post_count = all_pages.count() + if post_count < 1: + return render(request, 'weblog/index.html', context_dict) + page = 0 + if request.GET.get('page'): + page = int(request.GET['page'])-1 + if page * POSTS_PER_PAGE + 1 > post_count: + page = 0 + context_dict['current_page'] = page+1 + slice_start = page*POSTS_PER_PAGE + slice_end = page*POSTS_PER_PAGE + POSTS_PER_PAGE + if slice_end >= post_count: + slice_end = post_count + if post_count % POSTS_PER_PAGE == 0: + last_page = int(post_count/POSTS_PER_PAGE) + else: + last_page = int(post_count/POSTS_PER_PAGE)+1 + context_dict['last_page'] = last_page + posts_raw = all_pages[slice_start:slice_end] + if category_slug: + posts_raw = all_pages[slice_start:slice_end] + current_language = translation.get_language() + if current_language is None: + current_language = settings.LANGUAGE_CODE + if category_slug: + if IS_MULTILINGUAL and category_slug != 'misc': + category_translations = CategoryTranslation.objects.filter(category=category) + if category_translations.count() > 0: + for cat_trans in category_translations: + if current_language[0:2] == cat_trans.language[0:2]: + context_dict['category'] = cat_trans + if category_slug == 'misc': + context_dict['breadcrumbs'] = [{'url': reverse('weblog:CategoryIndex', kwargs={'category_slug': category_slug}), 'name': pgettext_lazy('Posts without category', 'Uncategorized')},] + else: + context_dict['breadcrumbs'] = [{'url': reverse('weblog:CategoryIndex', kwargs={'category_slug': category_slug}), 'name': context_dict['category']},] + posts = [] + for post_raw in posts_raw: + post = {'publish_date': post_raw.publish_date, 'url': post_raw.get_absolute_url()} + if SHOW_AUTHOR: + post['author'] = post_raw.author.get_full_name() + if USE_AUTHORS_USERNAME: + post['author'] = post_raw.author.get_username() + translation_exists = False + post_translations = Translation.objects.filter(post=post_raw) + if post_translations.count() < 1 or not IS_MULTILINGUAL: + post['title'] = post_raw.title + post['content'] = post_raw.content + post['preview_image'] = post_raw.preview_image + if len(post_raw.preview_text) > 5: + post['preview_text'] = post_raw.preview_text + else: + post['preview_text'] = post_raw.content.split('</p>', 1)[0]+'</p>' + else: + post_trans = None + orig_lang = post_raw.original_language + if len(orig_lang) < 2: + orig_lang = settings.LANGUAGE_CODE[0:2] + post['languages'] = [orig_lang,] + for post_translation in post_translations: + post['languages'].append(post_translation.language) + if current_language[0:2] == post_translation.language[0:2]: + post_trans = post_translation + if post_trans: + post['title'] = post_trans.title + post['content'] = post_trans.content + post['current_language'] = post_trans.language + post['preview_image'] = post_trans.preview_image + if len(post_trans.preview_text) > 5: + post['preview_text'] = post_trans.preview_text + else: + post['preview_text'] = post_trans.content.split('\n', 1)[0]+'</p>' + else: + post['title'] = post_raw.title + post['content'] = post_raw.content + post['current_language'] = orig_lang + post['preview_image'] = post_raw.preview_image + if len(post_raw.preview_text) > 5: + post['preview_text'] = post_raw.preview_text + else: + post['preview_text'] = post_raw.content.split('</p>', 1)[0]+'</p>' + posts.append(post) + context_dict['posts'] = posts + return render(request, 'weblog/index.html', context_dict) + + +def PostView(request, category_slug, post_slug): + post = get_object_or_404(BlogPost, slug=post_slug) + context_dict = blog_settings.copy() + context_dict['comment_form'] = PostCommentForm() + post_translations = Translation.objects.filter(post=post) + category = None + current_language = translation.get_language() + if current_language is None: + current_language = settings.LANGUAGE_CODE + if category_slug: + if category_slug == 'misc': + context_dict['category'] = 'misc' + else: + category = get_object_or_404(Category, slug=category_slug) + context_dict['category'] = category + if IS_MULTILINGUAL: + category_translations = CategoryTranslation.objects.filter(category=category) + if category_translations.count() > 0: + for cat_trans in category_translations: + if current_language[0:2] == cat_trans.language[0:2]: + context_dict['category'] = cat_trans + if category_slug == 'misc': + context_dict['breadcrumbs'] = [{'url': reverse('weblog:CategoryIndex', kwargs={'category_slug': category_slug}), 'name': pgettext_lazy('Posts without category', 'Uncategorized')},] + else: + context_dict['breadcrumbs'] = [{'url': reverse('weblog:CategoryIndex', kwargs={'category_slug': category_slug}), 'name': context_dict['category']},] + if SHOW_AUTHOR: + context_dict['post_author'] = post.author.get_full_name() + if USE_AUTHORS_USERNAME: + context_dict['post_author'] = post.author.get_username() + if ENABLE_COMMENTS: + context_dict['comments'] = PostComment.objects.filter(post=post) + if request.method == 'POST': + form = PostCommentForm(request.POST) + context_dict['comment_submission'] = True + if form.is_valid(): + comment_content = form.cleaned_data['content'] + if request.user.is_authenticated(): + new_comment = PostComment(author=request.user, post=post, content=comment_content) + new_comment.save() + elif ALLOW_ANON_COMMENTS: + new_comment = PostComment(post=post, content=comment_content) + new_comment.save() + else: + context_dict['comment_submission_error'] = _('You need to sign in to submit a comment') + else: + context_dict['comment_submission_error'] = _('Error submitting comment: Invalid data') + context_dict['post'] = post + if post.categories.all().count() > 0: + context_dict['post_categories'] = [] + for raw_category in post.categories.all(): + next_category = {'name': raw_category.name, 'slug': raw_category.slug} + if CategoryTranslation.objects.filter(category=raw_category).count() > 0 and IS_MULTILINGUAL: + for category_translation in CategoryTranslation.objects.filter(category=raw_category): + if current_language[0:2] == category_translation.language[0:2]: + next_category['name'] = category_translation.name + context_dict['post_categories'].append(next_category) + if post_translations.count() < 1 or not IS_MULTILINGUAL: + context_dict['breadcrumbs'].append({'url': post.get_absolute_url(), 'name': post.title}) + return render(request, 'weblog/post.html', context_dict) + orig_lang = post.original_language + if len(orig_lang) < 2: + orig_lang = settings.LANGUAGE_CODE[0:2] + context_dict['languages'] = [orig_lang,] + for post_translation in post_translations: + context_dict['languages'].append(post_translation.language) + if current_language[0:2] == post_translation.language[0:2]: + context_dict['post_translation'] = post_translation + if 'post_translation' in context_dict: + context_dict['breadcrumbs'].append({'url': post.get_absolute_url(), 'name': post_translation.title}) + else: + context_dict['breadcrumbs'].append({'url': post.get_absolute_url(), 'name': post.title}) + return render(request, 'weblog/post.html', context_dict) + +def ChangeLanguage(request, language): + translation.activate(language) + request.session[translation.LANGUAGE_SESSION_KEY] = language + if request.GET.get('next'): + return HttpResponseRedirect(request.GET['next']) + return HttpResponseRedirect(reverse('weblog:Index')) +
\ No newline at end of file |