From 28b67ad39af15d27bad7e55a0ae7fafe91139239 Mon Sep 17 00:00:00 2001 From: Yaroslsav-95 Date: Wed, 31 Jan 2018 08:27:11 +0300 Subject: Initial --- LICENSE | 11 + MANIFEST.in | 5 + README.md | 34 ++- setup.py | 36 +++ weblog/__init__.py | 79 ++++++ weblog/admin.py | 46 ++++ weblog/apps.py | 18 ++ weblog/locale/es/LC_MESSAGES/django.mo | Bin 0 -> 3062 bytes weblog/locale/es/LC_MESSAGES/django.po | 274 +++++++++++++++++++++ weblog/locale/ru/LC_MESSAGES/django.mo | Bin 0 -> 3905 bytes weblog/locale/ru/LC_MESSAGES/django.po | 274 +++++++++++++++++++++ weblog/migrations/0001_initial.py | 65 +++++ weblog/migrations/0002_auto_20180113_1606.py | 57 +++++ weblog/migrations/0003_auto_20180119_0156.py | 28 +++ weblog/migrations/0004_auto_20180119_0156.py | 21 ++ weblog/migrations/0005_auto_20180119_0231.py | 20 ++ weblog/migrations/0006_auto_20180121_1002.py | 27 ++ weblog/migrations/0007_auto_20180122_1943.py | 22 ++ weblog/migrations/__init__.py | 0 weblog/models.py | 93 +++++++ weblog/static/weblog/css/weblog.css | 7 + weblog/static/weblog/js/weblog.js | 13 + weblog/templates/weblog/index.html | 78 ++++++ weblog/templates/weblog/post.html | 67 +++++ weblog/templates/weblog/sidebar_archive.html | 13 + weblog/templates/weblog/sidebar_categories.html | 11 + weblog/templates/weblog/weblog.html | 42 ++++ weblog/templates/weblog_base.html | 28 +++ weblog/templates/weblog_base_old.html | 35 +++ weblog/templatetags/__init__.py | 0 .../__pycache__/__init__.cpython-35.pyc | Bin 0 -> 166 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 166 bytes .../__pycache__/weblog_extras.cpython-35.pyc | Bin 0 -> 2682 bytes .../__pycache__/weblog_extras.cpython-36.pyc | Bin 0 -> 1448 bytes weblog/templatetags/weblog_extras.py | 70 ++++++ weblog/tests.py | 3 + weblog/urls.py | 12 + weblog/views.py | 207 ++++++++++++++++ 38 files changed, 1694 insertions(+), 2 deletions(-) create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 setup.py create mode 100644 weblog/__init__.py create mode 100644 weblog/admin.py create mode 100644 weblog/apps.py create mode 100644 weblog/locale/es/LC_MESSAGES/django.mo create mode 100644 weblog/locale/es/LC_MESSAGES/django.po create mode 100644 weblog/locale/ru/LC_MESSAGES/django.mo create mode 100644 weblog/locale/ru/LC_MESSAGES/django.po create mode 100644 weblog/migrations/0001_initial.py create mode 100644 weblog/migrations/0002_auto_20180113_1606.py create mode 100644 weblog/migrations/0003_auto_20180119_0156.py create mode 100644 weblog/migrations/0004_auto_20180119_0156.py create mode 100644 weblog/migrations/0005_auto_20180119_0231.py create mode 100644 weblog/migrations/0006_auto_20180121_1002.py create mode 100644 weblog/migrations/0007_auto_20180122_1943.py create mode 100644 weblog/migrations/__init__.py create mode 100644 weblog/models.py create mode 100644 weblog/static/weblog/css/weblog.css create mode 100644 weblog/static/weblog/js/weblog.js create mode 100644 weblog/templates/weblog/index.html create mode 100644 weblog/templates/weblog/post.html create mode 100644 weblog/templates/weblog/sidebar_archive.html create mode 100644 weblog/templates/weblog/sidebar_categories.html create mode 100644 weblog/templates/weblog/weblog.html create mode 100644 weblog/templates/weblog_base.html create mode 100644 weblog/templates/weblog_base_old.html create mode 100644 weblog/templatetags/__init__.py create mode 100644 weblog/templatetags/__pycache__/__init__.cpython-35.pyc create mode 100644 weblog/templatetags/__pycache__/__init__.cpython-36.pyc create mode 100644 weblog/templatetags/__pycache__/weblog_extras.cpython-35.pyc create mode 100644 weblog/templatetags/__pycache__/weblog_extras.cpython-36.pyc create mode 100644 weblog/templatetags/weblog_extras.py create mode 100644 weblog/tests.py create mode 100644 weblog/urls.py create mode 100644 weblog/views.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c06ffc1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,11 @@ +Copyright 2018 Yaroslav de la Peña Smirnov + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..5621115 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +include LICENSE +include README.rst +recursive-include weblog/static * +recursive-include weblog/templates * +recursive-include weblog/locale * \ No newline at end of file diff --git a/README.md b/README.md index 7358c09..1b0d718 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,32 @@ -# django-weblog -A simple blog engine for Django with multilingual capabilities + +# Weblog version 0.2 # + +Weblog is a simple blog engine for Django, with some focus on multilingual capabilities. It includes all of the basic features expected of a traditional Web log (also known as blog), as well as multilingual features, i.e. translations of blog posts which are delivered automatically in the user's preferred language using the internationalization capabilities of Django, enabling the possibility of targeting people from different countries in a single blog/site. + +This django app is still a work in progress. More features will be added/completed in the near future. Currently the app's strings are translated only to English (en), Russian (ru), and Spanish (es), + +### Quick Start ### + +1. Add "weblog" to your INSTALLED_APPS setting in your settings.py + +2. Include the app in your project's urls.py; for example: + + url(r'^blog/', include('weblog.urls')), + +3. Migrate the models to the database by running "python manage.py migrate" + +4. You can configure and customize the blog by adding and modifying to your liking/needs the following settings to your settings.py: + + WEBLOG_ENABLE_COMMENTS = True #Should comments be allowed on your blog + WEBLOG_ALLOW_ANON_COMMENTS = False #Are visitors allowed to leave comments without signing in (note that you should provide a way for them to register and login if you wish to allow only registered user's comments) + WEBLOG_MULTILINGUAL = True #Enable multilingual features of the weblog app (i.e.: BlogPost and Category translations) + WEBLOG_TITLE = 'Weblog Test' #The name/title of your blog (e.g.: Example Site Newsletter) + WEBLOG_BASE_TEMPLATE = 'site_base.html' #Which base template to use (if not indicated, it will use its own base template) + WEBLOG_SHOW_AUTHOR = True #Should the author of the post be shown (it uses the Django User model) + WEBLOG_USE_AUTHORS_USERNAME = True #Show the username of the author instead of the fullname + WEBLOG_SHOW_SIDEBAR = True #Enable the sidebar + WEBLOG_SHOW_CATEGORIES = True #Show links to categories in the sidebar + WEBLOG_SHOW_ARCHIVE = True #Show the archive treeview (Years>Months) in the sidebar + WEBLOG_POSTS_PER_PAGE = 10 #Number of posts that should be shown per page + +5. Note that if you use your own base template, you will either need to link bootstrap in your base template's head, or write your own styles for the site based on the bootstrap classes. You will as well need to link files "weblog/css/weblog.css" and "weblog/js/weblog.js" in your html head. \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..88265df --- /dev/null +++ b/setup.py @@ -0,0 +1,36 @@ +import os +from setuptools import find_packages, setup + +with open(os.path.join(os.path.dirname(__file__), 'README.md')) as readme: + README = readme.read() + +# allow setup.py to be run from any path +os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) + +setup( + name='django-weblog', + version='0.2.1', + packages=find_packages(), + include_package_data=True, + license='BSD License', + description='A simple blog engine for Django with multilingual capabilities.', + long_description=README, + url='https://www.yaroslavps.com/', + author='Yaroslav de la Peña Smirnov', + author_email='contact@yaroslavps.com', + classifiers=[ + 'Environment :: Web Environment', + 'Framework :: Django', + 'Framework :: Django :: 1.11', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', + ], +) \ No newline at end of file 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 new file mode 100644 index 0000000..9558ef1 Binary files /dev/null and b/weblog/locale/es/LC_MESSAGES/django.mo differ 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 , 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 \n" +"Language-Team: LANGUAGE \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 new file mode 100644 index 0000000..a419335 Binary files /dev/null and b/weblog/locale/ru/LC_MESSAGES/django.mo differ 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 , 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 \n" +"Language-Team: LANGUAGE \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 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' %} +

{% trans 'Uncategorized posts' context 'Uncategorized page title' %}

+{% else %} +

{% blocktrans with category_name=category.name context 'Posts in category' %}Posts in {{ category_name }}{% endblocktrans %}

+{% endif %} +{% else %} +

{{ blog_title }}

+{% endif %} + {% if posts %} + {% for post in posts %} +
+

{{ post.title }}

+

{% 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 %}

+
+ {% if post.preview_image %}{% endif %} + {{ post.preview_text|safe }} +
+ +
+ {% endfor %} + {% if last_page > 1 %} + + {% endif %} + {% else %} +

{% trans 'Nothing has been posted yet.' %}

+ {% 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 %} +
+

{% if post_translation %}{{ post_translation.title }}{% else %}{{ post.title }}{% endif %}

+

{% 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 %}

+
+ {% if post_translation %} + {{ post_translation.content|safe }} + {% else %} + {{ post.content|safe }} + {% endif %} + {% if post_categories %} +
+

{% trans 'Categories' context 'Post categories' %}: + {% for post_category in post_categories %} + {{ post_category.name }} + {% endfor %} +

{% endif %} + {% if enable_comments %} +
+ {% if user.is_authenticated or allow_anon_comments %} +

{% trans 'Leave a comment' %}

+
+ {% csrf_token %} +
+ {{ comment_form }} +
+
+ +
+
+ {% else %} +

{% trans 'To leave a comment you need to sign in' %}

+ {% endif %} + {% if comments %} + {% if comment_submission %} + {% if comment_submission_error %} +
+ × + {{ comment_submission_error }} +
+ {% else %} +
+ × + {% trans 'Comment submited successfully' %} +
+ {% endif %} + {% endif %} + {% for comment in comments %} +
+
+

{% if comment.author %}{{ comment.author.get_username }}{% else %}{% trans 'Anonymous' context 'Unauthenticated comment poster' %}{% endif %}

+

{{ comment.content }}

+
+
+ {% endfor %} + {% else %} +
+
+

{% trans 'Nobody has left a comment on this post yet' %}

+
+ {% endif %} + {% endif %} +
+{% 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 %} +

{% trans 'Archive' context 'Blog archive' %}

+ \ 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 %} +

{% trans 'Categories' context 'Post categories' %}

+
+ {% for category in categories %} + + {{ category.name }} + + {% endfor %} +
+{% 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 %} + +
+
+ {% block blog_content_block %} + {% endblock %} +
+ {% if show_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 %} +
+ {% endif %} +
+{% 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 @@ + +{% load static %} + + + + Simple blog - {% block title_block %} Home {% endblock %} + + + + + + + + +
+ {% block content_block %} + {% block blog_block %} + {% endblock %} + {% endblock %} +
+ + \ 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 @@ + +{% load static %} + + + + Simple blog - {% block title_block %} Home {% endblock %} + + + + + + + + +
+ {% block content_block %} + {% block blog_block %} + {% endblock %} + {% endblock %} +
+ + \ 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 diff --git a/weblog/templatetags/__pycache__/__init__.cpython-35.pyc b/weblog/templatetags/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000..8b2440b Binary files /dev/null and b/weblog/templatetags/__pycache__/__init__.cpython-35.pyc differ diff --git a/weblog/templatetags/__pycache__/__init__.cpython-36.pyc b/weblog/templatetags/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..8ec5021 Binary files /dev/null and b/weblog/templatetags/__pycache__/__init__.cpython-36.pyc differ diff --git a/weblog/templatetags/__pycache__/weblog_extras.cpython-35.pyc b/weblog/templatetags/__pycache__/weblog_extras.cpython-35.pyc new file mode 100644 index 0000000..bcc8327 Binary files /dev/null and b/weblog/templatetags/__pycache__/weblog_extras.cpython-35.pyc differ diff --git a/weblog/templatetags/__pycache__/weblog_extras.cpython-36.pyc b/weblog/templatetags/__pycache__/weblog_extras.cpython-36.pyc new file mode 100644 index 0000000..eb1b1e6 Binary files /dev/null and b/weblog/templatetags/__pycache__/weblog_extras.cpython-36.pyc differ 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[-\w]+)/$', views.ChangeLanguage, name='ChangeLanguage'), + url(r'^(?P[0-9]{4})/$', views.Index, name='ArchiveIndex'), + url(r'^(?P[0-9]{4})/(?P[0-9]{1,2})/$', views.Index, name='ArchiveIndex'), + url(r'^(?P[-\w]+)/$', views.Index, name='CategoryIndex'), + url(r'^(?P[-\w]+)/(?P[-\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('

', 1)[0]+'

' + 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]+'

' + 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('

', 1)[0]+'

' + 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 -- cgit v1.2.3