From 5c439fd8a65faeb9cc98e74228a607b2e24e8728 Mon Sep 17 00:00:00 2001 From: Bruksx Date: Wed, 30 Sep 2026 19:27:12 +0100 Subject: [PATCH] clone cms blog --- djangocms-blog | 1 + djangocms_blog/__init__.py | 3 + djangocms_blog/addon.json | 31 + djangocms_blog/admin.py | 560 +++++++++++++ djangocms_blog/apps.py | 8 + djangocms_blog/cms_appconfig.py | 211 +++++ djangocms_blog/cms_apps.py | 42 + djangocms_blog/cms_menus.py | 192 +++++ djangocms_blog/cms_plugins.py | 243 ++++++ djangocms_blog/cms_toolbars.py | 80 ++ djangocms_blog/cms_wizards.py | 122 +++ djangocms_blog/feeds.py | 179 ++++ djangocms_blog/fields.py | 9 + djangocms_blog/forms.py | 146 ++++ djangocms_blog/liveblog/__init__.py | 0 djangocms_blog/liveblog/apps.py | 7 + djangocms_blog/liveblog/cms_plugins.py | 27 + djangocms_blog/liveblog/consumers.py | 31 + .../liveblog/migrations/0001_initial.py | 58 ++ .../migrations/0002_liveblog_title.py | 16 + .../migrations/0003_auto_20160917_0123.py | 31 + .../liveblog/migrations/__init__.py | 0 djangocms_blog/liveblog/models.py | 119 +++ djangocms_blog/liveblog/routing.py | 6 + .../liveblog/static/liveblog/js/liveblog.js | 24 + .../liveblog/js/reconnecting-websocket.min.js | 1 + .../liveblog/includes/post_detail.html | 11 + .../templates/liveblog/plugins/liveblog.html | 7 + .../locale/ar/LC_MESSAGES/django.po | 767 ++++++++++++++++++ .../locale/de/LC_MESSAGES/django.po | 764 +++++++++++++++++ .../locale/en/LC_MESSAGES/django.po | 761 +++++++++++++++++ .../locale/es/LC_MESSAGES/django.po | 763 +++++++++++++++++ .../locale/et/LC_MESSAGES/django.po | 762 +++++++++++++++++ .../locale/fr/LC_MESSAGES/django.po | 767 ++++++++++++++++++ .../locale/it/LC_MESSAGES/django.po | 765 +++++++++++++++++ .../locale/lt/LC_MESSAGES/django.po | 764 +++++++++++++++++ .../locale/nb_NO/LC_MESSAGES/django.po | 763 +++++++++++++++++ .../locale/nl/LC_MESSAGES/django.po | 763 +++++++++++++++++ .../locale/pl_PL/LC_MESSAGES/django.po | 764 +++++++++++++++++ .../locale/pt_BR/LC_MESSAGES/django.po | 763 +++++++++++++++++ .../locale/ru/LC_MESSAGES/django.po | 767 ++++++++++++++++++ .../locale/sl/LC_MESSAGES/django.po | 764 +++++++++++++++++ .../locale/tr/LC_MESSAGES/django.po | 763 +++++++++++++++++ djangocms_blog/managers.py | 197 +++++ djangocms_blog/migrations/0001_initial.py | 286 +++++++ djangocms_blog/migrations/0002_post_sites.py | 24 + .../migrations/0003_auto_20141201_2252.py | 23 + .../migrations/0004_auto_20150108_1435.py | 58 ++ .../migrations/0005_auto_20150212_1118.py | 25 + .../migrations/0006_auto_20150214_1907.py | 17 + .../migrations/0007_auto_20150719_0933.py | 216 +++++ .../migrations/0008_auto_20150814_0831.py | 17 + .../0009_latestpostsplugin_tags_new.py | 59 ++ .../migrations/0010_auto_20150923_1151.py | 153 ++++ .../migrations/0011_auto_20151024_1809.py | 84 ++ .../migrations/0012_auto_20151220_1734.py | 14 + .../migrations/0013_auto_20160201_2235.py | 19 + .../migrations/0014_auto_20160215_1331.py | 41 + .../migrations/0015_auto_20160408_1849.py | 17 + .../migrations/0016_auto_20160502_1741.py | 20 + .../migrations/0017_thumbnail_move.py | 10 + .../migrations/0018_thumbnail_move2.py | 45 + .../migrations/0019_thumbnail_move3.py | 38 + .../migrations/0020_thumbnail_move4.py | 36 + .../migrations/0021_auto_20160823_2008.py | 76 ++ .../migrations/0021_post_liveblog.py | 19 + .../migrations/0022_auto_20160605_2305.py | 17 + .../migrations/0022_auto_20170304_1040.py | 26 + .../migrations/0023_auto_20160626_1539.py | 33 + .../migrations/0024_auto_20160706_1524.py | 47 ++ .../migrations/0025_auto_20160803_0858.py | 25 + djangocms_blog/migrations/0026_merge.py | 12 + .../migrations/0027_post_date_featured.py | 17 + .../migrations/0028_auto_20170304_1040.py | 26 + .../migrations/0029_post_related.py | 17 + .../migrations/0030_auto_20170509_1831.py | 19 + .../migrations/0031_auto_20170610_1744.py | 21 + .../migrations/0032_auto_20180109_0023.py | 50 ++ .../migrations/0033_auto_20180226_1410.py | 15 + djangocms_blog/migrations/0034_merge.py | 10 + .../0035_posttranslation_subtitle.py | 17 + .../migrations/0036_auto_20180913_1809.py | 17 + .../migrations/0037_auto_20190806_0743.py | 82 ++ djangocms_blog/migrations/0038_post_media.py | 27 + .../migrations/0039_auto_20200331_2227.py | 32 + .../migrations/0040_post_include_in_rss.py | 17 + .../migrations/0041_auto_20230720_1508.py | 42 + ...horentriesplugin_cmsplugin_ptr_and_more.py | 109 +++ djangocms_blog/migrations/__init__.py | 0 djangocms_blog/models.py | 637 +++++++++++++++ djangocms_blog/settings.py | 635 +++++++++++++++ djangocms_blog/sitemaps/__init__.py | 51 ++ .../djangocms_blog/css/djangocms_blog.css | 0 .../css/djangocms_blog_admin.css | 58 ++ djangocms_blog/taggit_urls.py | 7 + .../templates/djangocms_blog/base.html | 22 + .../djangocms_blog/includes/blog_item.html | 34 + .../djangocms_blog/includes/blog_meta.html | 33 + .../djangocms_blog/plugins/archive.html | 31 + .../djangocms_blog/plugins/authors.html | 19 + .../djangocms_blog/plugins/authors_posts.html | 19 + .../djangocms_blog/plugins/categories.html | 19 + .../plugins/featured_posts.html | 9 + .../plugins/latest_entries.html | 11 + .../djangocms_blog/plugins/tags.html | 21 + .../djangocms_blog/post_archive.html | 1 + .../templates/djangocms_blog/post_detail.html | 43 + .../djangocms_blog/post_instant_article.html | 34 + .../templates/djangocms_blog/post_list.html | 41 + djangocms_blog/templatetags/__init__.py | 0 djangocms_blog/templatetags/djangocms_blog.py | 80 ++ djangocms_blog/urls.py | 39 + djangocms_blog/views.py | 191 +++++ 113 files changed, 17832 insertions(+) create mode 160000 djangocms-blog create mode 100644 djangocms_blog/__init__.py create mode 100644 djangocms_blog/addon.json create mode 100644 djangocms_blog/admin.py create mode 100644 djangocms_blog/apps.py create mode 100644 djangocms_blog/cms_appconfig.py create mode 100644 djangocms_blog/cms_apps.py create mode 100644 djangocms_blog/cms_menus.py create mode 100644 djangocms_blog/cms_plugins.py create mode 100644 djangocms_blog/cms_toolbars.py create mode 100644 djangocms_blog/cms_wizards.py create mode 100644 djangocms_blog/feeds.py create mode 100644 djangocms_blog/fields.py create mode 100644 djangocms_blog/forms.py create mode 100644 djangocms_blog/liveblog/__init__.py create mode 100644 djangocms_blog/liveblog/apps.py create mode 100644 djangocms_blog/liveblog/cms_plugins.py create mode 100644 djangocms_blog/liveblog/consumers.py create mode 100644 djangocms_blog/liveblog/migrations/0001_initial.py create mode 100644 djangocms_blog/liveblog/migrations/0002_liveblog_title.py create mode 100644 djangocms_blog/liveblog/migrations/0003_auto_20160917_0123.py create mode 100644 djangocms_blog/liveblog/migrations/__init__.py create mode 100644 djangocms_blog/liveblog/models.py create mode 100644 djangocms_blog/liveblog/routing.py create mode 100644 djangocms_blog/liveblog/static/liveblog/js/liveblog.js create mode 100644 djangocms_blog/liveblog/static/liveblog/js/reconnecting-websocket.min.js create mode 100644 djangocms_blog/liveblog/templates/liveblog/includes/post_detail.html create mode 100644 djangocms_blog/liveblog/templates/liveblog/plugins/liveblog.html create mode 100644 djangocms_blog/locale/ar/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/de/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/en/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/es/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/et/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/fr/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/it/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/lt/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/nb_NO/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/nl/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/pl_PL/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/pt_BR/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/ru/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/sl/LC_MESSAGES/django.po create mode 100644 djangocms_blog/locale/tr/LC_MESSAGES/django.po create mode 100644 djangocms_blog/managers.py create mode 100644 djangocms_blog/migrations/0001_initial.py create mode 100644 djangocms_blog/migrations/0002_post_sites.py create mode 100644 djangocms_blog/migrations/0003_auto_20141201_2252.py create mode 100644 djangocms_blog/migrations/0004_auto_20150108_1435.py create mode 100644 djangocms_blog/migrations/0005_auto_20150212_1118.py create mode 100644 djangocms_blog/migrations/0006_auto_20150214_1907.py create mode 100644 djangocms_blog/migrations/0007_auto_20150719_0933.py create mode 100644 djangocms_blog/migrations/0008_auto_20150814_0831.py create mode 100644 djangocms_blog/migrations/0009_latestpostsplugin_tags_new.py create mode 100644 djangocms_blog/migrations/0010_auto_20150923_1151.py create mode 100644 djangocms_blog/migrations/0011_auto_20151024_1809.py create mode 100644 djangocms_blog/migrations/0012_auto_20151220_1734.py create mode 100644 djangocms_blog/migrations/0013_auto_20160201_2235.py create mode 100644 djangocms_blog/migrations/0014_auto_20160215_1331.py create mode 100644 djangocms_blog/migrations/0015_auto_20160408_1849.py create mode 100644 djangocms_blog/migrations/0016_auto_20160502_1741.py create mode 100644 djangocms_blog/migrations/0017_thumbnail_move.py create mode 100644 djangocms_blog/migrations/0018_thumbnail_move2.py create mode 100644 djangocms_blog/migrations/0019_thumbnail_move3.py create mode 100644 djangocms_blog/migrations/0020_thumbnail_move4.py create mode 100644 djangocms_blog/migrations/0021_auto_20160823_2008.py create mode 100644 djangocms_blog/migrations/0021_post_liveblog.py create mode 100644 djangocms_blog/migrations/0022_auto_20160605_2305.py create mode 100644 djangocms_blog/migrations/0022_auto_20170304_1040.py create mode 100644 djangocms_blog/migrations/0023_auto_20160626_1539.py create mode 100644 djangocms_blog/migrations/0024_auto_20160706_1524.py create mode 100644 djangocms_blog/migrations/0025_auto_20160803_0858.py create mode 100644 djangocms_blog/migrations/0026_merge.py create mode 100644 djangocms_blog/migrations/0027_post_date_featured.py create mode 100644 djangocms_blog/migrations/0028_auto_20170304_1040.py create mode 100644 djangocms_blog/migrations/0029_post_related.py create mode 100644 djangocms_blog/migrations/0030_auto_20170509_1831.py create mode 100644 djangocms_blog/migrations/0031_auto_20170610_1744.py create mode 100644 djangocms_blog/migrations/0032_auto_20180109_0023.py create mode 100644 djangocms_blog/migrations/0033_auto_20180226_1410.py create mode 100644 djangocms_blog/migrations/0034_merge.py create mode 100644 djangocms_blog/migrations/0035_posttranslation_subtitle.py create mode 100644 djangocms_blog/migrations/0036_auto_20180913_1809.py create mode 100644 djangocms_blog/migrations/0037_auto_20190806_0743.py create mode 100644 djangocms_blog/migrations/0038_post_media.py create mode 100644 djangocms_blog/migrations/0039_auto_20200331_2227.py create mode 100644 djangocms_blog/migrations/0040_post_include_in_rss.py create mode 100644 djangocms_blog/migrations/0041_auto_20230720_1508.py create mode 100644 djangocms_blog/migrations/0042_alter_authorentriesplugin_cmsplugin_ptr_and_more.py create mode 100644 djangocms_blog/migrations/__init__.py create mode 100644 djangocms_blog/models.py create mode 100644 djangocms_blog/settings.py create mode 100644 djangocms_blog/sitemaps/__init__.py create mode 100644 djangocms_blog/static/djangocms_blog/css/djangocms_blog.css create mode 100644 djangocms_blog/static/djangocms_blog/css/djangocms_blog_admin.css create mode 100644 djangocms_blog/taggit_urls.py create mode 100644 djangocms_blog/templates/djangocms_blog/base.html create mode 100644 djangocms_blog/templates/djangocms_blog/includes/blog_item.html create mode 100644 djangocms_blog/templates/djangocms_blog/includes/blog_meta.html create mode 100644 djangocms_blog/templates/djangocms_blog/plugins/archive.html create mode 100644 djangocms_blog/templates/djangocms_blog/plugins/authors.html create mode 100644 djangocms_blog/templates/djangocms_blog/plugins/authors_posts.html create mode 100644 djangocms_blog/templates/djangocms_blog/plugins/categories.html create mode 100644 djangocms_blog/templates/djangocms_blog/plugins/featured_posts.html create mode 100644 djangocms_blog/templates/djangocms_blog/plugins/latest_entries.html create mode 100644 djangocms_blog/templates/djangocms_blog/plugins/tags.html create mode 100644 djangocms_blog/templates/djangocms_blog/post_archive.html create mode 100644 djangocms_blog/templates/djangocms_blog/post_detail.html create mode 100644 djangocms_blog/templates/djangocms_blog/post_instant_article.html create mode 100644 djangocms_blog/templates/djangocms_blog/post_list.html create mode 100644 djangocms_blog/templatetags/__init__.py create mode 100644 djangocms_blog/templatetags/djangocms_blog.py create mode 100644 djangocms_blog/urls.py create mode 100644 djangocms_blog/views.py diff --git a/djangocms-blog b/djangocms-blog new file mode 160000 index 00000000..e692aef0 --- /dev/null +++ b/djangocms-blog @@ -0,0 +1 @@ +Subproject commit e692aef0bbcf8d0538175168667d6b409c59f718 diff --git a/djangocms_blog/__init__.py b/djangocms_blog/__init__.py new file mode 100644 index 00000000..4445ee56 --- /dev/null +++ b/djangocms_blog/__init__.py @@ -0,0 +1,3 @@ +__author__ = "Iacopo Spalletti" +__email__ = "i.spalletti@nephila.it" +__version__ = "2.0.11.dev1" diff --git a/djangocms_blog/addon.json b/djangocms_blog/addon.json new file mode 100644 index 00000000..684c5525 --- /dev/null +++ b/djangocms_blog/addon.json @@ -0,0 +1,31 @@ +{ + "package-name": "djangocms-blog", + "installed-apps": [ + "filer", + "easy_thumbnails", + "aldryn_apphooks_config", + "parler", + "taggit", + "taggit_autosuggest", + "meta", + "djangocms_blog", + "sortedm2m" + ], + "settings": { + "META_SITE_PROTOCOL": "https", + "META_USE_SITES": true, + "META_USE_OG_PROPERTIES": true, + "META_USE_TWITTER_PROPERTIES": true, + "META_USE_SCHEMAORG_PROPERTIES": true, + "THUMBNAIL_PROCESSORS": [ + "easy_thumbnails.processors.colorspace", + "easy_thumbnails.processors.autocrop", + "filer.thumbnail_processors.scale_and_crop_with_subject_location", + "easy_thumbnails.processors.filters" + ] + }, + "urls": [ + ["", "djangocms_blog.taggit_urls"] + ], + "message": "Installation completed, check documentation https://djangocms-blog.readthedocs.io/en/latest/installation.html#modify-templates for further information" +} diff --git a/djangocms_blog/admin.py b/djangocms_blog/admin.py new file mode 100644 index 00000000..5054b6b3 --- /dev/null +++ b/djangocms_blog/admin.py @@ -0,0 +1,560 @@ +from copy import deepcopy + +from aldryn_apphooks_config.admin import BaseAppHookConfig, ModelAppHookConfig +from cms.admin.placeholderadmin import FrontendEditableAdminMixin, PlaceholderAdminMixin +from cms.models import CMSPlugin, ValidationError +from django.apps import apps +from django.conf import settings +from django.contrib import admin, messages +from django.contrib.admin.options import InlineModelAdmin +from django.contrib.sites.models import Site +from django.db import models +from django.db.models import signals +from django.http import HttpResponseRedirect +from django.urls import path, reverse +from django.utils import timezone +from django.utils.translation import get_language_from_request, gettext_lazy as _, ngettext as __ +from parler.admin import TranslatableAdmin + +from .cms_appconfig import BlogConfig +from .forms import CategoryAdminForm, PostAdminForm +from .models import BlogCategory, Post +from .settings import get_setting + +signal_dict = {} + + +def register_extension(klass): + if issubclass(klass, InlineModelAdmin): + PostAdmin.inlines = type(PostAdmin.inlines)([klass]) + PostAdmin.inlines + return + if issubclass(klass, models.Model): + if klass in signal_dict: + raise Exception("Can not register {} twice.".format(klass)) + signal_dict[klass] = create_post_post_save(klass) + signals.post_save.connect(signal_dict[klass], sender=Post, weak=False) + return + raise Exception("Can not register {} type. You can only register a Model or a TabularInline.".format(klass)) + + +def unregister_extension(klass): + if issubclass(klass, InlineModelAdmin): + PostAdmin.inlines.remove(klass) + return + if issubclass(klass, models.Model): + if klass not in signal_dict: + raise Exception("Can not unregister {}. No signal found for this class.".format(klass)) + signals.post_save.disconnect(signal_dict[klass], sender=Post) + del signal_dict[klass] + return + raise Exception("Can not unregister {} type. You can only unregister a Model or a TabularInline.".format(klass)) + + +def create_post_post_save(model): + """A wrapper for creating create_instance function for a specific model.""" + + def create_instance(sender, instance, created, **kwargs): + """Create Model instance for every new Post.""" + if created: + model.objects.create(post=instance) + + return create_instance + + +class SiteListFilter(admin.SimpleListFilter): + title = _("site") + parameter_name = "sites" + + def lookups(self, request, model_admin): + restricted_sites = model_admin.get_restricted_sites(request).values_list("id", flat=True) + + qs = Site.objects.all() + if restricted_sites: + qs = qs.filter(id__in=restricted_sites) + + return [(site.id, str(site.name)) for site in qs] + + def queryset(self, request, queryset): + try: + if "sites" in self.used_parameters: + return queryset.on_site(Site.objects.get(pk=self.used_parameters["sites"])) + return queryset + except Site.DoesNotExist as e: # pragma: no cover + raise admin.options.IncorrectLookupParameters(e) + except ValidationError as e: # pragma: no cover + raise admin.options.IncorrectLookupParameters(e) + + +@admin.register(BlogCategory) +class BlogCategoryAdmin(ModelAppHookConfig, TranslatableAdmin): + form = CategoryAdminForm + search_fields = ["translations__name"] + list_display = [ + "name", + "parent", + "app_config", + "all_languages_column", + ] + + def get_prepopulated_fields(self, request, obj=None): + app_config_default = self._app_config_select(request, obj) + if app_config_default is None and request.method == "GET": + return {} + return {"slug": ("name",)} + + class Media: + css = {"all": ("{}djangocms_blog/css/{}".format(settings.STATIC_URL, "djangocms_blog_admin.css"),)} + + +@admin.register(Post) +class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin, ModelAppHookConfig, TranslatableAdmin): + form = PostAdminForm + list_display = ["title", "author", "date_published", "app_config", "all_languages_column", "date_published_end"] + search_fields = ("translations__title",) + autocomplete_fields = ["categories", "sites"] + date_hierarchy = "date_published" + raw_id_fields = ["author"] + frontend_editable_fields = ("title", "abstract", "post_text") + enhance_exclude = ("main_image", "tags") + actions = [ + "make_published", + "make_unpublished", + "enable_comments", + "disable_comments", + ] + inlines = [] + if apps.is_installed("djangocms_blog.liveblog"): + actions += ["enable_liveblog", "disable_liveblog"] + _fieldsets = [ + (None, {"fields": ["title", "subtitle", "slug", "publish", "include_in_rss", ["categories", "app_config"]]}), + # left empty for sites, author and related fields + (None, {"fields": [[]]}), + ( + _("Info"), + { + "fields": ["tags", ["date_published", "date_published_end", "date_featured"], ["enable_comments"]], + "classes": ("collapse",), + }, + ), + ( + _("Images"), + {"fields": [["main_image", "main_image_thumbnail", "main_image_full"]], "classes": ("collapse",)}, + ), + (_("SEO"), {"fields": [["meta_description", "meta_title", "meta_keywords"]], "classes": ("collapse",)}), + ] + """ + Default fieldsets structure. + + Follow the normal Django fieldsets syntax. + + When customizing the structure, check the :py:attr:`_fieldset_extra_fields_position` to ensure extra fields + position matches. + """ + _fieldset_extra_fields_position = { + "abstract": (0, 1), + "post_text": (0, 1), + "sites": (1, 1, 0), + "author": (1, 1, 0), + "enable_liveblog": (2, 1, 2), + "related": (1, 1, 0), + } + """ + Indexes where to append extra fields. + + Key: Supported extra / optional field name + Value: None / 2 / 3 item tuple. If you want to hide the field in any case set ``None`` as dictionary value, + otherwise use a tuple containing the index of the the "fields" attribute of the selected fieldset row and + an optional third value if the target "fields" has subgroups. + """ + + app_config_values = {"default_published": "publish"} + _sites = None + + # Bulk actions for post admin + @admin.action(description=_("Publish selection")) + def make_published(self, request, queryset): + """ + Bulk action to mark selected posts as published. + If the date_published field is empty the current time is saved as date_published. + queryset must not be empty (ensured by django CMS). + """ + cnt1 = queryset.filter( + date_published__isnull=True, + publish=False, + ).update(date_published=timezone.now(), publish=True) + cnt2 = queryset.filter( + date_published__isnull=False, + publish=False, + ).update(publish=True) + messages.add_message( + request, + messages.INFO, + __("%(updates)d entry published.", "%(updates)d entries published.", cnt1 + cnt2) + % {"updates": cnt1 + cnt2}, + ) + + @admin.action(description=_("Unpublish selection")) + def make_unpublished(self, request, queryset): + """ + Bulk action to mark selected posts as unpublished. + queryset must not be empty (ensured by django CMS). + """ + updates = queryset.filter(publish=True).update(publish=False) + messages.add_message( + request, + messages.INFO, + __("%(updates)d entry unpublished.", "%(updates)d entries unpublished.", updates) % {"updates": updates}, + ) + + @admin.action(description=_("Enable comments for selection")) + def enable_comments(self, request, queryset): + """ + Bulk action to enable comments for selected posts. + queryset must not be empty (ensured by django CMS). + """ + updates = queryset.filter(enable_comments=False).update(enable_comments=True) + messages.add_message( + request, + messages.INFO, + __("Comments for %(updates)d entry enabled.", "Comments for %(updates)d entries enabled", updates) + % {"updates": updates}, + ) + + @admin.action(description=_("Disable comments for selection ")) + def disable_comments(self, request, queryset): + """ + Bulk action to disable comments for selected posts. + queryset must not be empty (ensured by django CMS). + """ + updates = queryset.filter(enable_comments=True).update(enable_comments=False) + messages.add_message( + request, + messages.INFO, + __("Comments for %(updates)d entry disabled.", "Comments for %(updates)d entries disabled.", updates) + % {"updates": updates}, + ) + + @admin.action(description=_("Enable liveblog for selection")) + def enable_liveblog(self, request, queryset): + """ + Bulk action to enable comments for selected posts. + queryset must not be empty (ensured by django CMS). + """ + updates = queryset.filter(enable_liveblog=False).update(enable_liveblog=True) + messages.add_message( + request, + messages.INFO, + __("Liveblog for %(updates)d entry enabled.", "Liveblog for %(updates)d entries enabled.", updates) + % {"updates": updates}, + ) + + @admin.action(description=_("Disable liveblog for selection ")) + def disable_liveblog(self, request, queryset): + """ + Bulk action to disable comments for selected posts. + queryset must not be empty (ensured by django CMS). + """ + updates = queryset.filter(enable_liveblog=True).update(enable_liveblog=False) + messages.add_message( + request, + messages.INFO, + __("Liveblog for %(updates)d entry enabled.", "Liveblog for %(updates)d entries enabled.") + % {"updates": updates}, + ) + + # Make bulk action menu entries localizable + + def get_list_filter(self, request): + filters = ["app_config", "publish", "date_published"] + if get_setting("MULTISITE"): + filters.append(SiteListFilter) + try: + from taggit_helpers.admin import TaggitListFilter + + filters.append(TaggitListFilter) + except ImportError: # pragma: no cover + try: + from taggit_helpers import TaggitListFilter + + filters.append(TaggitListFilter) + except ImportError: + pass + return filters + + def get_urls(self): + """ + Customize the modeladmin urls + """ + urls = [ + path( + "publish//", + self.admin_site.admin_view(self.publish_post), + name="djangocms_blog_publish_article", + ), + ] + urls.extend(super().get_urls()) + return urls + + def post_add_plugin(self, request, obj1, obj2=None): + if isinstance(obj1, CMSPlugin): + plugin = obj1 + elif isinstance(obj2, CMSPlugin): + plugin = obj2 + if plugin.plugin_type in get_setting("LIVEBLOG_PLUGINS"): + plugin = plugin.move(plugin.get_siblings().first(), "first-sibling") + if isinstance(obj1, CMSPlugin): + return super().post_add_plugin(request, plugin) + elif isinstance(obj2, CMSPlugin): + return super().post_add_plugin(request, obj1, plugin) + + def publish_post(self, request, pk): + """ + Admin view to publish a single post + + :param request: request + :param pk: primary key of the post to publish + :return: Redirect to the post itself (if found) or fallback urls + """ + language = get_language_from_request(request, check_path=True) + try: + post = Post.objects.get(pk=int(pk)) + post.publish = True + post.save() + return HttpResponseRedirect(post.get_absolute_url(language)) + except Exception: + try: + return HttpResponseRedirect(request.headers["referer"]) + except KeyError: + return HttpResponseRedirect(reverse("djangocms_blog:posts-latest")) + + def has_restricted_sites(self, request): + """ + Whether the current user has permission on one site only + + :param request: current request + :return: boolean: user has permission on only one site + """ + sites = self.get_restricted_sites(request) + return sites and sites.count() == 1 + + def get_restricted_sites(self, request): + """ + The sites on which the user has permission on. + + To return the permissions, the method check for the ``get_sites`` + method on the user instance (e.g.: ``return request.user.get_sites()``) + which must return the queryset of enabled sites. + If the attribute does not exists, the user is considered enabled + for all the websites. + + :param request: current request + :return: boolean or a queryset of available sites + """ + try: + return request.user.get_sites() + except AttributeError: # pragma: no cover + return Site.objects.none() + + def _set_config_defaults(self, request, form, obj=None): + form = super()._set_config_defaults(request, form, obj) + sites = self.get_restricted_sites(request) + if "sites" in form.base_fields and sites.exists(): + form.base_fields["sites"].queryset = self.get_restricted_sites(request).all() + return form + + def _get_available_posts(self, config): + if config: + return self.model.objects.namespace(config.namespace).active_translations().exists() + return [] + + def get_fieldsets(self, request, obj=None): + """ + Customize the fieldsets according to the app settings + + :param request: request + :param obj: post + :return: fieldsets configuration + """ + app_config_default = self._app_config_select(request, obj) + if app_config_default is None and request.method == "GET": + return super().get_fieldsets(request, obj) + if not obj: + config = app_config_default + else: + config = obj.app_config + + fsets = deepcopy(self._fieldsets) + related_posts = [] + if config: + abstract = bool(config.use_abstract) + placeholder = bool(config.use_placeholder) + related = bool(config.use_related) + else: + abstract = get_setting("USE_ABSTRACT") + placeholder = get_setting("USE_PLACEHOLDER") + related = get_setting("USE_RELATED") + if related: + related_posts = self._get_available_posts(config) + if abstract: + self._patch_fieldsets(fsets, "abstract") + if not placeholder: + self._patch_fieldsets(fsets, "post_text") + if get_setting("MULTISITE") and not self.has_restricted_sites(request): + self._patch_fieldsets(fsets, "sites") + if request.user.is_superuser: + self._patch_fieldsets(fsets, "author") + if apps.is_installed("djangocms_blog.liveblog"): + self._patch_fieldsets(fsets, "enable_liveblog") + filter_function = get_setting("ADMIN_POST_FIELDSET_FILTER") + if related_posts: + self._patch_fieldsets(fsets, "related") + if callable(filter_function): + fsets = filter_function(fsets, request, obj=obj) + return fsets + + def _patch_fieldsets(self, fsets, field): + """Patch the fieldsets list with additional fields, based on :py:attr:`_fieldset_extra_fields_position`.""" + positions = self._get_extra_field_position(field) + if not positions or len(positions) not in (2, 3) or not all(True for i in positions[:2] if i is not None): + return fsets + if len(positions) == 2 or positions[2] is None: + fsets[positions[0]][positions[1]]["fields"].append(field) + elif len(positions) == 3: + fsets[positions[0]][positions[1]]["fields"][positions[2]].append(field) + return fsets + + def _get_extra_field_position(self, field): + """Return the position in the fieldset where to add the given field.""" + return self._fieldset_extra_fields_position.get(field, (None, None, None)) + + def get_prepopulated_fields(self, request, obj=None): + return {"slug": ("title",)} + + def save_model(self, request, obj, form, change): + obj._set_default_author(request.user) + super().save_model(request, obj, form, change) + + def get_queryset(self, request): + qs = super().get_queryset(request) + sites = self.get_restricted_sites(request) + if sites.exists(): + pks = list(sites.all().values_list("pk", flat=True)) + qs = qs.filter(sites__in=pks) + # can't use distinct here because it prevents deleting records, but we need a unique list of posts because + # filters can cause duplicates + return super().get_queryset(request).filter(pk__in=qs.values_list("pk", flat=True)) + + def save_related(self, request, form, formsets, change): + if self.get_restricted_sites(request).exists(): + if "sites" in form.cleaned_data: + form_sites = form.cleaned_data.get("sites", []) + removed = set(self.get_restricted_sites(request).all()).difference(form_sites) + diff_original = set(form.instance.sites.all()).difference(removed).union(form_sites) + form.cleaned_data["sites"] = diff_original + else: + form.instance.sites.add(*self.get_restricted_sites(request).all().values_list("pk", flat=True)) + super().save_related(request, form, formsets, change) + + class Media: + css = {"all": ("{}djangocms_blog/css/{}".format(settings.STATIC_URL, "djangocms_blog_admin.css"),)} + + +@admin.register(BlogConfig) +class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin): + @property + def declared_fieldsets(self): + return self.get_fieldsets(None) + + def get_fieldsets(self, request, obj=None): + """ + Fieldsets configuration + """ + return [ + (None, {"fields": ("type", "namespace", "app_title", "object_name")}), + ( + _("Generic"), + { + "fields": ( + "config.default_published", + "config.use_placeholder", + "config.use_abstract", + "config.set_author", + "config.use_related", + ) + }, + ), + ( + _("Layout"), + { + "fields": ( + "config.paginate_by", + "config.url_patterns", + "config.template_prefix", + "config.menu_structure", + "config.menu_empty_categories", + ("config.default_image_full", "config.default_image_thumbnail"), + ), + "classes": ("collapse",), + }, + ), + ( + _("Notifications"), + {"fields": ("config.send_knock_create", "config.send_knock_update"), "classes": ("collapse",)}, + ), + ( + _("Sitemap"), + { + "fields": ( + "config.sitemap_changefreq", + "config.sitemap_priority", + ), + "classes": ("collapse",), + }, + ), + (_("Meta"), {"fields": ("config.object_type",)}), + ( + "Open Graph", + { + "fields": ( + "config.og_type", + "config.og_app_id", + "config.og_profile_id", + "config.og_publisher", + "config.og_author_url", + "config.og_author", + ), + "description": _("You can provide plain strings, Post model attribute or method names"), + }, + ), + ( + "Twitter", + { + "fields": ( + "config.twitter_type", + "config.twitter_site", + "config.twitter_author", + ), + "description": _("You can provide plain strings, Post model attribute or method names"), + }, + ), + ( + "Schema.org", + { + "fields": ( + "config.gplus_type", + "config.gplus_author", + ), + "description": _("You can provide plain strings, Post model attribute or method names"), + }, + ), + ] + + def save_model(self, request, obj, form, change): + """ + Clear menu cache when changing menu structure + """ + if "config.menu_structure" in form.changed_data: + from menus.menu_pool import menu_pool + + menu_pool.clear(all=True) + return super().save_model(request, obj, form, change) diff --git a/djangocms_blog/apps.py b/djangocms_blog/apps.py new file mode 100644 index 00000000..36d8c55e --- /dev/null +++ b/djangocms_blog/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class BlogAppConfig(AppConfig): + name = "djangocms_blog" + verbose_name = _("django CMS Blog") + default_auto_field = "django.db.models.BigAutoField" diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py new file mode 100644 index 00000000..1565fb8d --- /dev/null +++ b/djangocms_blog/cms_appconfig.py @@ -0,0 +1,211 @@ +from aldryn_apphooks_config.models import AppHookConfig +from aldryn_apphooks_config.utils import setup_config +from app_data import AppDataForm +from django import forms +from django.db import models +from django.utils.translation import gettext_lazy as _ +from filer.models import ThumbnailOption +from parler.models import TranslatableModel, TranslatedFields + +from .settings import MENU_TYPE_COMPLETE, get_setting + + +class BlogConfig(TranslatableModel, AppHookConfig): + """ + Adds translatable, per-app-instance fields. + """ + + translations = TranslatedFields( + app_title=models.CharField(_("application title"), max_length=234), + object_name=models.CharField(_("object name"), max_length=234, default=get_setting("DEFAULT_OBJECT_NAME")), + ) + + class Meta: + verbose_name = _("blog config") + verbose_name_plural = _("blog configs") + + def get_app_title(self): + return getattr(self, "app_title", _("untitled")) + + @property + def schemaorg_type(self): + """Compatibility shim to fetch data from legacy gplus_type field.""" + return self.gplus_type + + +class BlogConfigForm(AppDataForm): + """ + Settings that can be changed per-apphook. + + Their default value is the same as the corresponding Django settings, + but it can be customized per each apphook and changed at runtime. + """ + + app_title = get_setting("AUTO_APP_TITLE") + """ + Free text title that can be used as title in templates + """ + object_name = get_setting("DEFAULT_OBJECT_NAME") + """ + Free text label for Blog items in django CMS Wizard + """ + #: Post published by default (default: :ref:`DEFAULT_PUBLISHED `) + default_published = forms.BooleanField( + label=_("Post published by default"), required=False, initial=get_setting("DEFAULT_PUBLISHED") + ) + #: Default size of full images + default_image_full = forms.ModelChoiceField( + label=_("Default size of full images"), + queryset=ThumbnailOption.objects.all(), + required=False, + help_text=_("If left empty the image size will have to be set for every newly created post."), + ) + #: Default size of thumbnail images + default_image_thumbnail = forms.ModelChoiceField( + label=_("Default size of thumbnail images"), + queryset=ThumbnailOption.objects.all(), + required=False, + help_text=_("If left empty the thumbnail image size will have to be set for every newly created post."), + ) + #: Structure of permalinks (get_absolute_url); see :ref:`AVAILABLE_PERMALINK_STYLES ` + url_patterns = forms.ChoiceField( + label=_("Permalink structure"), + required=False, + initial=get_setting("AVAILABLE_PERMALINK_STYLES")[0][0], + choices=get_setting("AVAILABLE_PERMALINK_STYLES"), + ) + #: Use placeholder and plugins for article body (default: :ref:`USE_PLACEHOLDER `) + use_placeholder = forms.BooleanField( + label=_("Use placeholder and plugins for article body"), required=False, initial=get_setting("USE_PLACEHOLDER") + ) + #: Use abstract field (default: :ref:`USE_ABSTRACT `) + use_abstract = forms.BooleanField( + label=_("Use abstract field"), required=False, initial=get_setting("USE_ABSTRACT") + ) + #: Enable related posts (default: :ref:`USE_RELATED `) + use_related = forms.BooleanField( + label=_("Enable related posts"), required=False, initial=get_setting("USE_RELATED") + ) + #: Set author by default (default: :ref:`AUTHOR_DEFAULT `) + set_author = forms.BooleanField( + label=_("Set author"), + required=False, + help_text=_("Set author by default"), + initial=get_setting("AUTHOR_DEFAULT"), + ) + #: When paginating list views, how many articles per page? (default: :ref:`PAGINATION `) + paginate_by = forms.IntegerField( + label=_("Paginate size"), + required=False, + initial=get_setting("PAGINATION"), + help_text=_("When paginating list views, how many articles per page?"), + ) + #: Alternative directory to load the blog templates from (default: "") + template_prefix = forms.CharField( + label=_("Template prefix"), + required=False, + initial="", + help_text=_("Alternative directory to load the blog templates from"), + ) + #: Menu structure (default: ``MENU_TYPE_COMPLETE``, see :ref:`MENU_TYPES `) + menu_structure = forms.ChoiceField( + label=_("Menu structure"), + required=True, + choices=get_setting("MENU_TYPES"), + initial=MENU_TYPE_COMPLETE, + help_text=_("Structure of the django CMS menu"), + ) + #: Show empty categories in menu (default: :ref:`MENU_EMPTY_CATEGORIES `) + menu_empty_categories = forms.BooleanField( + label=_("Show empty categories in menu"), + initial=get_setting("MENU_EMPTY_CATEGORIES"), + required=False, + help_text=_("Show categories with no post attached in the menu"), + ) + #: Sitemap changefreq (default: :ref:`SITEMAP_CHANGEFREQ_DEFAULT `, + #: see: :ref:`SITEMAP_CHANGEFREQ `) + sitemap_changefreq = forms.ChoiceField( + label=_("Sitemap changefreq"), + required=True, + choices=get_setting("SITEMAP_CHANGEFREQ"), + initial=get_setting("SITEMAP_CHANGEFREQ_DEFAULT"), + help_text=_("Changefreq attribute for sitemap items"), + ) + #: Sitemap priority (default: :ref:`SITEMAP_PRIORITY_DEFAULT `) + sitemap_priority = forms.CharField( + label=_("Sitemap priority"), + required=True, + initial=get_setting("SITEMAP_PRIORITY_DEFAULT"), + help_text=_("Priority attribute for sitemap items"), + ) + #: Object type (default: :ref:`TYPE `, see :ref:`TYPES `) + object_type = forms.ChoiceField( + label=_("Object type"), required=False, choices=get_setting("TYPES"), initial=get_setting("TYPE") + ) + #: Facebook type (default: :ref:`FB_TYPE `, see :ref:`FB_TYPES `) + og_type = forms.ChoiceField( + label=_("Facebook type"), required=False, choices=get_setting("FB_TYPES"), initial=get_setting("FB_TYPE") + ) + #: Facebook application ID (default: :ref:`FB_PROFILE_ID `) + og_app_id = forms.CharField( + max_length=200, label=_("Facebook application ID"), required=False, initial=get_setting("FB_PROFILE_ID") + ) + #: Facebook profile ID (default: :ref:`FB_PROFILE_ID `) + og_profile_id = forms.CharField( + max_length=200, label=_("Facebook profile ID"), required=False, initial=get_setting("FB_PROFILE_ID") + ) + #: Facebook page URL (default: :ref:`FB_PUBLISHER `) + og_publisher = forms.CharField( + max_length=200, label=_("Facebook page URL"), required=False, initial=get_setting("FB_PUBLISHER") + ) + #: Facebook author URL (default: :ref:`FB_AUTHOR_URL `) + og_author_url = forms.CharField( + max_length=200, label=_("Facebook author URL"), required=False, initial=get_setting("FB_AUTHOR_URL") + ) + #: Facebook author (default: :ref:`FB_AUTHOR `) + og_author = forms.CharField( + max_length=200, label=_("Facebook author"), required=False, initial=get_setting("FB_AUTHOR") + ) + #: Twitter type field (default: :ref:`TWITTER_TYPE `) + twitter_type = forms.ChoiceField( + label=_("Twitter type"), + required=False, + choices=get_setting("TWITTER_TYPES"), + initial=get_setting("TWITTER_TYPE"), + ) + #: Twitter site handle (default: :ref:`TWITTER_SITE `) + twitter_site = forms.CharField( + max_length=200, label=_("Twitter site handle"), required=False, initial=get_setting("TWITTER_SITE") + ) + #: Twitter author handle (default: :ref:`TWITTER_AUTHOR `) + twitter_author = forms.CharField( + max_length=200, label=_("Twitter author handle"), required=False, initial=get_setting("TWITTER_AUTHOR") + ) + #: Schema.org object type (default: :ref:`SCHEMAORG_TYPE `) + gplus_type = forms.ChoiceField( + label=_("Schema.org type"), + required=False, + choices=get_setting("SCHEMAORG_TYPES"), + initial=get_setting("SCHEMAORG_TYPE"), + ) + #: Schema.org author name abstract field (default: :ref:`SCHEMAORG_AUTHOR `) + gplus_author = forms.CharField( + max_length=200, label=_("Schema.org author name"), required=False, initial=get_setting("SCHEMAORG_AUTHOR") + ) + #: Send notifications on post update. Require channels integration + send_knock_create = forms.BooleanField( + label=_("Send notifications on post publish"), + required=False, + initial=False, + help_text=_("Emits a desktop notification -if enabled- when publishing a new post"), + ) + #: Send notifications on post update. Require channels integration + send_knock_update = forms.BooleanField( + label=_("Send notifications on post update"), + required=False, + initial=False, + help_text=_("Emits a desktop notification -if enabled- when editing a published post"), + ) + + +setup_config(BlogConfigForm, BlogConfig) diff --git a/djangocms_blog/cms_apps.py b/djangocms_blog/cms_apps.py new file mode 100644 index 00000000..292b558a --- /dev/null +++ b/djangocms_blog/cms_apps.py @@ -0,0 +1,42 @@ +from aldryn_apphooks_config.app_base import CMSConfigApp +from cms.apphook_pool import apphook_pool +from django.utils.translation import gettext_lazy as _ +from djangocms_apphook_setup.base import AutoCMSAppMixin + +from .cms_appconfig import BlogConfig +from .cms_menus import BlogCategoryMenu +from .settings import get_setting + + +@apphook_pool.register +class BlogApp(AutoCMSAppMixin, CMSConfigApp): + name = _("Blog") + _urls = [get_setting("URLCONF")] + app_name = "djangocms_blog" + app_config = BlogConfig + _menus = [BlogCategoryMenu] + auto_setup = { + "enabled": get_setting("AUTO_SETUP"), + "home title": get_setting("AUTO_HOME_TITLE"), + "page title": get_setting("AUTO_BLOG_TITLE"), + "namespace": get_setting("AUTO_NAMESPACE"), + "config_fields": {}, + "config_translated_fields": { + "app_title": get_setting("AUTO_APP_TITLE"), + "object_name": get_setting("DEFAULT_OBJECT_NAME"), + }, + } + + def get_urls(self, page=None, language=None, **kwargs): + return [get_setting("URLCONF")] + + @property + def urls(self): + return self.get_urls() + + @property + def menus(self): + return self._menus + + +BlogApp.setup() diff --git a/djangocms_blog/cms_menus.py b/djangocms_blog/cms_menus.py new file mode 100644 index 00000000..6338f941 --- /dev/null +++ b/djangocms_blog/cms_menus.py @@ -0,0 +1,192 @@ +import logging + +from cms.apphook_pool import apphook_pool +from cms.menu_bases import CMSAttachMenu +from django.contrib.sites.shortcuts import get_current_site +from django.db.models.signals import post_delete, post_save +from django.urls import Resolver404, resolve +from django.utils.translation import get_language_from_request, gettext_lazy as _ +from menus.base import Modifier, NavigationNode +from menus.menu_pool import menu_pool + +from .cms_appconfig import BlogConfig +from .models import BlogCategory, Post +from .settings import MENU_TYPE_CATEGORIES, MENU_TYPE_COMPLETE, MENU_TYPE_NONE, MENU_TYPE_POSTS, get_setting + +logger = logging.getLogger(__name__) + + +class BlogCategoryMenu(CMSAttachMenu): + """ + Main menu class + + Handles all types of blog menu + """ + + name = _("Blog menu") + _config = {} + + def get_nodes(self, request): + """ + Generates the nodelist + + :param request: + :return: list of nodes + """ + nodes = [] + + language = get_language_from_request(request, check_path=True) + current_site = get_current_site(request) + + page_site = self.instance.node.site + if self.instance and page_site != current_site: + return [] + + categories_menu = False + posts_menu = False + config = False + if self.instance: + if not self._config.get(self.instance.application_namespace, False): + try: + self._config[self.instance.application_namespace] = BlogConfig.objects.get( + namespace=self.instance.application_namespace + ) + except BlogConfig.DoesNotExist as e: + logger.exception(e) + return [] + config = self._config[self.instance.application_namespace] + if not getattr(request, "toolbar", False) or not request.toolbar.edit_mode_active: + if self.instance == self.instance.get_draft_object(): + return [] + else: + if self.instance == self.instance.get_public_object(): + return [] + if config and config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_CATEGORIES): + categories_menu = True + if config and config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_POSTS): + posts_menu = True + if config and config.menu_structure in (MENU_TYPE_NONE,): + return nodes + + used_categories = [] + if posts_menu: + posts = Post.objects + if hasattr(self, "instance") and self.instance: + posts = posts.namespace(self.instance.application_namespace).on_site() + posts = ( + posts.active_translations(language) + .distinct() + .select_related("app_config") + .prefetch_related("translations", "categories") + ) + for post in posts: + post_id = None + parent = None + used_categories.extend(post.categories.values_list("pk", flat=True)) + if categories_menu: + category = post.categories.first() + if category: + parent = "{}-{}".format(category.__class__.__name__, category.pk) + post_id = ("{}-{}".format(post.__class__.__name__, post.pk),) + else: + post_id = ("{}-{}".format(post.__class__.__name__, post.pk),) + if post_id: + node = NavigationNode(post.get_title(), post.get_absolute_url(language), post_id, parent) + nodes.append(node) + + if categories_menu: + categories = BlogCategory.objects + if config: + categories = categories.namespace(self.instance.application_namespace) + if config and not config.menu_empty_categories: + categories = categories.active_translations(language).filter(pk__in=used_categories).distinct() + else: + categories = categories.active_translations(language).distinct() + categories = ( + categories.order_by("parent__id", "translations__name") + .select_related("app_config") + .prefetch_related("translations") + ) + added_categories = [] + for category in categories: + if category.pk not in added_categories: + node = NavigationNode( + category.name, + category.get_absolute_url(), + "{}-{}".format(category.__class__.__name__, category.pk), + ("{}-{}".format(category.__class__.__name__, category.parent.id) if category.parent else None), + ) + nodes.append(node) + added_categories.append(category.pk) + + return nodes + + +class BlogNavModifier(Modifier): + """ + This navigation modifier makes sure that when + a particular blog post is viewed, + a corresponding category is selected in menu + """ + + _config = {} + + def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb): + """ + Actual modifier function + :param request: request + :param nodes: complete list of nodes + :param namespace: Menu namespace + :param root_id: eventual root_id + :param post_cut: flag for modifier stage + :param breadcrumb: flag for modifier stage + :return: nodeslist + """ + app = None + config = None + if getattr(request, "current_page", None) and request.current_page.application_urls: + app = apphook_pool.get_apphook(request.current_page.application_urls) + + if app and app.app_config: + try: + namespace = resolve(request.path).namespace + except Resolver404: # pragma: no cover + return nodes + if not self._config.get(namespace, False): + self._config[namespace] = app.get_config(namespace) + config = self._config[namespace] + try: + if config and (not isinstance(config, BlogConfig) or config.menu_structure != MENU_TYPE_CATEGORIES): + return nodes + except AttributeError: # pragma: no cover + # in case `menu_structure` is not present in config + return nodes + if post_cut: + return nodes + current_post = getattr(request, get_setting("CURRENT_POST_IDENTIFIER"), None) + category = None + if current_post and current_post.__class__ == Post: + category = current_post.categories.first() + if not category: + return nodes + + for node in nodes: + if "{}-{}".format(category.__class__.__name__, category.pk) == node.id: + node.selected = True + return nodes + + +menu_pool.register_modifier(BlogNavModifier) +menu_pool.register_menu(BlogCategoryMenu) + + +def clear_menu_cache(**kwargs): + """ + Empty menu cache when saving categories + """ + menu_pool.clear(all=True) + + +post_save.connect(clear_menu_cache, sender=BlogCategory) +post_delete.connect(clear_menu_cache, sender=BlogCategory) +post_delete.connect(clear_menu_cache, sender=BlogConfig) diff --git a/djangocms_blog/cms_plugins.py b/djangocms_blog/cms_plugins.py new file mode 100644 index 00000000..a49cc77e --- /dev/null +++ b/djangocms_blog/cms_plugins.py @@ -0,0 +1,243 @@ +import os.path + +from cms.plugin_base import CMSPluginBase +from cms.plugin_pool import plugin_pool +from django.contrib.sites.shortcuts import get_current_site +from django.db import models +from django.template.loader import select_template + +from .forms import AuthorPostsForm, BlogPluginForm, LatestEntriesForm +from .models import AuthorEntriesPlugin, BlogCategory, FeaturedPostsPlugin, GenericBlogPlugin, LatestPostsPlugin, Post +from .settings import get_setting + + +class BlogPlugin(CMSPluginBase): + module = get_setting("PLUGIN_MODULE_NAME") + form = BlogPluginForm + + def get_render_template(self, context, instance, placeholder): + """ + Select the template used to render the plugin. + + Check the default folder as well as the folders provided to the apphook config. + """ + templates = [os.path.join("djangocms_blog", instance.template_folder, self.base_render_template)] + if instance.app_config and instance.app_config.template_prefix: + templates.insert( + 0, + os.path.join(instance.app_config.template_prefix, instance.template_folder, self.base_render_template), + ) + + selected = select_template(templates) + return selected.template.name + + +@plugin_pool.register_plugin +class BlogLatestEntriesPlugin(BlogPlugin): + """ + Return the latest published posts which bypasses cache, taking into account the user / toolbar state. + """ + + name = get_setting("LATEST_ENTRIES_PLUGIN_NAME") + model = LatestPostsPlugin + form = LatestEntriesForm + filter_horizontal = ("categories",) + cache = False + base_render_template = "latest_entries.html" + + def get_fields(self, request, obj=None): + """ + Return the fields available when editing the plugin. + + 'template_folder' field is added if ``BLOG_PLUGIN_TEMPLATE_FOLDERS`` contains multiple folders. + + """ + fields = ["app_config", "latest_posts", "tags", "categories"] + if len(get_setting("PLUGIN_TEMPLATE_FOLDERS")) > 1: + fields.append("template_folder") + return fields + + def render(self, context, instance, placeholder): + """Render the plugin.""" + context = super().render(context, instance, placeholder) + context["posts_list"] = instance.get_posts(context["request"], published_only=False) + context["TRUNCWORDS_COUNT"] = get_setting("POSTS_LIST_TRUNCWORDS_COUNT") + return context + + +@plugin_pool.register_plugin +class BlogLatestEntriesPluginCached(BlogLatestEntriesPlugin): + """ + Return the latest published posts caching the result. + """ + + name = get_setting("LATEST_ENTRIES_PLUGIN_NAME_CACHED") + cache = True + + +@plugin_pool.register_plugin +class BlogFeaturedPostsPlugin(BlogPlugin): + """ + Return the selected posts which bypasses cache. + """ + + name = get_setting("FEATURED_POSTS_PLUGIN_NAME") + model = FeaturedPostsPlugin + form = BlogPluginForm + cache = False + base_render_template = "featured_posts.html" + + def get_fields(self, request, obj=None): + """ + Return the fields available when editing the plugin. + + 'template_folder' field is added if ``BLOG_PLUGIN_TEMPLATE_FOLDERS`` contains multiple folders. + + """ + fields = ["app_config", "posts"] + if len(get_setting("PLUGIN_TEMPLATE_FOLDERS")) > 1: + fields.append("template_folder") + return fields + + def render(self, context, instance, placeholder): + """Render the plugin.""" + context = super().render(context, instance, placeholder) + context["posts_list"] = instance.get_posts(context["request"], published_only=False) + context["TRUNCWORDS_COUNT"] = get_setting("POSTS_LIST_TRUNCWORDS_COUNT") + return context + + +@plugin_pool.register_plugin +class BlogFeaturedPostsPluginCached(BlogFeaturedPostsPlugin): + """ + Return the selected posts caching the result. + """ + + name = get_setting("FEATURED_POSTS_PLUGIN_NAME_CACHED") + cache = True + + +@plugin_pool.register_plugin +class BlogAuthorPostsPlugin(BlogPlugin): + """Render the list of authors.""" + + module = get_setting("PLUGIN_MODULE_NAME") + name = get_setting("AUTHOR_POSTS_PLUGIN_NAME") + model = AuthorEntriesPlugin + form = AuthorPostsForm + base_render_template = "authors.html" + filter_horizontal = ["authors"] + + def get_fields(self, request, obj=None): + """ + Return the fields available when editing the plugin. + + 'template_folder' field is added if ``BLOG_PLUGIN_TEMPLATE_FOLDERS`` contains multiple folders. + + """ + fields = ["app_config", "current_site", "authors"] + if len(get_setting("PLUGIN_TEMPLATE_FOLDERS")) > 1: + fields.append("template_folder") + return fields + + def render(self, context, instance, placeholder): + """Render the plugin.""" + context = super().render(context, instance, placeholder) + context["authors_list"] = instance.get_authors(context["request"]) + return context + + +@plugin_pool.register_plugin +class BlogAuthorPostsListPlugin(BlogAuthorPostsPlugin): + """Render the list of posts for each selected author.""" + + name = get_setting("AUTHOR_POSTS_LIST_PLUGIN_NAME") + base_render_template = "authors_posts.html" + fields = ( + ["app_config", "current_site", "authors", "latest_posts"] + ["template_folder"] + if len(get_setting("PLUGIN_TEMPLATE_FOLDERS")) > 1 + else [] + ) + + def get_fields(self, request, obj=None): + """ + Return the fields available when editing the plugin. + + 'template_folder' field is added if ``BLOG_PLUGIN_TEMPLATE_FOLDERS`` contains multiple folders. + + """ + fields = ["app_config", "current_site", "authors", "latest_posts"] + if len(get_setting("PLUGIN_TEMPLATE_FOLDERS")) > 1: + fields.append("template_folder") + return fields + + +@plugin_pool.register_plugin +class BlogTagsPlugin(BlogPlugin): + """Render the list of post tags.""" + + module = get_setting("PLUGIN_MODULE_NAME") + name = get_setting("TAGS_PLUGIN_NAME") + model = GenericBlogPlugin + base_render_template = "tags.html" + + def get_exclude(self, request, obj=None): + """Exclude 'template_folder' field if ``BLOG_PLUGIN_TEMPLATE_FOLDERS`` contains one folder.""" + return [] if len(get_setting("PLUGIN_TEMPLATE_FOLDERS")) > 1 else ["template_folder"] + + def render(self, context, instance, placeholder): + """Render the plugin.""" + context = super().render(context, instance, placeholder) + qs = instance.post_queryset(context["request"]) + context["tags"] = Post.objects.tag_cloud(queryset=qs.published()) + return context + + +@plugin_pool.register_plugin +class BlogCategoryPlugin(BlogPlugin): + """Render the list of post categories.""" + + module = get_setting("PLUGIN_MODULE_NAME") + name = get_setting("CATEGORY_PLUGIN_NAME") + model = GenericBlogPlugin + base_render_template = "categories.html" + + def get_exclude(self, request, obj=None): + """Exclude 'template_folder' field if ``BLOG_PLUGIN_TEMPLATE_FOLDERS`` contains one folder.""" + return [] if len(get_setting("PLUGIN_TEMPLATE_FOLDERS")) > 1 else ["template_folder"] + + def render(self, context, instance, placeholder): + """Render the plugin.""" + context = super().render(context, instance, placeholder) + qs = BlogCategory.objects.active_translations() + if instance.app_config: + qs = qs.namespace(instance.app_config.namespace) + if instance.current_site: + site = get_current_site(context["request"]) + qs = qs.filter(models.Q(blog_posts__sites__isnull=True) | models.Q(blog_posts__sites=site.pk)) + categories = qs.distinct() + if instance.app_config and not instance.app_config.menu_empty_categories: + categories = qs.filter(blog_posts__isnull=False).distinct() + context["categories"] = categories + return context + + +@plugin_pool.register_plugin +class BlogArchivePlugin(BlogPlugin): + """Render the list of months with available posts.""" + + module = get_setting("PLUGIN_MODULE_NAME") + name = get_setting("ARCHIVE_PLUGIN_NAME") + model = GenericBlogPlugin + base_render_template = "archive.html" + + def get_exclude(self, request, obj=None): + """Exclude 'template_folder' field if ``BLOG_PLUGIN_TEMPLATE_FOLDERS`` contains one folder.""" + return [] if len(get_setting("PLUGIN_TEMPLATE_FOLDERS")) > 1 else ["template_folder"] + + def render(self, context, instance, placeholder): + """Render the plugin.""" + context = super().render(context, instance, placeholder) + qs = instance.post_queryset(context["request"]) + context["dates"] = Post.objects.get_months(queryset=qs.published()) + return context diff --git a/djangocms_blog/cms_toolbars.py b/djangocms_blog/cms_toolbars.py new file mode 100644 index 00000000..83cefdf9 --- /dev/null +++ b/djangocms_blog/cms_toolbars.py @@ -0,0 +1,80 @@ +from cms.toolbar_base import CMSToolbar +from cms.toolbar_pool import toolbar_pool +from cms.utils.urlutils import admin_reverse +from django.urls import reverse +from django.utils.translation import gettext_lazy as _, override + +from .settings import get_setting + + +@toolbar_pool.register +class BlogToolbar(CMSToolbar): + def populate(self): + if ( + not self.is_current_app and not get_setting("ENABLE_THROUGH_TOOLBAR_MENU") + ) or not self.request.user.has_perm("djangocms_blog.add_post"): + return # pragma: no cover + admin_menu = self.toolbar.get_or_create_menu("djangocms_blog", _("Blog")) + with override(self.current_lang): + url = reverse("admin:djangocms_blog_post_changelist") + admin_menu.add_modal_item(_("Post list"), url=url) + + url = reverse("admin:djangocms_blog_blogcategory_changelist") + admin_menu.add_modal_item(_("Category list"), url=url) + + url = reverse("admin:taggit_tag_changelist") + admin_menu.add_modal_item(_("Tag list"), url=url) + + url = reverse("admin:djangocms_blog_post_add") + admin_menu.add_modal_item(_("Add post"), url=url) + current_config = getattr(self.request, get_setting("CURRENT_NAMESPACE"), None) + if current_config: + url = reverse("admin:djangocms_blog_blogconfig_change", args=(current_config.pk,)) + admin_menu.add_modal_item(_("Edit configuration"), url=url) + + current_post = getattr(self.request, get_setting("CURRENT_POST_IDENTIFIER"), None) + if current_post and self.request.user.has_perm("djangocms_blog.change_post"): # pragma: no cover # NOQA + admin_menu.add_modal_item( + _("Edit Post"), reverse("admin:djangocms_blog_post_change", args=(current_post.pk,)), active=True + ) + + def add_publish_button(self): + """ + Adds the publish button to the toolbar if the current post is unpublished + """ + current_post = getattr(self.request, get_setting("CURRENT_POST_IDENTIFIER"), None) + if ( + self.toolbar.edit_mode_active + and current_post + and not current_post.publish + and self.request.user.has_perm("djangocms_blog.change_post") + ): # pragma: no cover # NOQA + classes = ["cms-btn-action", "blog-publish"] + title = _("Publish {0} now").format(current_post.app_config.object_name) + + url = admin_reverse("djangocms_blog_publish_article", args=(current_post.pk,)) + + self.toolbar.add_button(title, url=url, extra_classes=classes, side=self.toolbar.RIGHT) + + def post_template_populate(self): + current_post = getattr(self.request, get_setting("CURRENT_POST_IDENTIFIER"), None) + if current_post and self.request.user.has_perm("djangocms_blog.change_post"): # pragma: no cover # NOQA + # removing page meta menu, if present, to avoid confusion + try: # pragma: no cover + import djangocms_page_meta # NOQA + + menu = self.request.toolbar.get_or_create_menu("page") + pagemeta = menu.get_or_create_menu("pagemeta", "meta") + menu.remove_item(pagemeta) + except ImportError: + pass + # removing page tags menu, if present, to avoid confusion + try: # pragma: no cover + import djangocms_page_tags # NOQA + + menu = self.request.toolbar.get_or_create_menu("page") + pagetags = menu.get_or_create_menu("pagetags", "tags") + menu.remove_item(pagetags) + except ImportError: + pass + self.add_publish_button() diff --git a/djangocms_blog/cms_wizards.py b/djangocms_blog/cms_wizards.py new file mode 100644 index 00000000..2ddad211 --- /dev/null +++ b/djangocms_blog/cms_wizards.py @@ -0,0 +1,122 @@ +import warnings + +from cms.api import add_plugin +from cms.utils.permissions import get_current_user +from cms.wizards.wizard_base import Wizard +from cms.wizards.wizard_pool import AlreadyRegisteredException, wizard_pool +from django import forms +from django.conf import settings +from django.utils.translation import gettext_lazy as _ + +from .cms_appconfig import BlogConfig +from .fields import slugify +from .forms import PostAdminFormBase +from .models import Post +from .settings import get_setting + + +class PostWizardForm(PostAdminFormBase): + default_appconfig = None + + slug = forms.SlugField( + label=_("Slug"), + max_length=752, + required=False, + help_text=_("Leave empty for automatic slug, or override as required."), + ) + + def __init__(self, *args, **kwargs): + if "initial" not in kwargs or not kwargs.get("initial", False): + kwargs["initial"] = {} + kwargs["initial"]["app_config"] = self.default_appconfig + if "data" in kwargs and kwargs["data"] is not None: + data = kwargs["data"].copy() + data["1-app_config"] = self.default_appconfig + kwargs["data"] = data + super().__init__(*args, **kwargs) + self.fields["app_config"].widget = forms.Select( + attrs=self.fields["app_config"].widget.attrs, + choices=self.fields["app_config"].widget.choices, + ) + self.fields["app_config"].widget.attrs["disabled"] = True + if "categories" in self.fields: + self.fields["categories"].queryset = self.available_categories + + class Meta: + model = Post + fields = ["app_config", "title", "slug", "abstract", "categories"] + + class Media: + js = ( + "admin/js/vendor/jquery/jquery.js", + "admin/js/jquery.init.js", + ) + + def save(self, commit=True): + self.instance._set_default_author(get_current_user()) + instance = super().save(commit) + self.add_plugin() + return instance + + def clean_slug(self): + """ + Generate a valid slug, in case the given one is taken + """ + source = self.cleaned_data.get("slug", "") + lang_choice = self.language_code + if not source: + source = slugify(self.cleaned_data.get("title", "")) + qs = Post._default_manager.active_translations(lang_choice).language(lang_choice) + used = list(qs.values_list("translations__slug", flat=True)) + slug = source + i = 1 + while slug in used: + slug = "{}-{}".format(source, i) + i += 1 + return slug + + def add_plugin(self): + """ + Add text field content as text plugin to the blog post. + """ + text = self.cleaned_data.get("post_text", "") + app_config = self.cleaned_data.get("app_config", None) + plugin_type = get_setting("WIZARD_CONTENT_PLUGIN") + plugin_body = get_setting("WIZARD_CONTENT_PLUGIN_BODY") + if text and app_config.use_placeholder: + opts = { + "placeholder": self.instance.content, + "plugin_type": plugin_type, + "language": self.language_code, + plugin_body: text, + } + add_plugin(**opts) + + +class PostWizard(Wizard): + pass + + +for config in BlogConfig.objects.all().order_by("namespace"): + seed = slugify("{}.{}".format(config.app_title, config.namespace)) + new_wizard = type(str(seed), (PostWizard,), {}) + new_form = type("{}Form".format(seed), (PostWizardForm,), {"default_appconfig": config.pk}) + post_wizard = new_wizard( + title=_("New {0}").format(config.object_name), + weight=200, + form=new_form, + model=Post, + description=_("Create a new {0} in {1}").format(config.object_name, config.app_title), + ) + try: + wizard_pool.register(post_wizard) + except AlreadyRegisteredException: # pragma: no cover + if settings.DEBUG: + raise + else: + warnings.warn( + f"Wizard {seed} cannot be registered. Please make sure that " + f"BlogConfig.namespace {config.namespace} and BlogConfig.app_title {config.app_title} are" + "unique together", + stacklevel=2, + ) diff --git a/djangocms_blog/feeds.py b/djangocms_blog/feeds.py new file mode 100644 index 00000000..d3a47cc8 --- /dev/null +++ b/djangocms_blog/feeds.py @@ -0,0 +1,179 @@ +from html import unescape +from io import BytesIO + +from aldryn_apphooks_config.utils import get_app_instance +from django.contrib.sites.models import Site +from django.contrib.syndication.views import Feed +from django.core.cache import cache +from django.urls import reverse +from django.utils.encoding import force_str +from django.utils.feedgenerator import Rss201rev2Feed +from django.utils.html import strip_tags +from django.utils.safestring import mark_safe +from django.utils.text import normalize_newlines +from django.utils.translation import get_language_from_request, gettext as _ +from lxml import etree + +from djangocms_blog.settings import get_setting +from djangocms_blog.views import PostDetailView + +from .models import Post + + +class LatestEntriesFeed(Feed): + feed_type = Rss201rev2Feed + feed_items_number = get_setting("FEED_LATEST_ITEMS") + + def __call__(self, request, *args, **kwargs): + self.request = request + self.namespace, self.config = get_app_instance(request) + return super().__call__(request, *args, **kwargs) + + def link(self): + return reverse("%s:posts-latest" % self.namespace, current_app=self.namespace) + + def title(self): + return Site.objects.get_current().name + + def description(self): + return _("Blog articles on %(site_name)s") % {"site_name": Site.objects.get_current().name} + + def items(self, obj=None): + return ( + Post.objects.namespace(self.namespace) + .published_on_rss() + .order_by("-date_published")[: self.feed_items_number] + ) + + def item_title(self, item): + return mark_safe(item.safe_translation_getter("title")) + + def item_description(self, item): + if item.app_config.use_abstract: + return mark_safe(item.safe_translation_getter("abstract")) + return mark_safe(item.safe_translation_getter("post_text")) + + def item_updateddate(self, item): + return item.date_modified + + def item_pubdate(self, item): + return item.date_published + + def item_guid(self, item): + return item.guid + + def item_author_name(self, item): + return item.get_author_name() + + def item_author_url(self, item): + return item.get_author_url() + + +class TagFeed(LatestEntriesFeed): + feed_items_number = get_setting("FEED_TAGS_ITEMS") + + def get_object(self, request, tag): + return tag # pragma: no cover + + def items(self, obj=None): + return Post.objects.published().filter(tags__slug=obj)[: self.feed_items_number] + + +class FBInstantFeed(Rss201rev2Feed): + date_format = "%Y-%m-%dT%H:%M:%S%z" + + def rss_attributes(self): + return {"version": self._version, "xmlns:content": "http://purl.org/rss/1.0/modules/content/"} + + def add_root_elements(self, handler): + handler.addQuickElement("title", self.feed["title"]) + handler.addQuickElement("link", self.feed["link"]) + handler.addQuickElement("description", self.feed["description"]) + if self.feed["language"] is not None: + handler.addQuickElement("language", self.feed["language"]) + for cat in self.feed["categories"]: + handler.addQuickElement("category", cat) + if self.feed["feed_copyright"] is not None: + handler.addQuickElement("copyright", self.feed["feed_copyright"]) + handler.addQuickElement("lastBuildDate", self.latest_post_date().strftime(self.date_format)) + if self.feed["ttl"] is not None: + handler.addQuickElement("ttl", self.feed["ttl"]) + + def add_item_elements(self, handler, item): + super().add_item_elements(handler, item) + if item["author"]: + handler.addQuickElement("author", item["author"]) + if item["date_pub"] is not None: + handler.addQuickElement("pubDate", item["date_pub"].strftime(self.date_format)) + if item["date_mod"] is not None: + handler.addQuickElement("modDate", item["date_mod"].strftime(self.date_format)) + + handler.startElement("description", {}) + handler._write( + "".format(unescape(normalize_newlines(force_str(item["abstract"])).replace("\n", " "))) + ) + handler.endElement("description") + handler.startElement("content:encoded", {}) + handler._write("") + handler._write(unescape(force_str(item["content"]))) + handler._write("]]>") + handler.endElement("content:encoded") + + +class FBInstantArticles(LatestEntriesFeed): + feed_type = FBInstantFeed + feed_items_number = get_setting("FEED_INSTANT_ITEMS") + + def items(self, obj=None): + return Post.objects.namespace(self.namespace).published().order_by("-date_modified")[: self.feed_items_number] + + def _clean_html(self, content): + body = BytesIO(content) + document = etree.iterparse(body, html=True) + for _a, element in document: + if not (element.text and element.text.strip()) and len(element) == 0 and element.tag == "p": + element.getparent().remove(element) + if element.tag in ("h3", "h4", "h5", "h6") and "op-kicker" not in element.attrib.get("class", ""): + element.tag = "h2" + return etree.tostring(document.root) + + def item_extra_kwargs(self, item): + if not item: + return {} + language = get_language_from_request(self.request, check_path=True) + key = item.get_cache_key(language, "feed") + content = cache.get(key) + if not content: + view = PostDetailView.as_view(instant_article=True) + response = view(self.request, slug=item.safe_translation_getter("slug")) + response.render() + content = self._clean_html(response.content) + cache.set(key, content, timeout=get_setting("FEED_CACHE_TIMEOUT")) + if item.app_config.use_abstract: + abstract = strip_tags(item.safe_translation_getter("abstract")) + else: + abstract = strip_tags(item.safe_translation_getter("post_text")) + return { + "author": item.get_author_name(), + "content": content, + "date": item.date_modified, + "date_pub": item.date_modified, + "date_mod": item.date_modified, + "abstract": abstract, + } + + def item_categories(self, item): + return [category.safe_translation_getter("name") for category in item.categories.all()] + + def item_author_name(self, item): + return "" + + def item_author_url(self, item): + return "" + + def item_description(self, item): + return None + + def item_pubdate(self, item): + return None diff --git a/djangocms_blog/fields.py b/djangocms_blog/fields.py new file mode 100644 index 00000000..4cbee724 --- /dev/null +++ b/djangocms_blog/fields.py @@ -0,0 +1,9 @@ +from django.utils.text import slugify as django_slugify + +from .settings import get_setting + +__all__ = ["slugify"] + + +def slugify(base): + return django_slugify(base, allow_unicode=get_setting("UNICODE_SLUGS")) diff --git a/djangocms_blog/forms.py b/djangocms_blog/forms.py new file mode 100644 index 00000000..4a05e830 --- /dev/null +++ b/djangocms_blog/forms.py @@ -0,0 +1,146 @@ +from django import forms +from django.conf import settings +from django.contrib.auth import get_user_model +from django.core.validators import MaxLengthValidator +from django.utils.functional import cached_property +from parler.forms import TranslatableModelForm +from taggit_autosuggest.widgets import TagAutoSuggest + +from .models import BlogCategory, BlogConfig, Post +from .settings import PERMALINK_TYPE_CATEGORY, get_setting + +User = get_user_model() + + +class ConfigFormBase: + """Base form class for all models depends on app_config.""" + + @cached_property + def app_config(self): + """ + Return the currently selected app_config, whether it's an instance attribute or passed in the request + """ + if getattr(self.instance, "app_config_id", None): + return self.instance.app_config + elif "app_config" in self.initial: + return BlogConfig.objects.get(pk=self.initial["app_config"]) + elif self.data.get("app_config", None): + return BlogConfig.objects.get(pk=self.data["app_config"]) + return None + + +class CategoryAdminForm(ConfigFormBase, TranslatableModelForm): + def __init__(self, *args, **kwargs): + self.base_fields["meta_description"].validators = [MaxLengthValidator(get_setting("META_DESCRIPTION_LENGTH"))] + original_attrs = self.base_fields["meta_description"].widget.attrs + if "cols" in original_attrs: + del original_attrs["cols"] + if "rows" in original_attrs: + del original_attrs["rows"] + original_attrs["maxlength"] = get_setting("META_DESCRIPTION_LENGTH") + self.base_fields["meta_description"].widget = forms.TextInput(original_attrs) + super().__init__(*args, **kwargs) + + if "parent" in self.fields: + qs = self.fields["parent"].queryset + if self.instance.pk: + qs = qs.exclude(pk__in=[self.instance.pk] + [child.pk for child in self.instance.descendants()]) + config = None + if getattr(self.instance, "app_config_id", None): + qs = qs.namespace(self.instance.app_config.namespace) + elif "app_config" in self.initial: + config = BlogConfig.objects.get(pk=self.initial["app_config"]) + elif self.data.get("app_config", None): + config = BlogConfig.objects.get(pk=self.data["app_config"]) + if config: + qs = qs.namespace(config.namespace) + self.fields["parent"].queryset = qs + + class Meta: + model = BlogCategory + fields = "__all__" + + +class BlogPluginForm(forms.ModelForm): + """Base plugin form to inject the list of configured template folders from BLOG_PLUGIN_TEMPLATE_FOLDERS.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if "template_folder" in self.fields: + self.fields["template_folder"].choices = get_setting("PLUGIN_TEMPLATE_FOLDERS") + + +class LatestEntriesForm(BlogPluginForm): + """Custom forms for BlogLatestEntriesPlugin to properly load taggit-autosuggest.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields["tags"].widget = TagAutoSuggest("taggit.Tag") + + class Media: + css = {"all": ("{}djangocms_blog/css/{}".format(settings.STATIC_URL, "djangocms_blog_admin.css"),)} + + +class AuthorPostsForm(BlogPluginForm): + """Custom form for BlogAuthorPostsPlugin to apply distinct to the list of authors in plugin changeform.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # apply distinct due to django issue #11707 + self.fields["authors"].queryset = User.objects.filter(djangocms_blog_post_author__publish=True).distinct() + + +class PostAdminFormBase(ConfigFormBase, TranslatableModelForm): + """ + Common methods between the admin and wizard form + """ + + class Meta: + model = Post + fields = "__all__" + + @cached_property + def available_categories(self): + qs = BlogCategory.objects + if self.app_config: + return qs.namespace(self.app_config.namespace).active_translations() + return qs + + def _post_clean_translation(self, translation): + # This is a quickfix for https://github.com/django-parler/django-parler/issues/236 + # which needs to be fixed in parler + # operating at form level ensure that if the model is validated outside the form + # the uniqueness check is not disabled + super()._post_clean_translation(translation) + self._validate_unique = False + + +class PostAdminForm(PostAdminFormBase): + def __init__(self, *args, **kwargs): + self.base_fields["meta_description"].validators = [MaxLengthValidator(get_setting("META_DESCRIPTION_LENGTH"))] + original_attrs = self.base_fields["meta_description"].widget.attrs + if "cols" in original_attrs: + del original_attrs["cols"] + if "rows" in original_attrs: + del original_attrs["rows"] + original_attrs["maxlength"] = get_setting("META_DESCRIPTION_LENGTH") + self.base_fields["meta_description"].widget = forms.TextInput(original_attrs) + self.base_fields["meta_title"].validators = [MaxLengthValidator(get_setting("META_TITLE_LENGTH"))] + super().__init__(*args, **kwargs) + if "categories" in self.fields: + if self.app_config and self.app_config.url_patterns == PERMALINK_TYPE_CATEGORY: + self.fields["categories"].required = True + self.fields["categories"].queryset = self.available_categories + + if "app_config" in self.fields: + # Don't allow app_configs to be added here. The correct way to add an + # apphook-config is to create an apphook on a cms Page. + self.fields["app_config"].widget.can_add_related = False + + if self.app_config: + if not self.initial.get("main_image_full", ""): + self.initial["main_image_full"] = self.app_config.app_data["config"].get("default_image_full") + if not self.initial.get("main_image_thumbnail", ""): + self.initial["main_image_thumbnail"] = self.app_config.app_data["config"].get( + "default_image_thumbnail" + ) diff --git a/djangocms_blog/liveblog/__init__.py b/djangocms_blog/liveblog/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/djangocms_blog/liveblog/apps.py b/djangocms_blog/liveblog/apps.py new file mode 100644 index 00000000..036a145e --- /dev/null +++ b/djangocms_blog/liveblog/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class LiveBlogAppConfig(AppConfig): + name = "djangocms_blog.liveblog" + verbose_name = _("Liveblog") diff --git a/djangocms_blog/liveblog/cms_plugins.py b/djangocms_blog/liveblog/cms_plugins.py new file mode 100644 index 00000000..e0082e93 --- /dev/null +++ b/djangocms_blog/liveblog/cms_plugins.py @@ -0,0 +1,27 @@ +from cms.plugin_pool import plugin_pool +from django.utils.translation import gettext_lazy as _ +from djangocms_text_ckeditor.cms_plugins import TextPlugin + +from djangocms_blog.settings import get_setting + +from .models import Liveblog + + +@plugin_pool.register_plugin +class LiveblogPlugin(TextPlugin): + module = get_setting("PLUGIN_MODULE_NAME") + name = _("Liveblog item") + model = Liveblog + fields = ("title", "publish", "body", "post_date") + render_template = "liveblog/plugins/liveblog.html" + + def save_model(self, request, obj, form, change): + super().save_model(request, obj, form, change) + if obj.publish: + obj.send(request) + + def render(self, context, instance, placeholder): + context = super().render(context, instance, placeholder) + instance.content = context["body"] + context["instance"] = instance + return context diff --git a/djangocms_blog/liveblog/consumers.py b/djangocms_blog/liveblog/consumers.py new file mode 100644 index 00000000..9a6e06e2 --- /dev/null +++ b/djangocms_blog/liveblog/consumers.py @@ -0,0 +1,31 @@ +from channels.generic.websocket import JsonWebsocketConsumer + +from ..models import Post + + +class LiveblogConsumer(JsonWebsocketConsumer): + def _get_post(self, kwargs): + apphook = kwargs.get("apphook") + lang = kwargs.get("lang") + slug = kwargs.get("post") + try: + return Post.objects.namespace(apphook).language(lang).active_translations(slug=slug).get() + except Post.DoesNotExist: + return + + def websocket_connect(self, message): + self.groups = self.get_groups() + return super().websocket_connect(message) + + def get_groups(self): + """ + Connect users to the group of the post according to the URL parameters + """ + post = self._get_post(self.scope["url_route"]["kwargs"]) + if post: + return [post.liveblog_group] + else: + return [] + + def send_json(self, content, close=False): + return super().send_json(content, close) diff --git a/djangocms_blog/liveblog/migrations/0001_initial.py b/djangocms_blog/liveblog/migrations/0001_initial.py new file mode 100644 index 00000000..a1d6ff18 --- /dev/null +++ b/djangocms_blog/liveblog/migrations/0001_initial.py @@ -0,0 +1,58 @@ +import django.db.models.deletion +import filer.fields.image +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("cms", "0013_urlconfrevision"), + ("filer", "0003_thumbnailoption"), + ] + + operations = [ + migrations.CreateModel( + name="Liveblog", + fields=[ + ( + "cmsplugin_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="cms.CMSPlugin", + on_delete=django.db.models.deletion.CASCADE, + ), + ), + ("body", models.TextField(verbose_name="body")), + ("publish", models.BooleanField(default=False, verbose_name="publish liveblog entry")), + ( + "image", + filer.fields.image.FilerImageField( + related_name="djangocms_blog_liveblog_image", + on_delete=django.db.models.deletion.SET_NULL, + verbose_name="image", + blank=True, + to="filer.Image", + null=True, + ), + ), + ( + "thumbnail", + models.ForeignKey( + related_name="djangocms_blog_liveblog_thumbnail", + on_delete=django.db.models.deletion.SET_NULL, + verbose_name="thumbnail size", + blank=True, + to="filer.ThumbnailOption", + null=True, + ), + ), + ], + options={ + "verbose_name": "liveblog entry", + "verbose_name_plural": "liveblog entries", + }, + bases=("cms.cmsplugin",), + ), + ] diff --git a/djangocms_blog/liveblog/migrations/0002_liveblog_title.py b/djangocms_blog/liveblog/migrations/0002_liveblog_title.py new file mode 100644 index 00000000..0066e286 --- /dev/null +++ b/djangocms_blog/liveblog/migrations/0002_liveblog_title.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("liveblog", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="liveblog", + name="title", + field=models.CharField(default="", max_length=255, verbose_name="title"), + preserve_default=False, + ), + ] diff --git a/djangocms_blog/liveblog/migrations/0003_auto_20160917_0123.py b/djangocms_blog/liveblog/migrations/0003_auto_20160917_0123.py new file mode 100644 index 00000000..5c131aac --- /dev/null +++ b/djangocms_blog/liveblog/migrations/0003_auto_20160917_0123.py @@ -0,0 +1,31 @@ +# Generated by Django 1.9.9 on 2016-09-16 23:23 + +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("liveblog", "0002_liveblog_title"), + ] + + operations = [ + migrations.AddField( + model_name="liveblog", + name="post_date", + field=models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name="post date"), + ), + migrations.AlterField( + model_name="liveblog", + name="cmsplugin_ptr", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + related_name="liveblog_liveblog", + serialize=False, + to="cms.CMSPlugin", + ), + ), + ] diff --git a/djangocms_blog/liveblog/migrations/__init__.py b/djangocms_blog/liveblog/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/djangocms_blog/liveblog/models.py b/djangocms_blog/liveblog/models.py new file mode 100644 index 00000000..ad550e16 --- /dev/null +++ b/djangocms_blog/liveblog/models.py @@ -0,0 +1,119 @@ +from operator import itemgetter + +from asgiref.sync import async_to_sync +from channels.layers import get_channel_layer +from cms.models import CMSPlugin +from cms.utils.plugins import reorder_plugins +from django.db import models +from django.template import Context +from django.utils.timezone import now +from django.utils.translation import gettext_lazy as _ +from djangocms_text_ckeditor.models import AbstractText +from filer.fields.image import FilerImageField + +from djangocms_blog.models import Post, thumbnail_model +from djangocms_blog.settings import DATE_FORMAT + + +class LiveblogInterface(models.Model): + """ + Abstract Liveblog plugin model, reusable to customize the liveblogging + plugins. + + When implementing this, you **must** call ``self._post_save()`` in the + concrete plugin model ``save`` method. + """ + + publish = models.BooleanField(_("publish liveblog entry"), default=False) + post_date = models.DateTimeField(_("post date"), blank=True, default=now) + + class Meta: + verbose_name = _("liveblog entry") + verbose_name_plural = _("liveblog entries") + abstract = True + + def __str__(self): + return str(self.pk) + + def _post_save(self): + """ + Reorder plugins according to the post_date value. All (and only) + subclasses of LiveblogInterface are taken into consideration and + reordered together + """ + items = [] + for model in LiveblogInterface.__subclasses__(): + items.extend(model.objects.filter(placeholder=self.placeholder).values("pk", "post_date")) + order = reversed([item["pk"] for item in sorted(items, key=itemgetter("post_date"))]) + reorder_plugins(self.placeholder, None, self.language, order) + + @property + def liveblog_group(self): + post = Post.objects.language(self.language).filter(liveblog=self.placeholder).first() + if post: + return post.liveblog_group + + def render(self, request): + context = Context({"request": request}) + from cms.plugin_rendering import ContentRenderer + + renderer = ContentRenderer(request) + return renderer.render_plugin( + instance=self, + context=context, + placeholder=self.placeholder, + ) + + def send(self, request): + """ + Render the content and send to the related group + """ + if self.liveblog_group: + content = self.render(request) + notification = { + "id": self.pk, + "content": content, + "creation_date": self.post_date.strftime(DATE_FORMAT), + "changed_date": self.changed_date.strftime(DATE_FORMAT), + "type": "send.json", + } + channel_layer = get_channel_layer() + group = self.liveblog_group + async_to_sync(channel_layer.group_send)(group, notification) + + +class Liveblog(LiveblogInterface, AbstractText): + """ + Basic liveblog plugin model + """ + + cmsplugin_ptr = models.OneToOneField( + CMSPlugin, related_name="%(app_label)s_%(class)s", primary_key=True, parent_link=True, on_delete=models.CASCADE + ) + title = models.CharField(_("title"), max_length=255) + image = FilerImageField( + verbose_name=_("image"), + blank=True, + null=True, + on_delete=models.SET_NULL, + related_name="djangocms_blog_liveblog_image", + ) + thumbnail = models.ForeignKey( + thumbnail_model, + verbose_name=_("thumbnail size"), + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name="djangocms_blog_liveblog_thumbnail", + ) + + class Meta: + verbose_name = _("liveblog entry") + verbose_name_plural = _("liveblog entries") + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + self._post_save() + + def __str__(self): + return AbstractText.__str__(self) diff --git a/djangocms_blog/liveblog/routing.py b/djangocms_blog/liveblog/routing.py new file mode 100644 index 00000000..b45cb536 --- /dev/null +++ b/djangocms_blog/liveblog/routing.py @@ -0,0 +1,6 @@ +from channels.routing import URLRouter +from django.urls import path + +from .consumers import LiveblogConsumer + +channel_routing = URLRouter([path("///", LiveblogConsumer.as_asgi())]) diff --git a/djangocms_blog/liveblog/static/liveblog/js/liveblog.js b/djangocms_blog/liveblog/static/liveblog/js/liveblog.js new file mode 100644 index 00000000..52e6b3ea --- /dev/null +++ b/djangocms_blog/liveblog/static/liveblog/js/liveblog.js @@ -0,0 +1,24 @@ +document.addEventListener("DOMContentLoaded", function() { + // Correctly decide between ws:// and wss:// + const ws_scheme = window.location.protocol === "https:" ? "wss" : "ws"; + const ws_path = ws_scheme + '://' + window.location.host + "/liveblog/liveblog/" + liveblog_apphook + "/" + liveblog_language + "/" + liveblog_post + "/"; + const socket = new ReconnectingWebSocket(ws_path); + // Handle incoming messages + socket.onmessage = function (message) { + // Decode the JSON + const data = JSON.parse(message.data); + // See if there's a div to replace it in, or if we should add a new one + const existing = document.querySelectorAll("div[data-post-id*='" + data.id + "']"); + const postContainer = document.getElementById("liveblog-posts") + const item = document.createElement('div'); + item.innerHTML = data.content.replace(/\"/g, "'") + if (existing.length) { + existing[0].parentNode.replaceChild(item.children[0], existing[0]); + } else { + postContainer.insertBefore( + item.children[0], postContainer.children[0] + ); + } + }; + +}, false); diff --git a/djangocms_blog/liveblog/static/liveblog/js/reconnecting-websocket.min.js b/djangocms_blog/liveblog/static/liveblog/js/reconnecting-websocket.min.js new file mode 100644 index 00000000..3015099a --- /dev/null +++ b/djangocms_blog/liveblog/static/liveblog/js/reconnecting-websocket.min.js @@ -0,0 +1 @@ +!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a}); diff --git a/djangocms_blog/liveblog/templates/liveblog/includes/post_detail.html b/djangocms_blog/liveblog/templates/liveblog/includes/post_detail.html new file mode 100644 index 00000000..a318eb68 --- /dev/null +++ b/djangocms_blog/liveblog/templates/liveblog/includes/post_detail.html @@ -0,0 +1,11 @@ +{% load cms_tags sekizai_tags %} +{% add_data "js-script" "liveblog/js/reconnecting-websocket.min.js" %} +{% add_data "js-script" "liveblog/js/liveblog.js" %} + +
+ {% render_placeholder post.liveblog %} +
diff --git a/djangocms_blog/liveblog/templates/liveblog/plugins/liveblog.html b/djangocms_blog/liveblog/templates/liveblog/plugins/liveblog.html new file mode 100644 index 00000000..d01b84a7 --- /dev/null +++ b/djangocms_blog/liveblog/templates/liveblog/plugins/liveblog.html @@ -0,0 +1,7 @@ +{% spaceless %}{% if instance.publish %} +
+

{{ instance.title }}

+

{{ instance.post_date|date:"D d M Y H:i" }}

+ {{ instance.content|safe }} +
+{% endif %}{% endspaceless %} diff --git a/djangocms_blog/locale/ar/LC_MESSAGES/django.po b/djangocms_blog/locale/ar/LC_MESSAGES/django.po new file mode 100644 index 00000000..a9e21943 --- /dev/null +++ b/djangocms_blog/locale/ar/LC_MESSAGES/django.po @@ -0,0 +1,767 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Bashar Al-Abdulhadi, 2016 +# Bashar Al-Abdulhadi, 2014 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: Bashar Al-Abdulhadi, 2016\n" +"Language-Team: Arabic (http://app.transifex.com/nephila/djangocms-blog/language/ar/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ar\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "" + +#: admin.py:131 +msgid "Info" +msgstr "" + +#: admin.py:138 +msgid "Images" +msgstr "" + +#: admin.py:141 +msgid "SEO" +msgstr "" + +#: admin.py:172 +msgid "Publish selection" +msgstr "" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "" + +#: admin.py:473 +msgid "Generic" +msgstr "" + +#: admin.py:485 +msgid "Layout" +msgstr "" + +#: admin.py:499 +msgid "Notifications" +msgstr "" + +#: admin.py:503 +msgid "Sitemap" +msgstr "" + +#: admin.py:512 +msgid "Meta" +msgstr "" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "مدونة نظام ادارة المحتوى لجانغو" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "عنوان التطبيق" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "اسم الكائن" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "إعدادات المدونة" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "إعدادات المدونة" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "غير معنون" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "نشر التدوينة تلقائيا" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "" + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "" + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "ضبط الكاتب" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "ضبطها من قبل الكاتب" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "عدد تعدد الصفحات" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "المدونة" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "قائمة المواضيع" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "إضافة موضوع" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "التعديل على موضوع" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "مواضيع المدونة على %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "" + +#: liveblog/models.py:95 +msgid "image" +msgstr "" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "" + +#: models.py:95 +msgid "parent" +msgstr "" + +#: models.py:97 +msgid "created at" +msgstr "" + +#: models.py:98 +msgid "modified at" +msgstr "" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "" + +#: models.py:102 +msgid "name" +msgstr "" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "" + +#: models.py:104 +msgid "category meta description" +msgstr "" + +#: models.py:132 +msgid "blog category" +msgstr "" + +#: models.py:133 +msgid "blog categories" +msgstr "" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "" + +#: models.py:196 +msgid "author" +msgstr "" + +#: models.py:203 +msgid "created" +msgstr "" + +#: models.py:204 +msgid "last modified" +msgstr "" + +#: models.py:205 +msgid "published since" +msgstr "" + +#: models.py:206 +msgid "published until" +msgstr "" + +#: models.py:207 +msgid "featured date" +msgstr "" + +#: models.py:208 +msgid "publish" +msgstr "" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "" + +#: models.py:214 +msgid "main image" +msgstr "" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "" + +#: models.py:230 +msgid "main image full" +msgstr "" + +#: models.py:237 +msgid "enable comments on post" +msgstr "" + +#: models.py:241 +msgid "Site(s)" +msgstr "" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "" + +#: models.py:256 +msgid "subtitle" +msgstr "" + +#: models.py:257 +msgid "abstract" +msgstr "" + +#: models.py:258 +msgid "post meta description" +msgstr "" + +#: models.py:259 +msgid "post meta keywords" +msgstr "" + +#: models.py:261 +msgid "post meta title" +msgstr "" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "" + +#: models.py:267 +msgid "text" +msgstr "" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "" + +#: models.py:314 +msgid "blog article" +msgstr "" + +#: models.py:315 +msgid "blog articles" +msgstr "" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "" + +#: models.py:493 +msgid "current site" +msgstr "" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "" + +#: models.py:497 +msgid "Plugin template" +msgstr "" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "" + +#: models.py:541 +msgid "filter by tag" +msgstr "" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "" + +#: models.py:549 +msgid "filter by category" +msgstr "" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "" + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "" + +#: models.py:574 +msgid "authors" +msgstr "" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "" + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "" + +#: settings.py:26 +msgid "Full date" +msgstr "" + +#: settings.py:27 +msgid "Year / Month" +msgstr "" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "" + +#: settings.py:29 +msgid "Just slug" +msgstr "" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "" + +#: settings.py:60 +msgid "Categories only" +msgstr "" + +#: settings.py:61 +msgid "Posts only" +msgstr "" + +#: settings.py:62 +msgid "None" +msgstr "" + +#: settings.py:71 +msgid "always" +msgstr "" + +#: settings.py:72 +msgid "hourly" +msgstr "" + +#: settings.py:73 +msgid "daily" +msgstr "" + +#: settings.py:74 +msgid "weekly" +msgstr "" + +#: settings.py:75 +msgid "monthly" +msgstr "" + +#: settings.py:76 +msgid "yearly" +msgstr "" + +#: settings.py:77 +msgid "never" +msgstr "" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "آخر مواضيع المدونة" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "ناشر مواضيع المدونة" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "علامات" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "الأقسام" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "الأرشيف" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "" + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "" diff --git a/djangocms_blog/locale/de/LC_MESSAGES/django.po b/djangocms_blog/locale/de/LC_MESSAGES/django.po new file mode 100644 index 00000000..7d54ebce --- /dev/null +++ b/djangocms_blog/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,764 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Christoph Reimers , 2014 +# Fabian Braun , 2016 +# Fabian Braun , 2016-2019,2022 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: Fabian Braun , 2016-2019,2022\n" +"Language-Team: German (http://app.transifex.com/nephila/djangocms-blog/language/de/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "Web-Site" + +#: admin.py:131 +msgid "Info" +msgstr "Informationen" + +#: admin.py:138 +msgid "Images" +msgstr "Beitragsbild" + +#: admin.py:141 +msgid "SEO" +msgstr "SEO" + +#: admin.py:172 +msgid "Publish selection" +msgstr "Auswahl veröffentlichen" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "Veröffentlichung für Auswahl aufheben" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "Kommentare für Auswahl aktivieren" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "Kommentare für Auswahl deaktivieren" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "Liveblog für Auswahl aktivieren" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "Liveblog für Auswahl deaktivieren" + +#: admin.py:473 +msgid "Generic" +msgstr "Generisch" + +#: admin.py:485 +msgid "Layout" +msgstr "Layout" + +#: admin.py:499 +msgid "Notifications" +msgstr "Benachrichtigungen" + +#: admin.py:503 +msgid "Sitemap" +msgstr "Sitemap" + +#: admin.py:512 +msgid "Meta" +msgstr "Meta-Informationen" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "Strings und Post-Attribute oder -Methoden erlaubt" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "django CMS Blog" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "Name der Anwendung" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "Objektbezeichnung" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "Einstellung" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "Einstellungen" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "unbenannt" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "Beitrag standardmäßig veröffentlichen" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "Standardgröße für Beitragsbilder" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "Wenn das Feld leer bleibt, muß die Größe für jedes Beitragsbild neu gesetzt werden." + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "Standardgröße für Vorschaubilder" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "Wenn das Feld leer bleibt, muß die Größe für jedes Vorschaubild neu gesetzt werden." + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Permalink-Struktur" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "Placeholder und Plugins für Beitrag verwenden" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "Feld Zusammenfassung nutzen" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "\"Verwandte Beiträge\" aktivieren" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Autor eintragen" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "Autor standardmäßig eintragen" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "Beiträge pro Seite" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "Anzahl der Beiträge, ab der die Beitragsübersicht in Seiten geteilt wird." + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "Vorlagen-Ordner" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "Alternativer Ordner, aus dem die Vorlagen für diesen Blog geladen werden" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "Menüstruktur" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "Aufbau des Django CMS-Menü" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "Leere Kategorien im Menü anzeigen" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "Auch Kategorien im Menü anzeigen, zu denen es keine Beiträge gibt" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "Sitemap-Anpassungen" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "Anpassungsfrequenz-Einstellung für Sitemap-Einträge" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "Sitemap-Priorität" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "Prioritäts-Einstellung für Sitemap-Einträge" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "Objekt-Typ" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "Facebook-Typ" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "Facebook Application ID" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "Facebook Profil-ID" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "Facebook Seiten-URL" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "Facebook Autoren-URL" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "Facebook Autor" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "Twitter-Typ" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "Twitter Site-Kennung" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "Twitter Autoren-Kennung" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "Schema.org-Typ" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "Schema.org Autorenname" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "Mitteilung bei Beitrags-Veröffentlichungen senden" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "Wenn aktiviert: Desktop-Mitteilung senden, wenn Beiträge veröffentlicht werden" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "Mitteilung bei Aktualisierungen senden" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "Wenn aktiviert: Desktop-Mitteilung senden, wenn Beiträge aktualisiert werden" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Blog" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "Blog-Menü" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Betragsübersicht anzeigen" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Beitrag hinzufügen" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "Einstellungen bearbeiten" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Beitrag bearbeiten" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "{0} jetzt veröffentlichen" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "Slug" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "Leer lassen für automatisch generierte Slug oder händisch eintragen" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "Neue(r) {0}" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "Neue(n) {0} in {1} erstellen" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "Blog-Beträge auf %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "Liveblog" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "Liveblog-Meldung" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "Liveblog-Meldung veröffentlichen" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "Veröffentlichungsdatum" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "Liveblog-Meldung" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "Liveblog-Meldungen" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "Titel" + +#: liveblog/models.py:95 +msgid "image" +msgstr "Beitragsbild" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "Vorschau-Größe" + +#: models.py:95 +msgid "parent" +msgstr "übergeordnetes Element" + +#: models.py:97 +msgid "created at" +msgstr "erstellt am" + +#: models.py:98 +msgid "modified at" +msgstr "bearbeitet am" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "App-Einstellung" + +#: models.py:102 +msgid "name" +msgstr "Name" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "slug" + +#: models.py:104 +msgid "category meta description" +msgstr "Meta-Beschreibung Kategorie" + +#: models.py:132 +msgid "blog category" +msgstr "Blog-Kategorie" + +#: models.py:133 +msgid "blog categories" +msgstr "Blog-Kategorien" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "Blog-Kategorie (ohne Übersetzung)" + +#: models.py:196 +msgid "author" +msgstr "Autor" + +#: models.py:203 +msgid "created" +msgstr "erstellt" + +#: models.py:204 +msgid "last modified" +msgstr "zuletzt geändert" + +#: models.py:205 +msgid "published since" +msgstr "öffentlich seit" + +#: models.py:206 +msgid "published until" +msgstr "öffentlich bis" + +#: models.py:207 +msgid "featured date" +msgstr "Vorstellungs-Datum" + +#: models.py:208 +msgid "publish" +msgstr "veröffentlicht" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "Kategorie" + +#: models.py:214 +msgid "main image" +msgstr "Beitragsbild" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "Beitragsbild (Vorschau)" + +#: models.py:230 +msgid "main image full" +msgstr "Beitragsbild (volle Größe)" + +#: models.py:237 +msgid "enable comments on post" +msgstr "Kommentare zu diesem Beitrag erlauben" + +#: models.py:241 +msgid "Site(s)" +msgstr "Web-Site(s)" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Wähle Web-Sites aus, auf welchen der Post angezeigt wird. Ohne Auswahl sichtbar auf allen konfigurierten Seiten." + +#: models.py:256 +msgid "subtitle" +msgstr "Untertitel" + +#: models.py:257 +msgid "abstract" +msgstr "Zusammenfassung" + +#: models.py:258 +msgid "post meta description" +msgstr "Meta-Beschreibung Beitrag" + +#: models.py:259 +msgid "post meta keywords" +msgstr "Meta-Stichwörter Beitrag" + +#: models.py:261 +msgid "post meta title" +msgstr "Meta-Titel Beitrag" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "Wird als Titel-Schlagwort und für das Teilen auf sozialen Netzwerken genutzt" + +#: models.py:267 +msgid "text" +msgstr "Text" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "Liveblog für Beitrag aktivieren" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "Schlagwort eintippen und Tabulator drücken oder Anfang eintippen und aus der Vorschlagsliste auswählen." + +#: models.py:282 +msgid "Related Posts" +msgstr "Verwandte Beiträge" + +#: models.py:314 +msgid "blog article" +msgstr "Blog-Beitrag" + +#: models.py:315 +msgid "blog articles" +msgstr "Blog-Beiträge" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "Beitrag (ohne Übersetzung)" + +#: models.py:493 +msgid "current site" +msgstr "Aktuelle Web-Site" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "Nur Einträge der aktuelle Web-Site zulassen" + +#: models.py:497 +msgid "Plugin template" +msgstr "Plugin-Vorlage" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "Wählen Sie die Vorlage für diese Instanz des Plugins" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "Beiträge" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "Anzahl der anzuzeigenden letzten Beiträge" + +#: models.py:541 +msgid "filter by tag" +msgstr "Nach Schlagwort filtern" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Nur Blog-Beiträge mit dem ausgewählten Schlagwort anzeigen" + +#: models.py:549 +msgid "filter by category" +msgstr "Nach Kategorie filtern" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Nur die Blog-Beiträge der ausgewählten Kategorie anzeigen" + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "%s neueste Beiträge nach Schlagwort" + +#: models.py:574 +msgid "authors" +msgstr "Autoren" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "Die Anzahl der anzuzeigenden Autoren-Beiträge" + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "%s neueste Beiträge nach Autor" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "Generisches Blog-Plugin" + +#: settings.py:26 +msgid "Full date" +msgstr "Jahr / Monat / Tag" + +#: settings.py:27 +msgid "Year / Month" +msgstr "Jahr / Monat" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Kategorie" + +#: settings.py:29 +msgid "Just slug" +msgstr "Nur Slug" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "Kategorien und Beiträge" + +#: settings.py:60 +msgid "Categories only" +msgstr "Nur Kategorien" + +#: settings.py:61 +msgid "Posts only" +msgstr "Nur Beiträge" + +#: settings.py:62 +msgid "None" +msgstr "Kein Menü" + +#: settings.py:71 +msgid "always" +msgstr "Fortlaufend" + +#: settings.py:72 +msgid "hourly" +msgstr "Stündlich" + +#: settings.py:73 +msgid "daily" +msgstr "Täglich" + +#: settings.py:74 +msgid "weekly" +msgstr "Wöchentlich" + +#: settings.py:75 +msgid "monthly" +msgstr "Monatlich" + +#: settings.py:76 +msgid "yearly" +msgstr "Jährlich" + +#: settings.py:77 +msgid "never" +msgstr "Nie" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Letzte Blog-Beiträge" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "Letzte Blog-Einträge - Cache" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Blog-Beiträge nach Autor" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "Blog-Beitrags-Liste nach Autor" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Schlagworte" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Kategorien" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Archiv" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "Standard-Vorlage" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "weiterlesen" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "von" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "Ein Artikel" +msgstr[1] "%(articles)s Beiträge" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 Beiträge" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Keinen Beitrag gefunden." + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Autoren" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "Kein Autor gefunden." + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Artikel von" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "Keine Kategorien gefunden." + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Schlagwort" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Zurück" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "vorheriger" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Seite" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "von" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "nächste" diff --git a/djangocms_blog/locale/en/LC_MESSAGES/django.po b/djangocms_blog/locale/en/LC_MESSAGES/django.po new file mode 100644 index 00000000..e7eeb9f4 --- /dev/null +++ b/djangocms_blog/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,761 @@ +# 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: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-05 18:09+0100\n" +"Last-Translator: Iacopo Spalletti\n" +"Language-Team: Italian \n" +"Language: it\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" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "" + +#: admin.py:131 +msgid "Info" +msgstr "" + +#: admin.py:138 +msgid "Images" +msgstr "" + +#: admin.py:141 +msgid "SEO" +msgstr "" + +#: admin.py:172 +msgid "Publish selection" +msgstr "" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "" + +#: admin.py:473 +msgid "Generic" +msgstr "" + +#: admin.py:485 +msgid "Layout" +msgstr "" + +#: admin.py:499 +msgid "Notifications" +msgstr "" + +#: admin.py:503 +msgid "Sitemap" +msgstr "" + +#: admin.py:512 +msgid "Meta" +msgstr "" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "" + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "" + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "" + +#: cms_appconfig.py:207 +msgid "Emits a desktop notification -if enabled- when editing a published post" +msgstr "" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "" + +#: liveblog/models.py:95 +msgid "image" +msgstr "" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "" + +#: models.py:95 +msgid "parent" +msgstr "" + +#: models.py:97 +msgid "created at" +msgstr "" + +#: models.py:98 +msgid "modified at" +msgstr "" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "" + +#: models.py:102 +msgid "name" +msgstr "" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "" + +#: models.py:104 +msgid "category meta description" +msgstr "" + +#: models.py:132 +msgid "blog category" +msgstr "" + +#: models.py:133 +msgid "blog categories" +msgstr "" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "" + +#: models.py:196 +msgid "author" +msgstr "" + +#: models.py:203 +msgid "created" +msgstr "" + +#: models.py:204 +msgid "last modified" +msgstr "" + +#: models.py:205 +msgid "published since" +msgstr "" + +#: models.py:206 +msgid "published until" +msgstr "" + +#: models.py:207 +msgid "featured date" +msgstr "" + +#: models.py:208 +msgid "publish" +msgstr "" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "" + +#: models.py:214 +msgid "main image" +msgstr "" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "" + +#: models.py:230 +msgid "main image full" +msgstr "" + +#: models.py:237 +msgid "enable comments on post" +msgstr "" + +#: models.py:241 +msgid "Site(s)" +msgstr "" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in " +"all the configured sites." +msgstr "" + +#: models.py:256 +msgid "subtitle" +msgstr "" + +#: models.py:257 +msgid "abstract" +msgstr "" + +#: models.py:258 +msgid "post meta description" +msgstr "" + +#: models.py:259 +msgid "post meta keywords" +msgstr "" + +#: models.py:261 +msgid "post meta title" +msgstr "" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "" + +#: models.py:267 +msgid "text" +msgstr "" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "" + +#: models.py:314 +msgid "blog article" +msgstr "" + +#: models.py:315 +msgid "blog articles" +msgstr "" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "" + +#: models.py:493 +msgid "current site" +msgstr "" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "" + +#: models.py:497 +msgid "Plugin template" +msgstr "" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "" + +#: models.py:541 +msgid "filter by tag" +msgstr "" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "" + +#: models.py:549 +msgid "filter by category" +msgstr "" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "" + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "" + +#: models.py:574 +msgid "authors" +msgstr "" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "" + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "" + +#: settings.py:26 +msgid "Full date" +msgstr "" + +#: settings.py:27 +msgid "Year / Month" +msgstr "" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "" + +#: settings.py:29 +msgid "Just slug" +msgstr "" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "" + +#: settings.py:60 +msgid "Categories only" +msgstr "" + +#: settings.py:61 +msgid "Posts only" +msgstr "" + +#: settings.py:62 +msgid "None" +msgstr "" + +#: settings.py:71 +msgid "always" +msgstr "" + +#: settings.py:72 +msgid "hourly" +msgstr "" + +#: settings.py:73 +msgid "daily" +msgstr "" + +#: settings.py:74 +msgid "weekly" +msgstr "" + +#: settings.py:75 +msgid "monthly" +msgstr "" + +#: settings.py:76 +msgid "yearly" +msgstr "" + +#: settings.py:77 +msgid "never" +msgstr "" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "" +msgstr[1] "" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "" + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "" diff --git a/djangocms_blog/locale/es/LC_MESSAGES/django.po b/djangocms_blog/locale/es/LC_MESSAGES/django.po new file mode 100644 index 00000000..c4e7a21a --- /dev/null +++ b/djangocms_blog/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,763 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# David , 2015 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: David , 2015\n" +"Language-Team: Spanish (http://app.transifex.com/nephila/djangocms-blog/language/es/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: es\n" +"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "" + +#: admin.py:131 +msgid "Info" +msgstr "" + +#: admin.py:138 +msgid "Images" +msgstr "" + +#: admin.py:141 +msgid "SEO" +msgstr "" + +#: admin.py:172 +msgid "Publish selection" +msgstr "" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "" + +#: admin.py:473 +msgid "Generic" +msgstr "" + +#: admin.py:485 +msgid "Layout" +msgstr "" + +#: admin.py:499 +msgid "Notifications" +msgstr "" + +#: admin.py:503 +msgid "Sitemap" +msgstr "" + +#: admin.py:512 +msgid "Meta" +msgstr "" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "" + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "" + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Blog" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Lista de entradas" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Añadir entradas" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Editar entrada" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "Artículos del blog en %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "" + +#: liveblog/models.py:95 +msgid "image" +msgstr "" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "" + +#: models.py:95 +msgid "parent" +msgstr "padre" + +#: models.py:97 +msgid "created at" +msgstr "Creado en" + +#: models.py:98 +msgid "modified at" +msgstr "Actualizado en" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "" + +#: models.py:102 +msgid "name" +msgstr "nombre" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "slug" + +#: models.py:104 +msgid "category meta description" +msgstr "" + +#: models.py:132 +msgid "blog category" +msgstr "categoría del blog" + +#: models.py:133 +msgid "blog categories" +msgstr "categorías del blog" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "" + +#: models.py:196 +msgid "author" +msgstr "" + +#: models.py:203 +msgid "created" +msgstr "" + +#: models.py:204 +msgid "last modified" +msgstr "" + +#: models.py:205 +msgid "published since" +msgstr "" + +#: models.py:206 +msgid "published until" +msgstr "" + +#: models.py:207 +msgid "featured date" +msgstr "" + +#: models.py:208 +msgid "publish" +msgstr "" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "categoría" + +#: models.py:214 +msgid "main image" +msgstr "" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "" + +#: models.py:230 +msgid "main image full" +msgstr "" + +#: models.py:237 +msgid "enable comments on post" +msgstr "" + +#: models.py:241 +msgid "Site(s)" +msgstr "Sitio(s)" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Selecciona los sitios en los que mostrar la entrada. Si no se selecciona ninguno, será visible en todos los sitios configurados." + +#: models.py:256 +msgid "subtitle" +msgstr "" + +#: models.py:257 +msgid "abstract" +msgstr "" + +#: models.py:258 +msgid "post meta description" +msgstr "" + +#: models.py:259 +msgid "post meta keywords" +msgstr "" + +#: models.py:261 +msgid "post meta title" +msgstr "" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "usado en la etiqueta de título y al compartir en las redes sociales" + +#: models.py:267 +msgid "text" +msgstr "" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "" + +#: models.py:314 +msgid "blog article" +msgstr "artículo del blog" + +#: models.py:315 +msgid "blog articles" +msgstr "artículos del blog" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "" + +#: models.py:493 +msgid "current site" +msgstr "" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "" + +#: models.py:497 +msgid "Plugin template" +msgstr "" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "El número de últimos artículos a mostrar." + +#: models.py:541 +msgid "filter by tag" +msgstr "" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Mostrar solo los artículos del blog etiquetados con las etiquetas elegidas." + +#: models.py:549 +msgid "filter by category" +msgstr "" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Mostrar solo los artículos del blog etiquetados con las categorías elegidas." + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "" + +#: models.py:574 +msgid "authors" +msgstr "" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "El número de autores de artículo a mostrar." + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "" + +#: settings.py:26 +msgid "Full date" +msgstr "" + +#: settings.py:27 +msgid "Year / Month" +msgstr "" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Categoría" + +#: settings.py:29 +msgid "Just slug" +msgstr "" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "" + +#: settings.py:60 +msgid "Categories only" +msgstr "" + +#: settings.py:61 +msgid "Posts only" +msgstr "" + +#: settings.py:62 +msgid "None" +msgstr "" + +#: settings.py:71 +msgid "always" +msgstr "" + +#: settings.py:72 +msgid "hourly" +msgstr "" + +#: settings.py:73 +msgid "daily" +msgstr "" + +#: settings.py:74 +msgid "weekly" +msgstr "" + +#: settings.py:75 +msgid "monthly" +msgstr "" + +#: settings.py:76 +msgid "yearly" +msgstr "" + +#: settings.py:77 +msgid "never" +msgstr "" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Últimos artículos del blog" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Artículos del blog por author" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Etiquetas" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Categorías" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Archivo" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "leer más" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "por" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "1 artículo" +msgstr[1] "%(articles)s artículos" +msgstr[2] "%(articles)s artículos" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 artículos" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Ningún artículo encontrado." + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Autores" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Artículos por" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "" + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Etiqueta" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Atrás" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "Anterior" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Página" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "de" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "siguiente" diff --git a/djangocms_blog/locale/et/LC_MESSAGES/django.po b/djangocms_blog/locale/et/LC_MESSAGES/django.po new file mode 100644 index 00000000..c6e57041 --- /dev/null +++ b/djangocms_blog/locale/et/LC_MESSAGES/django.po @@ -0,0 +1,762 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Rivo Zängov , 2018 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: Rivo Zängov , 2018\n" +"Language-Team: Estonian (http://app.transifex.com/nephila/djangocms-blog/language/et/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: et\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "sait" + +#: admin.py:131 +msgid "Info" +msgstr "Info" + +#: admin.py:138 +msgid "Images" +msgstr "Pildid" + +#: admin.py:141 +msgid "SEO" +msgstr "SEO" + +#: admin.py:172 +msgid "Publish selection" +msgstr "Avalda valik" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "Peida valik" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "" + +#: admin.py:473 +msgid "Generic" +msgstr "Üldine" + +#: admin.py:485 +msgid "Layout" +msgstr "Paigutus" + +#: admin.py:499 +msgid "Notifications" +msgstr "Teavitused" + +#: admin.py:503 +msgid "Sitemap" +msgstr "Sisukaart" + +#: admin.py:512 +msgid "Meta" +msgstr "META" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "rakenduse nimi" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "objekti nimi" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "blogi seaded" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "blogi seaded" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "pealkirjata" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "" + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "" + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Püsilingi struktuur" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Määra autor" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "Menüü struktuur" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "Objekti liik" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "Facebooki liik" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "Facebooki rakenduse ID" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "Facebooki profiili ID" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "Facebook lehekülje URL" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "Facebooki autori URL" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "Facebooki autor" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "Twitteri liik" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "Blogi menüü" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Postituste nimekiri" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Lisa postitus" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "Muuda seadistust" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Muuda postitust" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "Uus {0}" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "postituse kuupäev" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "pealkiri" + +#: liveblog/models.py:95 +msgid "image" +msgstr "pilt" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "pispildi suurus" + +#: models.py:95 +msgid "parent" +msgstr "peamine" + +#: models.py:97 +msgid "created at" +msgstr "loodud" + +#: models.py:98 +msgid "modified at" +msgstr "muudetud" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "rakenduse seaded" + +#: models.py:102 +msgid "name" +msgstr "nimi" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "" + +#: models.py:104 +msgid "category meta description" +msgstr "kategooria META kirjeldus" + +#: models.py:132 +msgid "blog category" +msgstr "blogi kategooria" + +#: models.py:133 +msgid "blog categories" +msgstr "blogi kategooriad" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "" + +#: models.py:196 +msgid "author" +msgstr "tor" + +#: models.py:203 +msgid "created" +msgstr "loodud" + +#: models.py:204 +msgid "last modified" +msgstr "viimati muudetud" + +#: models.py:205 +msgid "published since" +msgstr "avaldatud alates" + +#: models.py:206 +msgid "published until" +msgstr "avaldatud kuni" + +#: models.py:207 +msgid "featured date" +msgstr "esiletõstmise kuupäev" + +#: models.py:208 +msgid "publish" +msgstr "avalda" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "kategooria" + +#: models.py:214 +msgid "main image" +msgstr "peamine pilt" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "peamise pildi pisipilt" + +#: models.py:230 +msgid "main image full" +msgstr "peamine pilt täissuuruses" + +#: models.py:237 +msgid "enable comments on post" +msgstr "" + +#: models.py:241 +msgid "Site(s)" +msgstr "Sait (Saidid)" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "" + +#: models.py:256 +msgid "subtitle" +msgstr "" + +#: models.py:257 +msgid "abstract" +msgstr "abstraktne" + +#: models.py:258 +msgid "post meta description" +msgstr "" + +#: models.py:259 +msgid "post meta keywords" +msgstr "" + +#: models.py:261 +msgid "post meta title" +msgstr "" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "" + +#: models.py:267 +msgid "text" +msgstr "tekst" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "Seotud postitused" + +#: models.py:314 +msgid "blog article" +msgstr "blogi artikkel" + +#: models.py:315 +msgid "blog articles" +msgstr "blogi artiklid" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "" + +#: models.py:493 +msgid "current site" +msgstr "praegune sait" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "" + +#: models.py:497 +msgid "Plugin template" +msgstr "Plugina mall" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "artiklit" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "" + +#: models.py:541 +msgid "filter by tag" +msgstr "filtreeri sildi järgi" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "" + +#: models.py:549 +msgid "filter by category" +msgstr "filtreeri kategooria järgi" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "" + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "" + +#: models.py:574 +msgid "authors" +msgstr "autorid" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "" + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "üldine blogi plugin" + +#: settings.py:26 +msgid "Full date" +msgstr "Täispikk kuupäev" + +#: settings.py:27 +msgid "Year / Month" +msgstr "Aasta / Kuu" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Kategooria" + +#: settings.py:29 +msgid "Just slug" +msgstr "" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "Kategooriad ja postitused" + +#: settings.py:60 +msgid "Categories only" +msgstr "Ainult kategooriad" + +#: settings.py:61 +msgid "Posts only" +msgstr "Ainult postitused" + +#: settings.py:62 +msgid "None" +msgstr "Mitte midagi" + +#: settings.py:71 +msgid "always" +msgstr "alati" + +#: settings.py:72 +msgid "hourly" +msgstr "iga tund" + +#: settings.py:73 +msgid "daily" +msgstr "iga päev" + +#: settings.py:74 +msgid "weekly" +msgstr "iga nädal" + +#: settings.py:75 +msgid "monthly" +msgstr "iga kuu" + +#: settings.py:76 +msgid "yearly" +msgstr "iga aasta" + +#: settings.py:77 +msgid "never" +msgstr "mitte kunagi" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Viimased blogi artiklid" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "Viimased blogi artiklid - Vahemälust" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Autori blogi artiklid" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Sildid" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Kategooriad" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Arhiiv" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "Vaikimisi kujundus" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "loe lisa" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "autor" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "1 artikkel" +msgstr[1] "%(articles)s artiklit" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 artiklit" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Ühtegi artiklit ei leitud" + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Autorid" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Artiklid autorilt" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "Ühtegi kategooriat ei leitud" + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Silt" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Tagasi" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "eelmine" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Lehekülg" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "/" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "järgmine" diff --git a/djangocms_blog/locale/fr/LC_MESSAGES/django.po b/djangocms_blog/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 00000000..335279b6 --- /dev/null +++ b/djangocms_blog/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,767 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Alban Tiberghien , 2017 +# Corentin Bettiol, 2023 +# Iacopo Spalletti, 2014 +# yakky , 2016 +# Robin Durner, 2022 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: Corentin Bettiol, 2023\n" +"Language-Team: French (http://app.transifex.com/nephila/djangocms-blog/language/fr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fr\n" +"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "site" + +#: admin.py:131 +msgid "Info" +msgstr "Info" + +#: admin.py:138 +msgid "Images" +msgstr "Images" + +#: admin.py:141 +msgid "SEO" +msgstr "SEO" + +#: admin.py:172 +msgid "Publish selection" +msgstr "Publier les articles sélectionnés" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "Dépublier les articles sélectionnés" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "Activer les commentaires sur les articles sélectionnés" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "Désactiver les commentaires sur les articles sélectionnés" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "Activer le liveblog sur les articles sélectionnés" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "Désactiver le liveblog sur les articles sélectionnés" + +#: admin.py:473 +msgid "Generic" +msgstr "Générique" + +#: admin.py:485 +msgid "Layout" +msgstr "Mise en page" + +#: admin.py:499 +msgid "Notifications" +msgstr "Notifications" + +#: admin.py:503 +msgid "Sitemap" +msgstr "Sitemap" + +#: admin.py:512 +msgid "Meta" +msgstr "Meta" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "Vous pouvez préciser des chaines de caractères, des attributs ou des methodes du modèle Post" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "django CMS Blog" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "Titre de l’application" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "Nom de l’objet" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "Configuration du blog" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "Configurations du blog" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "sans titre" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "Post publié par défaut" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "Taille par défaut des images principales" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "Si laissé vide, la taille de l'image doit être précisée pour chaque nouvel article." + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "Taille par défaut des images miniatures" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "Si laissé vide, la taille de l'image miniature doit être précisée pour chaque nouvel article." + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Structure permalien" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "Utiliser une balise et des plugins pour le corps de l’article" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "Utiliser un champ abrégé" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "Activer articles similaires" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Auteur" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "Spécifier l’auteur par défaut" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "Nombre d’articles par page" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "Lors de la pagination des listes, combien d’articles souhaitez vous afficher par page ?" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "Prefixe dans le template" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "Répertoire alternatif à partir duquel charger les templates du blog" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "Structure du menu" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "Structure du menu django CMS" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "Afficher des catégories vides dans le menu" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "Afficher les catégories sans articles liés dans le menu" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "Fréquence de mise à jour de la Sitemap" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "Attribut ‘Changfreq ‘ pour chaque entrée de la sitemap." + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "Priorité de la sitemap" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "Priorité de chaque entrée de la sitemap" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "Type de l’objet" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "Facebook type" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "Facebook application ID" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "Facebook profile ID" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "Facebook page URL" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "Facebook author URL" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "Facebook author" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "Twitter type" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "Twitter site handle" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "Twitter author handle" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "Schema.org type" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "Schema.org nom d'auteur" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "Envoyer des notifications lors de la publication d'un article" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "Emets une notification sur le bureau, si activée, lors de la publication d'un nouvel article" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "Envoyer des notifications lors de la mise à jour d'un article" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "Emets une notification sur le bureau, si activée, lors de la édition d'un article publié" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Blog" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "Menu du blog" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Liste des articles" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "Liste des catégories" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "Liste des tags" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Ajouter un article" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "Modifier la configuration" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Editer un article" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "Publier {0} maintenant" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "Slug (pour l'URL)" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "Laissez vide pour générer un slug automatiquement, ou remplacez-le si nécessaire." + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "Nouveau {0}" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "Ajouter un nouveau {0} dans {1}" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "Articles de blog sur le site %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "Liveblog" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "Élément liveblog" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "Publier l'éntrée liveblog" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "Date de publication" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "Entrée liveblog" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "Entrées liveblog" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "Titre" + +#: liveblog/models.py:95 +msgid "image" +msgstr "image" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "taille de l'image miniature" + +#: models.py:95 +msgid "parent" +msgstr "père" + +#: models.py:97 +msgid "created at" +msgstr "crée le" + +#: models.py:98 +msgid "modified at" +msgstr "modifié le" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "configuration de l'application" + +#: models.py:102 +msgid "name" +msgstr "nom" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "slug" + +#: models.py:104 +msgid "category meta description" +msgstr "déscription de la catégorie" + +#: models.py:132 +msgid "blog category" +msgstr "catégorie du blog" + +#: models.py:133 +msgid "blog categories" +msgstr "catégories du blog" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "Catégorie du blog (sans traduction)" + +#: models.py:196 +msgid "author" +msgstr "auteur" + +#: models.py:203 +msgid "created" +msgstr "crée le" + +#: models.py:204 +msgid "last modified" +msgstr "modifié le" + +#: models.py:205 +msgid "published since" +msgstr "publié depuis" + +#: models.py:206 +msgid "published until" +msgstr "publié jusqu’à" + +#: models.py:207 +msgid "featured date" +msgstr "date présentée" + +#: models.py:208 +msgid "publish" +msgstr "publier" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "inclus dans le flux RSS" + +#: models.py:211 +msgid "category" +msgstr "catégorie" + +#: models.py:214 +msgid "main image" +msgstr "image principale" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "miniature image principale" + +#: models.py:230 +msgid "main image full" +msgstr "image principale taille réelle" + +#: models.py:237 +msgid "enable comments on post" +msgstr "activer les commentaires sur l’article" + +#: models.py:241 +msgid "Site(s)" +msgstr "Site(s)" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Sélectionnez les sites dans lesquels afficher l’article. Si aucun site n’est sélectionné, l’article sera visible dans tous les sites." + +#: models.py:256 +msgid "subtitle" +msgstr "sous-titre" + +#: models.py:257 +msgid "abstract" +msgstr "résumé" + +#: models.py:258 +msgid "post meta description" +msgstr "description ‘meta’ de l’article" + +#: models.py:259 +msgid "post meta keywords" +msgstr "mots clés ‘meta’ de l’article" + +#: models.py:261 +msgid "post meta title" +msgstr "titre ‘meta’ de l’article" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "visible dans le titre et dans le partage sur les réseaux sociaux" + +#: models.py:267 +msgid "text" +msgstr "texte" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "activer le liveblog sur l'article" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "Tapez un tag et appuyez sur la touche Tab, ou commencez à taper et sélectionnez de la liste d'auto-complétion." + +#: models.py:282 +msgid "Related Posts" +msgstr "Articles similaires" + +#: models.py:314 +msgid "blog article" +msgstr "article du blog" + +#: models.py:315 +msgid "blog articles" +msgstr "articles du blog" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "Article (sans traduction)" + +#: models.py:493 +msgid "current site" +msgstr "Site actuel" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "Sélectionner les éléments à partir du site actuel seulement" + +#: models.py:497 +msgid "Plugin template" +msgstr "Template de plugin" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "Sélectionner un template de plugin pour cette instance" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "articles" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "Nombre d’articles récents à afficher." + +#: models.py:541 +msgid "filter by tag" +msgstr "filtrer par tag" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Afficher seulement les articles qui ont les tags séléctionnés." + +#: models.py:549 +msgid "filter by category" +msgstr "filtrer par catégorie" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Afficher seulement les articles des catégories séléctionnées" + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "%s derniers articles par tag" + +#: models.py:574 +msgid "authors" +msgstr "Auteurs" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "Le nombre d’articles de l’auteur à afficher" + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "%s derniers articles par auteur" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "Posts mis en avant" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "plugin générique pour le blog" + +#: settings.py:26 +msgid "Full date" +msgstr "Date complète" + +#: settings.py:27 +msgid "Year / Month" +msgstr "Année / Mois" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Catégorie" + +#: settings.py:29 +msgid "Just slug" +msgstr "Uniquement le slug" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "Catégories et articles" + +#: settings.py:60 +msgid "Categories only" +msgstr "Catégories seulement" + +#: settings.py:61 +msgid "Posts only" +msgstr "Articles seulement" + +#: settings.py:62 +msgid "None" +msgstr "Aucun" + +#: settings.py:71 +msgid "always" +msgstr "toujours" + +#: settings.py:72 +msgid "hourly" +msgstr "toutes les heures" + +#: settings.py:73 +msgid "daily" +msgstr "quotidien" + +#: settings.py:74 +msgid "weekly" +msgstr "hebdomadaire" + +#: settings.py:75 +msgid "monthly" +msgstr "mensuel" + +#: settings.py:76 +msgid "yearly" +msgstr "annuel" + +#: settings.py:77 +msgid "never" +msgstr "jamais" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Articles récents du blog" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "Articles récents du blog - Cache" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Articles de l’auteur" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "Liste d'articles de l’auteur" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Tags" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Catégories" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Archive" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "Articles mis en avant" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr " Articles mis en avant - Cache" + +#: settings.py:590 +msgid "Default template" +msgstr "Template par défaut" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "lire la suite" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "par" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "1 article" +msgstr[1] "%(articles)s articles" +msgstr[2] "%(articles)s articles" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 articles" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Aucun article trouvé" + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Auteurs" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "Aucun auteur trouvé." + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Articles écrits par" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "Aucune catégorie trouvée" + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Tag" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Retour" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "précédent" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Page" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "sur" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "suivant" diff --git a/djangocms_blog/locale/it/LC_MESSAGES/django.po b/djangocms_blog/locale/it/LC_MESSAGES/django.po new file mode 100644 index 00000000..28f3e9c1 --- /dev/null +++ b/djangocms_blog/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,765 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Iacopo Spalletti, 2014 +# yakky , 2014-2020 +# Leonardo Cavallucci, 2023 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: Leonardo Cavallucci, 2023\n" +"Language-Team: Italian (http://app.transifex.com/nephila/djangocms-blog/language/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "Sito" + +#: admin.py:131 +msgid "Info" +msgstr "Info" + +#: admin.py:138 +msgid "Images" +msgstr "Immagini" + +#: admin.py:141 +msgid "SEO" +msgstr "SEO" + +#: admin.py:172 +msgid "Publish selection" +msgstr "Pubblica gli articoli selezionati" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "Spubblica gli articoli selezionati" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "Abilita i commenti per gli articoli selezionati" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "Disabilita i commenti per gli articoli selezionati" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "Abilita il liveblog per gli articoli selezionati" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "Disabilita il liveblog per gli articoli selezionati" + +#: admin.py:473 +msgid "Generic" +msgstr "Generale" + +#: admin.py:485 +msgid "Layout" +msgstr "Layout" + +#: admin.py:499 +msgid "Notifications" +msgstr "Notifiche" + +#: admin.py:503 +msgid "Sitemap" +msgstr "Sitemap" + +#: admin.py:512 +msgid "Meta" +msgstr "Meta" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "Puoi inserire stringhe, attributi o metodi del model Post" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "django CMS Blog" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "Titolo applicazione" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "Nome oggetto" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "Configurazione del blog" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "Configurazioni del blog" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "Senza titolo" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "Articoli pubblicati di default" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "Dimensione standard immagine principale" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "Se vuoto, la dimensione dell'immagine principale dovrà essere impostata per ogni nuovo articolo." + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "Dimensione standard immagine ridotta" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "Se vuoto, la dimensione dell'immagine ridotta dovrà essere impostata per ogni nuovo articolo." + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Struttura permalink" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "Usa placeholder e plugin per il contenuto" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "Usa campo abstract" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "Abilita articoli collegati" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Imposta autore" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "Imposta autore di default" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "Dimensione paginazione" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "Se la lista è paginata, quanti articoli visualizzati per pagina?" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "Prefisso template" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "Directory alternativa da cui caricare i template" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "Struttura del menu" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "Struttura del menu django CMS" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "Mostra categorie vuote nel menù" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "Mostra categorie senza post nel menù" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "Frequenza aggiornamento Sitemap" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "Attributo Changefreq per gli oggetti della sitemap" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "Priorità sitemap" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "Attributo priorità per gli oggetti nella sitemap" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "Tipo oggetto" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "Tipo oggetto Facebook" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "ID Applicazione Facebook" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "ID Profilo Facebook" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "URL Pagina Facebook" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "URL Autore Facebook" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "Autore Facebook" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "Tipo oggetto Twitter" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "Nome Twitter del sito" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "Nome Twitter autore" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "Tipo Schema.org" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "Nome autore Schema.org" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "Invia notifiche alla pubblicazione dell'articolo" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "Invia notifiche desktop -se abilitate- quando viene pubblicato un articolo" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "Invia notifiche all'aggiornamento dell'articolo" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "Invia notifiche desktop -se abilitate- quando viene aggiornato un articolo" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Blog" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "Menu del blog" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Lista degli articoli" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "Lista categorie" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "Lista tag" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Aggiungi articolo" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "Modifica configurazione" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Modifica articolo" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "Pubblica {0}" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "Slug" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "Lascia vuoto per generare lo slug automaticamente" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "Nuovo {0}" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "Crea un nuovo {0} in {1}" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "Articoli del blog su %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "Liveblog" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "Elemento liveblog" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "Pubblica elemento liveblog" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "Data del post" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "Elemento liveblog" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "Elementi liveblog" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "titolo" + +#: liveblog/models.py:95 +msgid "image" +msgstr "Immagine" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "Dimensione miniatura" + +#: models.py:95 +msgid "parent" +msgstr "superiore" + +#: models.py:97 +msgid "created at" +msgstr "creato il" + +#: models.py:98 +msgid "modified at" +msgstr "modificato il" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "config. app." + +#: models.py:102 +msgid "name" +msgstr "nome" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "slug" + +#: models.py:104 +msgid "category meta description" +msgstr "metatag descrizione" + +#: models.py:132 +msgid "blog category" +msgstr "categoria del blog" + +#: models.py:133 +msgid "blog categories" +msgstr "categorie del blog" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "Categoria (nessuna traduzione)" + +#: models.py:196 +msgid "author" +msgstr "autore" + +#: models.py:203 +msgid "created" +msgstr "creato" + +#: models.py:204 +msgid "last modified" +msgstr "ultima modifica" + +#: models.py:205 +msgid "published since" +msgstr "Pubblicato il" + +#: models.py:206 +msgid "published until" +msgstr "fino al" + +#: models.py:207 +msgid "featured date" +msgstr "Data visualizzata" + +#: models.py:208 +msgid "publish" +msgstr "pubblica" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "includi nel feed RSS" + +#: models.py:211 +msgid "category" +msgstr "categoria" + +#: models.py:214 +msgid "main image" +msgstr "immagine principale" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "dimensioni miniatura immagine" + +#: models.py:230 +msgid "main image full" +msgstr "dimensioni immagine" + +#: models.py:237 +msgid "enable comments on post" +msgstr "abilita commenti sull'articolo" + +#: models.py:241 +msgid "Site(s)" +msgstr "Sito/i" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Seleziona i siti in cui deve apparire l'articolo. Se non è selezionato nessun sito, l'articolo comparirà in tutti i siti configurati." + +#: models.py:256 +msgid "subtitle" +msgstr "sottotitolo" + +#: models.py:257 +msgid "abstract" +msgstr "abstract" + +#: models.py:258 +msgid "post meta description" +msgstr "metatag descrizione" + +#: models.py:259 +msgid "post meta keywords" +msgstr "metatag keyword" + +#: models.py:261 +msgid "post meta title" +msgstr "metatag titolo" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "Usato nel tag title e nei titoli per le condivisioni sui social network" + +#: models.py:267 +msgid "text" +msgstr "testo" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "Attiva liveblog sul post" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "Scrivi un tag e premi il task tab, o comincia a scrivere e seleziona il tag dalla lista che comparirà" + +#: models.py:282 +msgid "Related Posts" +msgstr "Articoli collegati" + +#: models.py:314 +msgid "blog article" +msgstr "articolo del blog" + +#: models.py:315 +msgid "blog articles" +msgstr "articoli del blog" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "Articolo (nessuna translation)" + +#: models.py:493 +msgid "current site" +msgstr "Sito corrente" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "Seleziona gli elementi per il solo sito corrente" + +#: models.py:497 +msgid "Plugin template" +msgstr "Template del plugin" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "Seleziona il template da usare per questa istanza" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "articoli" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "Il numero di articoli da mostrare." + +#: models.py:541 +msgid "filter by tag" +msgstr "filtra per tag" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Mostra solo gli articoli marcati con i tag selezionati." + +#: models.py:549 +msgid "filter by category" +msgstr "filtra per categoria" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Mostra solo gli articoli marcati con i categorie selezionati." + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "ultimi %s articoli per tag" + +#: models.py:574 +msgid "authors" +msgstr "autori" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "Numero di elementi per autore da mostrare." + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "ultimi %s articoli per autore" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "Articoli in evidenza" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "plugin blog" + +#: settings.py:26 +msgid "Full date" +msgstr "Data completa" + +#: settings.py:27 +msgid "Year / Month" +msgstr "Anno / Mese" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Categoria" + +#: settings.py:29 +msgid "Just slug" +msgstr "Solo slug" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "Categorie e articoli" + +#: settings.py:60 +msgid "Categories only" +msgstr "Solo categorie" + +#: settings.py:61 +msgid "Posts only" +msgstr "Solo articoli" + +#: settings.py:62 +msgid "None" +msgstr "Niente" + +#: settings.py:71 +msgid "always" +msgstr "sempre" + +#: settings.py:72 +msgid "hourly" +msgstr "ogni ora" + +#: settings.py:73 +msgid "daily" +msgstr "ogni giorno" + +#: settings.py:74 +msgid "weekly" +msgstr "ogni settimana" + +#: settings.py:75 +msgid "monthly" +msgstr "ogni mese" + +#: settings.py:76 +msgid "yearly" +msgstr "ogni anno" + +#: settings.py:77 +msgid "never" +msgstr "mai" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Ultimi articoli" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "Ultimi articoli del blog - in cache" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Articoli per autore" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "Lista autori articoli" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Tag" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Categorie" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Archivio" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "Articoli in evidenza" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "Articoli in evidenza - Cache" + +#: settings.py:590 +msgid "Default template" +msgstr "Template standard" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "leggi" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "di" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "1 articolo" +msgstr[1] "%(articles)s articoli" +msgstr[2] "%(articles)s articoli" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 articoli" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Nessun articolo trovato." + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Autori" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "Nessun autore trovato" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Articoli di" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "Nessuna categoria trovata." + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Tag" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Indietro" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "precedente" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Pagina" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "di" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "successivo" diff --git a/djangocms_blog/locale/lt/LC_MESSAGES/django.po b/djangocms_blog/locale/lt/LC_MESSAGES/django.po new file mode 100644 index 00000000..cabc9694 --- /dev/null +++ b/djangocms_blog/locale/lt/LC_MESSAGES/django.po @@ -0,0 +1,764 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Matas Dailyda , 2015-2018 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: Matas Dailyda , 2015-2018\n" +"Language-Team: Lithuanian (http://app.transifex.com/nephila/djangocms-blog/language/lt/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: lt\n" +"Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "tinklalapis" + +#: admin.py:131 +msgid "Info" +msgstr "Informacija" + +#: admin.py:138 +msgid "Images" +msgstr "Paveikslėliai" + +#: admin.py:141 +msgid "SEO" +msgstr "SEO" + +#: admin.py:172 +msgid "Publish selection" +msgstr "Publikuoti pasirinktą dalį" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "Nebepublikuoti pasirinktos dalies" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "Įgalinti komentarus pasirinktai daliai" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "Išjungti komentarus pasirinktai daliai" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "Įgalinti gyvą tinklaraštį pasirinktai daliai" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "Išjungti gyvą tinklaraštį pasirinktai daliai" + +#: admin.py:473 +msgid "Generic" +msgstr "Bendras" + +#: admin.py:485 +msgid "Layout" +msgstr "Išdėstymas" + +#: admin.py:499 +msgid "Notifications" +msgstr "Pranešimai" + +#: admin.py:503 +msgid "Sitemap" +msgstr "Tinklalapio žemėlapis" + +#: admin.py:512 +msgid "Meta" +msgstr "Meta" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "Galite pateikti paprasto teksto eilutes, įrašo modelio atributą arba metodo pavadinimus " + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "django CMS Tinklaraštis" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "aplikacijos pavadinimas" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "objekto pavadinimas" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "tinklaraščio konfiguracija" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "tinklaraščio konfiguracijos" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "be pavadinimo" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "Pagal numatymą įrašas publikuojamas" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "Numatytas pilnų paveikslėlių dydis" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "Jei palikta tuščia, paveiksliuko dydis turės būt pasirinktas kiekvienam naujai kuriamam straipsniui." + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "Numatytas piktogramų dydis" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "Jei palikta tuščia, piktogramos dydis turės būt pasirinktas kiekvienam naujai kuriamam straipsniui." + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Nuolatinės nuorodos struktūra" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "Straipsnio tekstui naudokite rezervuotą vietą ir įskiepiuis" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "Naudoti santraukos lauką" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "Įgalinti susijusius straipsnius" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Nustatyti autorių" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "Pagal numatymą nustatyti autorių" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "Puslapiavimo dydis" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "Kai puslapiuojamas sąrašas, kiek straipsnių atvaizduoti puslapyje?" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "Šablono priešdėlis" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "Alternatyvus katalogas iš kurio būtų užkraunami šablonai" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "Meniu struktūra" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "Django CMS meniu struktūra" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "Rodyti tuščias kategorijas meniu" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "Rodyti kategorijas be susietų straipsnių meniu" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "Tinklalapio medžio keitimo dažnumas" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "Keitimo dažnumo atributas tinklalapio medžio objektams" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "Tinklalapio medžio prioritetas" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "Prioriteto atributas tinklalapio medžio objektams" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "Objekto tipas" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "Facebook tipas" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "Facebook aplikacijos ID" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "Facebook profilio ID" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "Facebook puslapio URL" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "Facebook autoriaus URL" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "Facebook autorius" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "Twitter tipas" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "Twitter tinklalapio vardas" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "Twitter autoriaus vardas" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "Publikavus straipsnį siųsti pranešimus" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "Išsiunčia darbalaukio pranešima -jei įgalinta- kai publikuojamas naujas straipsnis" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "Atnaujinus straipsnį siųsti pranešimus" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "Išsiunčia darbalaukio pranešima -jei įgalinta- kai redaguojamas publikuotas straipsnis" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Tinklaraštis" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "Tinklaraščio meniu" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Straipsnių sąrašas" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Pridėti straipsnį" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "Redaguoti nustatymus" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Redaguoti straipsnį" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "Publikuoti {0} dabar" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "Nuoroda" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "Palikti tuščią, kad būtų nuoroda būtų sugeneruota automatiškai, arba perrašyti pagal poreikius." + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "Naujas {0}" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "Kurti naują {0} viduje {1}" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "Tinklaraščio straipsniai %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "Gyvas tinklaraštis" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "Gyvo tinklaraščio objektas" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "publikuoti gyvo tinklaraščio įrašą" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "publikavimo data" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "gyvo tinklaraščio įrašas" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "gyvo tinklaraščio įrašai" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "pavadinimas" + +#: liveblog/models.py:95 +msgid "image" +msgstr "paveikslėlis" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "piktogramos dydis" + +#: models.py:95 +msgid "parent" +msgstr "pirminis elementas" + +#: models.py:97 +msgid "created at" +msgstr "sukurta" + +#: models.py:98 +msgid "modified at" +msgstr "redaguota" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "aplikacijos nustatymai" + +#: models.py:102 +msgid "name" +msgstr "pavadinimas" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "nuoroda" + +#: models.py:104 +msgid "category meta description" +msgstr "kategorijos meta apibūdinimas" + +#: models.py:132 +msgid "blog category" +msgstr "tinklaraščio kategorija" + +#: models.py:133 +msgid "blog categories" +msgstr "tinklaraščio kategorijos" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "" + +#: models.py:196 +msgid "author" +msgstr "autorius" + +#: models.py:203 +msgid "created" +msgstr "sukurta" + +#: models.py:204 +msgid "last modified" +msgstr "paskutinį kartą redaguota" + +#: models.py:205 +msgid "published since" +msgstr "publikuota nuo" + +#: models.py:206 +msgid "published until" +msgstr "publikuota iki" + +#: models.py:207 +msgid "featured date" +msgstr "populiarinimo data" + +#: models.py:208 +msgid "publish" +msgstr "publikuoti" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "kategorija" + +#: models.py:214 +msgid "main image" +msgstr "pagrindinis paveikslėlis" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "pagrindinio paveikslėlio miniatiūra" + +#: models.py:230 +msgid "main image full" +msgstr "pagrindinis paveikslėlis pilnas" + +#: models.py:237 +msgid "enable comments on post" +msgstr "įgalinti straipsniui komentarus" + +#: models.py:241 +msgid "Site(s)" +msgstr "Tinklalapis(iai)" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Pasirinkite puslapius kuriuose bus rodomas straipsnis. Jei nepasirinktas nei vienas puslapis, straipsnis bus rodomas visuose puslapiuose." + +#: models.py:256 +msgid "subtitle" +msgstr "" + +#: models.py:257 +msgid "abstract" +msgstr "santrauka" + +#: models.py:258 +msgid "post meta description" +msgstr "straipsnio meta apibūdinimas" + +#: models.py:259 +msgid "post meta keywords" +msgstr "straipsnio meta raktažodžiai" + +#: models.py:261 +msgid "post meta title" +msgstr "straipsnio meta pavadinimas" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "naudojama pavadinimo žymenoje ir socialiniuose tinkluose" + +#: models.py:267 +msgid "text" +msgstr "tekstas" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "įgalinti gyvą tinklaraštį šiame straipsnyje" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "Susiję straipsniai" + +#: models.py:314 +msgid "blog article" +msgstr "tinklaraščio straipsnis" + +#: models.py:315 +msgid "blog articles" +msgstr "tinklaraščio straipsniai" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "" + +#: models.py:493 +msgid "current site" +msgstr "dabartinis tinklalapis" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "Pasirinkti objektus tik iš dabartinio tinklalapio" + +#: models.py:497 +msgid "Plugin template" +msgstr "Įskiepio šablonas" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "Pasirinkite įskiepio šabloną kuris bus kraunamas šiam atvejui" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "straipsniai" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "Atvaizduojamas naujausių straipsnių kiekis." + +#: models.py:541 +msgid "filter by tag" +msgstr "filtruoti pagal žymeną" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Atvaizduoti straipsnius su pasirinktomis žymenomis." + +#: models.py:549 +msgid "filter by category" +msgstr "filtruoti pagal kategoriją" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Atvaizduoti straipsnius su pasirinktomis kategorijomis." + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "%s paskutiniai straipsniai pagal žymeną" + +#: models.py:574 +msgid "authors" +msgstr "autoriai" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "Atvaizduojamas autoriaus straipsnių kiekis." + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "%s paskutiniai straipsniai pagal autorių" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "bendras tinklaraščio įskiepis" + +#: settings.py:26 +msgid "Full date" +msgstr "Pilna data" + +#: settings.py:27 +msgid "Year / Month" +msgstr "Metai / Mėnesis" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Kategorija" + +#: settings.py:29 +msgid "Just slug" +msgstr "Tik nuoroda" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "Kategorijos ir įrašai" + +#: settings.py:60 +msgid "Categories only" +msgstr "Tik kategorijos" + +#: settings.py:61 +msgid "Posts only" +msgstr "Tik įrašai" + +#: settings.py:62 +msgid "None" +msgstr "Niekas" + +#: settings.py:71 +msgid "always" +msgstr "visada" + +#: settings.py:72 +msgid "hourly" +msgstr "kas valandą" + +#: settings.py:73 +msgid "daily" +msgstr "kas dieną" + +#: settings.py:74 +msgid "weekly" +msgstr "kas savaitę" + +#: settings.py:75 +msgid "monthly" +msgstr "kas mėnesį" + +#: settings.py:76 +msgid "yearly" +msgstr "kas metus" + +#: settings.py:77 +msgid "never" +msgstr "niekada" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Naujausi tinklaraščio straipsniai" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "Paskutiniai tinklaraščio straipsniai - kešas" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Tinklaraščio straipsnių autorius" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Žymenos" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Kategorijos" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Archyvas" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "Numatytasis šablonas" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "skaityti daugiau" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "pagal" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "%(articles)s straipsnis" +msgstr[1] "%(articles)s straipsniai" +msgstr[2] "%(articles)s straipsnių" +msgstr[3] "%(articles)s straipsnių" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 straipsnių" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Nerasta jokių straipsnių." + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Autoriai" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Straipsniai pagal" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "Nerasta jokių kategorijų." + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Žymena" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Atgal" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "buvęs" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Puslapis" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "iš" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "kitas" diff --git a/djangocms_blog/locale/nb_NO/LC_MESSAGES/django.po b/djangocms_blog/locale/nb_NO/LC_MESSAGES/django.po new file mode 100644 index 00000000..4a117aa5 --- /dev/null +++ b/djangocms_blog/locale/nb_NO/LC_MESSAGES/django.po @@ -0,0 +1,763 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# fivethreeo , 2016 +# fivethreeo , 2016 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: fivethreeo , 2016\n" +"Language-Team: Norwegian Bokmål (Norway) (http://app.transifex.com/nephila/djangocms-blog/language/nb_NO/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: nb_NO\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "" + +#: admin.py:131 +msgid "Info" +msgstr "" + +#: admin.py:138 +msgid "Images" +msgstr "" + +#: admin.py:141 +msgid "SEO" +msgstr "" + +#: admin.py:172 +msgid "Publish selection" +msgstr "" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "" + +#: admin.py:473 +msgid "Generic" +msgstr "" + +#: admin.py:485 +msgid "Layout" +msgstr "" + +#: admin.py:499 +msgid "Notifications" +msgstr "" + +#: admin.py:503 +msgid "Sitemap" +msgstr "" + +#: admin.py:512 +msgid "Meta" +msgstr "" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "Du kan bruke vanlige strenger, Post modell attributt eller metodenavn" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "django CMS Blog" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "applikasjonstittel" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "objekt navn" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "bloggkonfigurasjon" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "bloggkonfigurasjoner" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "uten tittel" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "Publiser Post automatisk" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "" + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "" + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Permanent link struktur" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "Bruk plassholder og plugin for artikkelinnhold" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "Bruk abstrakt felt" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Sett forfatter" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "Sett forfatter automatisk" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "Sidevisningsstørrelse" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "Ved sidevisning av liste views, hvor mange artikler per side?" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "Template prefix" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "Alternativ mappe for å laste blogg templates fra" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "Menystruktur" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "Strukturen på django CMS menyen" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "Sidekart endringsfrekvens" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "Changefreq attributt for sidekartelementer" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "Sidekartprioritet" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "Priority attributt fir sidekartelementer" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "Objekttype" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "Facebooktype" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "Facebook applikasjonsid" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "Facebook profilid" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "Facebook sideadresse" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "Facebook forfatteradresse" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "Facebook forfatter" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "Twittertype" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "Twitter sidenavn" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "Twitter forfatternavn" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "Send notifikasjoner når post publiseres" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "Gir fra seg en skrivebordsnotifikasjon -hvis slått på- når en ny post publiseres" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "Send notifikasjoner når poster oppdateres" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "Gir fra seg en skrivebordsnotifikasjon -hvis slått på- når en publisert post redigeres" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Blogg" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "Bloggmeny" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Postliste" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Legg til post" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "Rediger konfigurasjon" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Rediger Post" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "Publiser {0} nå" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "Ny {0}" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "Opprett en ny {0} i {1}" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "Bloggartikler på %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "tittel" + +#: liveblog/models.py:95 +msgid "image" +msgstr "" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "" + +#: models.py:95 +msgid "parent" +msgstr "forelder" + +#: models.py:97 +msgid "created at" +msgstr "opprettet" + +#: models.py:98 +msgid "modified at" +msgstr "endret" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "app. konfig" + +#: models.py:102 +msgid "name" +msgstr "navn" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "slug" + +#: models.py:104 +msgid "category meta description" +msgstr "" + +#: models.py:132 +msgid "blog category" +msgstr "bloggkategori" + +#: models.py:133 +msgid "blog categories" +msgstr "bloggkategorier" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "" + +#: models.py:196 +msgid "author" +msgstr "forfatter" + +#: models.py:203 +msgid "created" +msgstr "opprettet" + +#: models.py:204 +msgid "last modified" +msgstr "sist endret" + +#: models.py:205 +msgid "published since" +msgstr "publisert siden" + +#: models.py:206 +msgid "published until" +msgstr "publisert inntil" + +#: models.py:207 +msgid "featured date" +msgstr "" + +#: models.py:208 +msgid "publish" +msgstr "publiser" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "kategori" + +#: models.py:214 +msgid "main image" +msgstr "hovedbilde" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "hovedbilde i miniatyr" + +#: models.py:230 +msgid "main image full" +msgstr "hovedbilde i fullstørrelse " + +#: models.py:237 +msgid "enable comments on post" +msgstr "slå på kommentarer ved posting" + +#: models.py:241 +msgid "Site(s)" +msgstr "Nettsted" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Velg nettstedene du vil vise denne posten på. Hvis satt til ingen vil den vises på alle de konfigurerte nettstedene." + +#: models.py:256 +msgid "subtitle" +msgstr "" + +#: models.py:257 +msgid "abstract" +msgstr "abstrakt" + +#: models.py:258 +msgid "post meta description" +msgstr "post meta beskrivelse" + +#: models.py:259 +msgid "post meta keywords" +msgstr "post meta nøkkelord" + +#: models.py:261 +msgid "post meta title" +msgstr "post meta tittel" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "brukes i title taggen og sosial deling" + +#: models.py:267 +msgid "text" +msgstr "tekst" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "" + +#: models.py:314 +msgid "blog article" +msgstr "bloggartikkel" + +#: models.py:315 +msgid "blog articles" +msgstr "bloggartikler" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "" + +#: models.py:493 +msgid "current site" +msgstr "" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "" + +#: models.py:497 +msgid "Plugin template" +msgstr "" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "artikler" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "Antall siste artikler som skal vises" + +#: models.py:541 +msgid "filter by tag" +msgstr "filtrer etter tagg" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Vis bare bloggartikler tagget med valgte tagger." + +#: models.py:549 +msgid "filter by category" +msgstr "filtrer etter kategori" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Vis bare bloggartikler med valgte kategorier." + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "%s siste artikler etter tagg" + +#: models.py:574 +msgid "authors" +msgstr "forfattere" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "Antall forfatterartikler som skal vises." + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "%s siste artikler av forfatter" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "generisk bloggplugin" + +#: settings.py:26 +msgid "Full date" +msgstr "Full dato" + +#: settings.py:27 +msgid "Year / Month" +msgstr "År / Måned" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Kategori" + +#: settings.py:29 +msgid "Just slug" +msgstr "Bare slug" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "Kategorier og poster" + +#: settings.py:60 +msgid "Categories only" +msgstr "Bare kategorier" + +#: settings.py:61 +msgid "Posts only" +msgstr "Bare poster" + +#: settings.py:62 +msgid "None" +msgstr "Ingen" + +#: settings.py:71 +msgid "always" +msgstr "alltid" + +#: settings.py:72 +msgid "hourly" +msgstr "hver time" + +#: settings.py:73 +msgid "daily" +msgstr "daglig" + +#: settings.py:74 +msgid "weekly" +msgstr "ukentlig" + +#: settings.py:75 +msgid "monthly" +msgstr "månedlig" + +#: settings.py:76 +msgid "yearly" +msgstr "årlig" + +#: settings.py:77 +msgid "never" +msgstr "aldri" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Siste Bloggartikler" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Forfatters Bloggartikler" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Tagger" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Kategorier" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Arkiv" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "les mer" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "av" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "1 artikkel" +msgstr[1] "%(articles)s artikler" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 artikler" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Ingen artikler funnet." + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Forfattere" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Artikler av" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "Ingen kategorier funnet." + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Tagg" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Tilbake" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "forrige" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "side" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "av" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "neste" diff --git a/djangocms_blog/locale/nl/LC_MESSAGES/django.po b/djangocms_blog/locale/nl/LC_MESSAGES/django.po new file mode 100644 index 00000000..ef158213 --- /dev/null +++ b/djangocms_blog/locale/nl/LC_MESSAGES/django.po @@ -0,0 +1,763 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# 9b534940ba88da102a8b2023ae8b0a5c_833de33 <6eff1839cb13e50fd6e97f7bd38c9dda_152118>, 2016 +# Zingende Vogel , 2018 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: Zingende Vogel , 2018\n" +"Language-Team: Dutch (http://app.transifex.com/nephila/djangocms-blog/language/nl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: nl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "" + +#: admin.py:131 +msgid "Info" +msgstr "" + +#: admin.py:138 +msgid "Images" +msgstr "Afbeeldingen" + +#: admin.py:141 +msgid "SEO" +msgstr "" + +#: admin.py:172 +msgid "Publish selection" +msgstr "Publiceer selectie" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "Maak publicatie selectie ongedaan" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "Sta reacties voor selectie toe" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "Reacties voor selectie uitschakelen" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "Activeer liveblog voor selectie" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "Schakel liveblog uit voor selectie" + +#: admin.py:473 +msgid "Generic" +msgstr "Algemeen" + +#: admin.py:485 +msgid "Layout" +msgstr "Layout" + +#: admin.py:499 +msgid "Notifications" +msgstr "Notificaties" + +#: admin.py:503 +msgid "Sitemap" +msgstr "Sitemap" + +#: admin.py:512 +msgid "Meta" +msgstr "Meta" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "Je kan gewone strings opgeven, een Bericht model attribuut of methode namen" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "django CMS Blog" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "applicatie titel" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "object naam" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "blog configuratie" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "blog configuraties" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "geen titel" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "Berichten standaard publiceren" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "" + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "" + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Permalink opbouw" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "Gebruik placeholder en plugins voor de inhoud van het artikel" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "Gebruik het excerpt veld" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Stel auteur in" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "Standaard de auteur instellen" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "Grootte van paginering" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "Wanneer overzichten worden gepagineerd, hoeveel artikelen moeten er dan per pagina getoond worden?" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "Template prefix" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "Alternatieve map waaruit de blog templates geladen worden" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "Menu opbouw" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "Opbouw van het django CMS menu" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "Toon lege categorieën in menu" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "Toon categorieën zonder artikelen in menu" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "Verander-frequentie van de Sitemap" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "Verander-frequentie attribuut voor sitemap items" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "Sitemap prioriteit" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "Prioriteit attribuut voor sitemap items" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "Object type" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "Facebook type" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "Facebook applicatie ID" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "Facebook profiel ID" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "Facebook pagina URL" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "Facebook auteur URL" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "Facebook auteur" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "Twitter type" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "Twitter site handle" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "Twitter auteur handle" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "Stuur melding bij publicatie bericht" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "Verstuur desktop notificatie, mits het aan staat, als een nieuw bericht gepubliceerd wordt" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "Stuur meldingen bij update bericht" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "Verstuur desktop notificatie, mits het aan staat, als een nieuw bericht gewijzigd wordt" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Blog" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "Blog menu" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Overzicht van Berichten" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Bericht toevoegen" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "Wijzig configuratie" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Wijzig Bericht" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "Publiceer {0} nu" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "Nieuwe {0}" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "Maak een nieuwe {0} in {1}" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "Blog artikelen op %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "Liveblog artikel" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "Publiceer liveblog artikel" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "Post datum" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "liveblog artikel" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "liveblog artikelen" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "title" + +#: liveblog/models.py:95 +msgid "image" +msgstr "afbeelding" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "thumbnail afmeting" + +#: models.py:95 +msgid "parent" +msgstr "bovenliggende" + +#: models.py:97 +msgid "created at" +msgstr "aangemaakt op" + +#: models.py:98 +msgid "modified at" +msgstr "gewijzigd op" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "applicatie configuratie" + +#: models.py:102 +msgid "name" +msgstr "naam" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "slug" + +#: models.py:104 +msgid "category meta description" +msgstr "" + +#: models.py:132 +msgid "blog category" +msgstr "blog categorie" + +#: models.py:133 +msgid "blog categories" +msgstr "blog categorieën" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "" + +#: models.py:196 +msgid "author" +msgstr "auteur" + +#: models.py:203 +msgid "created" +msgstr "aangemaakt" + +#: models.py:204 +msgid "last modified" +msgstr "laatst gewijzigd" + +#: models.py:205 +msgid "published since" +msgstr "gepubliceerd sinds" + +#: models.py:206 +msgid "published until" +msgstr "gepubliceerd tot" + +#: models.py:207 +msgid "featured date" +msgstr "feature datum" + +#: models.py:208 +msgid "publish" +msgstr "publiceer" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "categorie" + +#: models.py:214 +msgid "main image" +msgstr "hoofd afbeelding" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "hoofd afbeelding thumbnail" + +#: models.py:230 +msgid "main image full" +msgstr "hoofd afbeelding volledig" + +#: models.py:237 +msgid "enable comments on post" +msgstr "sta reacties toe op bericht" + +#: models.py:241 +msgid "Site(s)" +msgstr "Site(s)" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Selecteer sites die het bericht moeten tonen. Als geen site ingesteld wordt, dan is het zichtbaar voor alle geconfigureerde sites." + +#: models.py:256 +msgid "subtitle" +msgstr "" + +#: models.py:257 +msgid "abstract" +msgstr "korte samenvatting" + +#: models.py:258 +msgid "post meta description" +msgstr "meta omschrijving van bericht" + +#: models.py:259 +msgid "post meta keywords" +msgstr "meta sleutelwoorden van bericht " + +#: models.py:261 +msgid "post meta title" +msgstr "meta titel van bericht" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "wordt gebruikt in de titel tag en bij het delen via sociale media" + +#: models.py:267 +msgid "text" +msgstr "tekst" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "activeer liveblog voor post" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "Gerelateerde artikelen" + +#: models.py:314 +msgid "blog article" +msgstr "blog artikel" + +#: models.py:315 +msgid "blog articles" +msgstr "blog artikelen" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "" + +#: models.py:493 +msgid "current site" +msgstr "huidige site" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "Selecteer alleen items van de huidige site" + +#: models.py:497 +msgid "Plugin template" +msgstr "" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "Selecteer plugin template voor deze instance" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "artikelen" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "Het aantal nieuwste berichten om te tonen." + +#: models.py:541 +msgid "filter by tag" +msgstr "filter op tag" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Alleen blog artikelen tonen die gekoppeld zijn aan de gekozen tags." + +#: models.py:549 +msgid "filter by category" +msgstr "filter op categorie" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Alleen blog artikelen tonen die gekoppeld zijn aan de gekozen categorieën." + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "%s nieuwste artikelen met tag" + +#: models.py:574 +msgid "authors" +msgstr "auteurs" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "Het aantal artikelen door auteur om te tonen." + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "%s nieuwste artikelen door auteur" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "generieke blog plugin" + +#: settings.py:26 +msgid "Full date" +msgstr "Volledige datum" + +#: settings.py:27 +msgid "Year / Month" +msgstr "Jaar / Maand" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Categorie" + +#: settings.py:29 +msgid "Just slug" +msgstr "Alleen slug" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "Categorieën en berichten" + +#: settings.py:60 +msgid "Categories only" +msgstr "Alleen Categorieën" + +#: settings.py:61 +msgid "Posts only" +msgstr "Alleen Berichten" + +#: settings.py:62 +msgid "None" +msgstr "Geen" + +#: settings.py:71 +msgid "always" +msgstr "altijd" + +#: settings.py:72 +msgid "hourly" +msgstr "elk uur" + +#: settings.py:73 +msgid "daily" +msgstr "elke dag" + +#: settings.py:74 +msgid "weekly" +msgstr "elke week" + +#: settings.py:75 +msgid "monthly" +msgstr "elke maand" + +#: settings.py:76 +msgid "yearly" +msgstr "elk jaar" + +#: settings.py:77 +msgid "never" +msgstr "nooit" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Nieuwste Blog Artikelen" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "Laatste Blog Artikelen - Cache" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Blog Artikelen Van Auteur" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Tags" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Categorieën " + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Archief" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "Standaard template" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "lees verder" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "door" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "1 artikel" +msgstr[1] "%(articles)s artikelen" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 artikelen" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Geen artikelen gevonden." + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Auteurs" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Artikelen door" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "Geen categorieën gevonden." + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Tag" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Terug" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "vorige" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Pagina" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "van" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "volgende" diff --git a/djangocms_blog/locale/pl_PL/LC_MESSAGES/django.po b/djangocms_blog/locale/pl_PL/LC_MESSAGES/django.po new file mode 100644 index 00000000..a8231a75 --- /dev/null +++ b/djangocms_blog/locale/pl_PL/LC_MESSAGES/django.po @@ -0,0 +1,764 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# marcingarbarczykapz , 2019 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: marcingarbarczykapz , 2019\n" +"Language-Team: Polish (Poland) (http://app.transifex.com/nephila/djangocms-blog/language/pl_PL/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl_PL\n" +"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "strona" + +#: admin.py:131 +msgid "Info" +msgstr "Info" + +#: admin.py:138 +msgid "Images" +msgstr "Obrazki" + +#: admin.py:141 +msgid "SEO" +msgstr "SEO" + +#: admin.py:172 +msgid "Publish selection" +msgstr "Opublikuj zaznaczone" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "Ukryj zaznaczone" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "Włącz komentarze dla zaznaczonych elementów" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "Wyłącz komentarze dla zaznaczonych elementów" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "Włącz liveblog dla zaznaczonych" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "Wyłącz liveblog dla zaznaczonych" + +#: admin.py:473 +msgid "Generic" +msgstr "Generic" + +#: admin.py:485 +msgid "Layout" +msgstr "Szablon" + +#: admin.py:499 +msgid "Notifications" +msgstr "Powiadomienia" + +#: admin.py:503 +msgid "Sitemap" +msgstr "Sitemapa" + +#: admin.py:512 +msgid "Meta" +msgstr "Meta" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "Możesz podać ciągi znaków, model lub nazwy metod" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "django CMS Blog" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "nazwa aplikacji" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "nazwa obiektu" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "konfiguracja bloga" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "konfiguracje bloga" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "bez tytułu" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "Opublikowane posty domyślnie" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "Domyślny rozmiar pełnych obrazków" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "Jeśli pozostanie pusty, rozmiar obrazu będzie musiał zostać ustawiony dla każdego nowo utworzonego wpisu." + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "Domyślny rozmiar miniaturek" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "Jeśli pozostanie pusty, rozmiar miniatury obrazu będzie musiał zostać ustawiony dla każdego nowo utworzonego wpisu." + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Struktura linku" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "Użyj placeholder i pluginy dla treści artykułu" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "Użyj pola abstrakcyjnego" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "Włącz powiązane posty" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Ustaw autora" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "Ustaw autora domyślnie" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "Paginacja" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "Jak dużo artykułów na stronę?" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "Prefix szablonu" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "Alternatywna ścieżka do ładowania szablonów" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "Struktura menu" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "Struktura menu django CMS" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "Pokaż puste kategorie w menu" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "Pokaż kategorie bez postów w menu" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "Sitemap changefreq" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "Changefreq attribute for sitemap items" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "Priorytet sitemapy" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "Priority attribute for sitemap items" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "Typ obiektu" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "Facebook type" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "Facebook application ID" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "Facebook profile ID" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "Facebook page URL" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "Facebook author URL" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "Facebook author" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "Twitter type" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "Twitter site handle" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "Twitter author handle" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "Wyślij powiadomienie przy publikowaniu" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "Emituje powiadomienie na pulpicie - jeśli włączone - podczas publikowania nowego postu" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "Wyślij powiadomienie przy edycji postu" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "Emituje powiadomienie na pulpicie - jeśli włączone - podczas edycji opublikowanego postu" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Blog" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "Menu bloga" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Lista postów" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Dodaj post" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "Edytuj konfigurację" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Edytuj posta" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "Publikuj {0} teraz" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "Slug" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "Pozostaw puste dla automatycznego ślimaka lub zastąp w razie potrzeby." + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "Nowe {0}" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "Stwórz nowe {0} w {1}" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "Artykuły na %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "Liveblog" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "Elementy liveblog" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "publikuj wpis liveblog" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "data postu" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "element liveblog" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "elementy liveblog" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "tytuł" + +#: liveblog/models.py:95 +msgid "image" +msgstr "obrazek" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "rozmiar miniaturki" + +#: models.py:95 +msgid "parent" +msgstr "rodzic" + +#: models.py:97 +msgid "created at" +msgstr "stworzony" + +#: models.py:98 +msgid "modified at" +msgstr "zmodyfikowany" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "app. config" + +#: models.py:102 +msgid "name" +msgstr "nazwa" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "slug" + +#: models.py:104 +msgid "category meta description" +msgstr "meta description kategorii" + +#: models.py:132 +msgid "blog category" +msgstr "kategoria bloga" + +#: models.py:133 +msgid "blog categories" +msgstr "kategorie bloga" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "BlogCategory (no translation)" + +#: models.py:196 +msgid "author" +msgstr "autor" + +#: models.py:203 +msgid "created" +msgstr "stworzone" + +#: models.py:204 +msgid "last modified" +msgstr "zmodyfikowane" + +#: models.py:205 +msgid "published since" +msgstr "opublikowane od" + +#: models.py:206 +msgid "published until" +msgstr "opublikowane do" + +#: models.py:207 +msgid "featured date" +msgstr "data wyróżnienia" + +#: models.py:208 +msgid "publish" +msgstr "publikuj" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "kategoria" + +#: models.py:214 +msgid "main image" +msgstr "główny obrazek" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "miniaturka głównego obrazka" + +#: models.py:230 +msgid "main image full" +msgstr "pełny główny obrazek" + +#: models.py:237 +msgid "enable comments on post" +msgstr "włącz komentarze na poście" + +#: models.py:241 +msgid "Site(s)" +msgstr "Strona(y)" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Wybierz witryny, w których chcesz pokazać wpis. Jeśli żaden nie jest ustawiony, będzie widoczny we wszystkich skonfigurowanych witrynach." + +#: models.py:256 +msgid "subtitle" +msgstr "podtytuł" + +#: models.py:257 +msgid "abstract" +msgstr "abstract" + +#: models.py:258 +msgid "post meta description" +msgstr "meta description postu" + +#: models.py:259 +msgid "post meta keywords" +msgstr "meta keywords postu" + +#: models.py:261 +msgid "post meta title" +msgstr "meta title postu" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "używany w tagu tytułu i udostępnianiu społecznościowym" + +#: models.py:267 +msgid "text" +msgstr "tekst" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "włącz liveblog na poście" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "Powiązane posty" + +#: models.py:314 +msgid "blog article" +msgstr "artykuł bloga" + +#: models.py:315 +msgid "blog articles" +msgstr "artykuły bloga" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "Posty (nie przetłumaczone)" + +#: models.py:493 +msgid "current site" +msgstr "obecna strona" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "Wybierz elementy z obecnej strony" + +#: models.py:497 +msgid "Plugin template" +msgstr "Plugin template" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "Wybierz template pluginu do załadowania dla tej instancji" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "artykuły" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "Liczba wyświetlanych ostatnich artykułów" + +#: models.py:541 +msgid "filter by tag" +msgstr "filtruj według tagu" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Pokaż tylko artykuły oznaczone wybranymi tagami" + +#: models.py:549 +msgid "filter by category" +msgstr "filtruj według kategorii" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Pokaż tylko artykuły oznaczone wybranymi kategoriami" + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "%s ostatnie artykuły według tagu" + +#: models.py:574 +msgid "authors" +msgstr "autorzy" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "Liczba autorów artykułów do wyświetlenia." + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "%s ostatnich artykułów według autora" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "generic blog plugin" + +#: settings.py:26 +msgid "Full date" +msgstr "Pełna data" + +#: settings.py:27 +msgid "Year / Month" +msgstr "Rok / miesiąc" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Kategoria" + +#: settings.py:29 +msgid "Just slug" +msgstr "Just slug" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "Kategorie i posty" + +#: settings.py:60 +msgid "Categories only" +msgstr "Tylko kategorie" + +#: settings.py:61 +msgid "Posts only" +msgstr "Tylko posty" + +#: settings.py:62 +msgid "None" +msgstr "Brak" + +#: settings.py:71 +msgid "always" +msgstr "zawsze" + +#: settings.py:72 +msgid "hourly" +msgstr "godzinnie" + +#: settings.py:73 +msgid "daily" +msgstr "dziennie" + +#: settings.py:74 +msgid "weekly" +msgstr "tygodniowo" + +#: settings.py:75 +msgid "monthly" +msgstr "miesięcznie" + +#: settings.py:76 +msgid "yearly" +msgstr "rocznie" + +#: settings.py:77 +msgid "never" +msgstr "nigdy" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Ostatnie artykuły bloga" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "Ostatnie artykuły bloga - cache" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Artykuły bloga autora" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Tagi" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Kategorie" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Archiwum" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "Domyślny template" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "czytaj więcej" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "przez" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "1 artykuł" +msgstr[1] "%(articles)s artykuły" +msgstr[2] "%(articles)s artykułów" +msgstr[3] "%(articles)s artykułów" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 artykułów" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Nie znaleziono artykułów." + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Autorzy" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Artykuły według" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "Nie znaleziono kategorii." + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Tag" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Powrót" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "poprzedni" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Strona" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "z" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "następny" diff --git a/djangocms_blog/locale/pt_BR/LC_MESSAGES/django.po b/djangocms_blog/locale/pt_BR/LC_MESSAGES/django.po new file mode 100644 index 00000000..86e2fb4a --- /dev/null +++ b/djangocms_blog/locale/pt_BR/LC_MESSAGES/django.po @@ -0,0 +1,763 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Diego Ponci , 2016 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: Diego Ponci , 2016\n" +"Language-Team: Portuguese (Brazil) (http://app.transifex.com/nephila/djangocms-blog/language/pt_BR/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt_BR\n" +"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "" + +#: admin.py:131 +msgid "Info" +msgstr "" + +#: admin.py:138 +msgid "Images" +msgstr "" + +#: admin.py:141 +msgid "SEO" +msgstr "" + +#: admin.py:172 +msgid "Publish selection" +msgstr "" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "" + +#: admin.py:473 +msgid "Generic" +msgstr "" + +#: admin.py:485 +msgid "Layout" +msgstr "" + +#: admin.py:499 +msgid "Notifications" +msgstr "" + +#: admin.py:503 +msgid "Sitemap" +msgstr "" + +#: admin.py:512 +msgid "Meta" +msgstr "" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "Você pode fornecer texto puro, atributos do modelo Post ou nomes de métodos" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "blog do django CMS" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "título da aplicação" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "nome do objeto" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "configuração do blog" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "configurações do blog" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "sem título" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "Post publicado por padrão" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "" + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "" + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Estrutura de links permanentes" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "Usar placeholder e plugins para o corpo da notícia" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "Usar campo abstrato" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Informar o autor" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "Informar o autor por padrão" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "Tamanho da paginação" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "Em paginações de listagem, quantas notícias por página?" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "Prefixo do template" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "Diretório alternativo de origem dos templates do blog" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "Estrutura do menu" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "Estrutura do menu do django CMS" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "'changefreq' do sitemap" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "Atributo changefreq de items do sitemap" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "'priority' do sitemap" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "Atributo 'priority' de items do sitemap" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "Tipo de objeto" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "Tipo de conta do facebook" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "ID da aplicação no Facebook" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "ID do perfil no Facebook" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "URL da página no Facebook" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "URL do autor no Facebook" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "Autor no Facebook" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "Tipo do Twitter" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "Nome de usuário no Twitter" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "Usuário do autor no Twitter" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "Enviar notificações ao publicar postagem" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "Enviar notificação de desktop -se ativada- quando publicar uma nova postagem" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "Enviar notificações ao atualizar postagem" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "Enviar notificação de desktop -se ativada- quando editar uma postagem publicada" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Blog" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "Menu do blog" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Lista de postagens" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Adicionar postagem" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "Editar preferências" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Ediar Postagem" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "Publicar {0} agora" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "Novo {0}" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "Criar um novo {0} em {1}" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "Artigos em %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "título" + +#: liveblog/models.py:95 +msgid "image" +msgstr "" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "" + +#: models.py:95 +msgid "parent" +msgstr "pai" + +#: models.py:97 +msgid "created at" +msgstr "criado em" + +#: models.py:98 +msgid "modified at" +msgstr "modificado em" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "configuração da aplicação" + +#: models.py:102 +msgid "name" +msgstr "nome" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "slug" + +#: models.py:104 +msgid "category meta description" +msgstr "" + +#: models.py:132 +msgid "blog category" +msgstr "categoria do blog" + +#: models.py:133 +msgid "blog categories" +msgstr "categorias do blog" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "" + +#: models.py:196 +msgid "author" +msgstr "autor" + +#: models.py:203 +msgid "created" +msgstr "criado" + +#: models.py:204 +msgid "last modified" +msgstr "última modificação" + +#: models.py:205 +msgid "published since" +msgstr "publicado desde" + +#: models.py:206 +msgid "published until" +msgstr "publicado até" + +#: models.py:207 +msgid "featured date" +msgstr "" + +#: models.py:208 +msgid "publish" +msgstr "publicar" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "categoria" + +#: models.py:214 +msgid "main image" +msgstr "imagem principal" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "miniatura da imagem principal" + +#: models.py:230 +msgid "main image full" +msgstr "imagem principal completa" + +#: models.py:237 +msgid "enable comments on post" +msgstr "habilitar comentários nessa postagem" + +#: models.py:241 +msgid "Site(s)" +msgstr "Site(s)" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Selecionar sites onde a postagem deve aparecer. Se nenhum for selecionado ela ficará visível em todos os sites configurados." + +#: models.py:256 +msgid "subtitle" +msgstr "" + +#: models.py:257 +msgid "abstract" +msgstr "resumo" + +#: models.py:258 +msgid "post meta description" +msgstr "meta descrição da postagem" + +#: models.py:259 +msgid "post meta keywords" +msgstr "palavras-chave da postagem" + +#: models.py:261 +msgid "post meta title" +msgstr "meta título da postagem" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "usado na tag de título e compartilhamento em redes sociais" + +#: models.py:267 +msgid "text" +msgstr "texto" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "" + +#: models.py:314 +msgid "blog article" +msgstr "artigo do blog" + +#: models.py:315 +msgid "blog articles" +msgstr "artigos do blog" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "" + +#: models.py:493 +msgid "current site" +msgstr "" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "" + +#: models.py:497 +msgid "Plugin template" +msgstr "" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "artigos" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "O número dos últimos artigos a serem exibidos." + +#: models.py:541 +msgid "filter by tag" +msgstr "filtrar por tag" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Exibir apenas os artigos marcados com as tags escolhidas." + +#: models.py:549 +msgid "filter by category" +msgstr "filtrar por categoria" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Exibir apenas os artigos marcados com as categorias escolhidas." + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "%s últimos artigos por tag" + +#: models.py:574 +msgid "authors" +msgstr "autores" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "Número de artigos do autor a serem exibidos." + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "%s últimos artigos por autor" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "plugin genérico do blog" + +#: settings.py:26 +msgid "Full date" +msgstr "Data completa" + +#: settings.py:27 +msgid "Year / Month" +msgstr "Ano / Mês" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Categoria" + +#: settings.py:29 +msgid "Just slug" +msgstr "Apenas a slug" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "Categorias e postagens" + +#: settings.py:60 +msgid "Categories only" +msgstr "Apenas categorias" + +#: settings.py:61 +msgid "Posts only" +msgstr "Apenas postagens" + +#: settings.py:62 +msgid "None" +msgstr "Nenhum" + +#: settings.py:71 +msgid "always" +msgstr "sempre" + +#: settings.py:72 +msgid "hourly" +msgstr "a cada hora" + +#: settings.py:73 +msgid "daily" +msgstr "diariamente" + +#: settings.py:74 +msgid "weekly" +msgstr "semanalmente" + +#: settings.py:75 +msgid "monthly" +msgstr "mensalmente" + +#: settings.py:76 +msgid "yearly" +msgstr "anualmente" + +#: settings.py:77 +msgid "never" +msgstr "nunca" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Últimos Artigos" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Artigos do Autor" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Tags" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Categorias" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Arquivo" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "ler mais" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "por" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "1 artigo" +msgstr[1] "%(articles)s artigos" +msgstr[2] "%(articles)s artigos" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 artigos" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Nenhum artigo encontrado." + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Autores" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Artigos por" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "Nenhuma categoria encontrada." + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Tag" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Voltar" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "anterior" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Página" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "de" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "próxima" diff --git a/djangocms_blog/locale/ru/LC_MESSAGES/django.po b/djangocms_blog/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 00000000..c0fc2cec --- /dev/null +++ b/djangocms_blog/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,767 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Iacopo Spalletti, 2014 +# Rustam Mirzaev , 2016-2018 +# Rustam Mirzaev , 2015 +# Rustam Mirzaev , 2015 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: Rustam Mirzaev , 2016-2018\n" +"Language-Team: Russian (http://app.transifex.com/nephila/djangocms-blog/language/ru/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ru\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" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "сайт" + +#: admin.py:131 +msgid "Info" +msgstr "Информация" + +#: admin.py:138 +msgid "Images" +msgstr "Изображения" + +#: admin.py:141 +msgid "SEO" +msgstr "СЕО" + +#: admin.py:172 +msgid "Publish selection" +msgstr "Опубликовать выделенное" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "Снять с публикации выделенное" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "Включить коментарии к выделенному" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "Выключить коментарии к выделенному" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "Включить живой блог к выделенному" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "Выключить живой блог к выделенному" + +#: admin.py:473 +msgid "Generic" +msgstr "Общий" + +#: admin.py:485 +msgid "Layout" +msgstr "Макет" + +#: admin.py:499 +msgid "Notifications" +msgstr "Уведомления" + +#: admin.py:503 +msgid "Sitemap" +msgstr "Карта сайта" + +#: admin.py:512 +msgid "Meta" +msgstr "Мета" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "Вы можете использовать простую строку, атрибут модели статьи или имена методов" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "django CMS Блог" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "название приложения" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "название объекта" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "настройка блога" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "настройки блога" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "безымянный" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "Статья опубликованная по умолчанию" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "Размер изображения по умолчанию" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "Если слева пусто, то размер изображения будет применен к каждому новосозданному изображению" + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "Размер изображения заглушки по умолчанию" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "Если слева пусто, то этот размер изображения заглушки будет применен к каждому новосозданному изображению" + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Строение постоянной ссылки" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "Используйте местозаполнитель и плагины для тела статьи" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "Используйте поле краткого описания" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "Включить связанные публикации" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Указать автора" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "Указать автора по умолчанию" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "Разбить на части" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "Сколько статей на страницу вы бы хотели использовать при разбиении её на части." + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "Префикс шаблона" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "Альтернативная директория загрузки шаблонов блога" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "Структура меню" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "Структура меню django CMS " + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "Показывать пустые категории в меню" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "Показывать категории без статей в меню" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "Частота обновления карты сайта" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "Атрибут частоты изменения объектов карты сайта" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "Приоритет" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "Приоритет атребута для объектов карты сайта" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "Тип объекта" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "тип в Facebook" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "ID приложения на Facebook" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "ID профиля на Facebook" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "URL страницы на Facebook" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "URL автора на Facebook " + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "автор на Facebook " + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "тип на Twitter" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "дескриптор сайта на Twitter" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "дескриптор автора на Twitter" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "Отправлять уведомления после публикации статьи" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "Уведомлять сигналами на рабочем столе, если включено, при публикации новой записи" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "Отправлять уведомления после публикации" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "Уведомлять сигналами на рабочем столе, если включено, при редактировании опубликованной записи" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Блог" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "Меню блога" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Список статей" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Добавить статью" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "Редактировать настройки" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Редактировать статью" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "Опубликовано сейчас {0} " + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "ЧПУ" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "Оставьте пустым для автоматического ЧПУ, либо измените как понадобится" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "Новых {0}" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "Создать новых {0} из {1}" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "Статьи блога с сайта: %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "Транслируемый блог" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "Новость в транслируемом блоге" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "опубликовать запись в транслируемый блог" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "дата публикации" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "запись транслируемого блога" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "записи в транслируемом блоге" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "Заголовок" + +#: liveblog/models.py:95 +msgid "image" +msgstr "изображение" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "размер миниатюры" + +#: models.py:95 +msgid "parent" +msgstr "предок" + +#: models.py:97 +msgid "created at" +msgstr "время создания" + +#: models.py:98 +msgid "modified at" +msgstr "время изменения" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "настройки приложения" + +#: models.py:102 +msgid "name" +msgstr "название" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "ЧПУ" + +#: models.py:104 +msgid "category meta description" +msgstr "категория мета описания" + +#: models.py:132 +msgid "blog category" +msgstr "категория блога" + +#: models.py:133 +msgid "blog categories" +msgstr "категории блога" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "" + +#: models.py:196 +msgid "author" +msgstr "автор" + +#: models.py:203 +msgid "created" +msgstr "создан(о)" + +#: models.py:204 +msgid "last modified" +msgstr "последние изменения" + +#: models.py:205 +msgid "published since" +msgstr "опубликовано" + +#: models.py:206 +msgid "published until" +msgstr "опубликовано до" + +#: models.py:207 +msgid "featured date" +msgstr "избранная дата" + +#: models.py:208 +msgid "publish" +msgstr "опубликовать" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "категория" + +#: models.py:214 +msgid "main image" +msgstr "главная страница" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "уменьшенная копия главного изображения" + +#: models.py:230 +msgid "main image full" +msgstr "Полноразмерное изображение" + +#: models.py:237 +msgid "enable comments on post" +msgstr "Разрешить комментарии к статье" + +#: models.py:241 +msgid "Site(s)" +msgstr "Сайт(ы)" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Выберите сайты на которых будет опубликована статья. Если сайты не выбраны, статья будет опубликована на всех сайтах сразу." + +#: models.py:256 +msgid "subtitle" +msgstr "" + +#: models.py:257 +msgid "abstract" +msgstr "Краткое описание" + +#: models.py:258 +msgid "post meta description" +msgstr "мета-описание статьи" + +#: models.py:259 +msgid "post meta keywords" +msgstr "ключевые слова к статье" + +#: models.py:261 +msgid "post meta title" +msgstr "мета-заголовок к статье" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "используется в метках и обмене в социальных сетях" + +#: models.py:267 +msgid "text" +msgstr "текст" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "включить трансляцию при публикации" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "Связанные Публикации" + +#: models.py:314 +msgid "blog article" +msgstr "статья блога" + +#: models.py:315 +msgid "blog articles" +msgstr "статьи блога" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "" + +#: models.py:493 +msgid "current site" +msgstr "текущий сайт" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "Выбрать записи только из текущего сайта" + +#: models.py:497 +msgid "Plugin template" +msgstr "Шаблон плагина" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "Выберите шаблон плагина для загрузки к данному экземпляру объекта" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "статьи" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "Количество последних статей, которые будут показаны." + +#: models.py:541 +msgid "filter by tag" +msgstr "фильтровать по тегам" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Показывать статьи только с выбранными метками." + +#: models.py:549 +msgid "filter by category" +msgstr "фильтровать по категориям" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Показывать статьи только из выбранных категорий." + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "%s последних статей по тегам" + +#: models.py:574 +msgid "authors" +msgstr "авторы" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "Количество статей автора, которые будут показаны." + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "%s последних статей по авторам" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "генерируемый плагин блога" + +#: settings.py:26 +msgid "Full date" +msgstr "Полная дата" + +#: settings.py:27 +msgid "Year / Month" +msgstr "Год / Месяц" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Категория" + +#: settings.py:29 +msgid "Just slug" +msgstr "ЧПУ" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "Категории и статьи" + +#: settings.py:60 +msgid "Categories only" +msgstr "Только категории" + +#: settings.py:61 +msgid "Posts only" +msgstr "Только статьи" + +#: settings.py:62 +msgid "None" +msgstr "Ничего" + +#: settings.py:71 +msgid "always" +msgstr "всегда" + +#: settings.py:72 +msgid "hourly" +msgstr "ежечасно" + +#: settings.py:73 +msgid "daily" +msgstr "ежедневно" + +#: settings.py:74 +msgid "weekly" +msgstr "еженедельно" + +#: settings.py:75 +msgid "monthly" +msgstr "ежемесячно" + +#: settings.py:76 +msgid "yearly" +msgstr "ежегодно" + +#: settings.py:77 +msgid "never" +msgstr "никогда" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Последние статьи блога" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "Последние статьи блога - Кэш" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Автор статей блога" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Метки" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Категории" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Архив" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "Шаблон по умолчанию" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "читать далее" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "создана" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "%(articles)s статья" +msgstr[1] "%(articles)s статьи" +msgstr[2] "%(articles)s статей" +msgstr[3] "%(articles)s статей" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 статей" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Не найдено ни одной статьи." + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Авторы" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Статьи созданы" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "Не найдено ни одной категории." + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Метка" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Назад" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "предыдущая" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Страница" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "из" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "следующая" diff --git a/djangocms_blog/locale/sl/LC_MESSAGES/django.po b/djangocms_blog/locale/sl/LC_MESSAGES/django.po new file mode 100644 index 00000000..dc16dc38 --- /dev/null +++ b/djangocms_blog/locale/sl/LC_MESSAGES/django.po @@ -0,0 +1,764 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Iacopo Spalletti, 2014 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: Iacopo Spalletti, 2014\n" +"Language-Team: Slovenian (http://app.transifex.com/nephila/djangocms-blog/language/sl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sl\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "" + +#: admin.py:131 +msgid "Info" +msgstr "" + +#: admin.py:138 +msgid "Images" +msgstr "" + +#: admin.py:141 +msgid "SEO" +msgstr "" + +#: admin.py:172 +msgid "Publish selection" +msgstr "" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "" + +#: admin.py:473 +msgid "Generic" +msgstr "" + +#: admin.py:485 +msgid "Layout" +msgstr "" + +#: admin.py:499 +msgid "Notifications" +msgstr "" + +#: admin.py:503 +msgid "Sitemap" +msgstr "" + +#: admin.py:512 +msgid "Meta" +msgstr "" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "" + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "django CMS Blog" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "naziv aplikacije" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "ime objekta" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "nepoimenovano" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "Objava naj bo samodejno objavljena" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "" + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "" + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Struktura povezav" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "Za objavo naj bodo uporabljeni vtičniki in označbe mest" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "Uporaba abstraktnih polj" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Nastavi avtorja" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "Avtor naj bo nastavljen samodejno" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "Število objav na stran" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "Koliko objav naj bo na eni strani?" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "Predpona predlog" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "Alternativni direktorij za nalaganje predlog bloga" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "Struktura menija" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "Struktura django CMS menija" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "Sitemap prioriteta" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "Prioriteta atributov za elemente sitemapa" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "Tip objekta" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "Facebook avtor" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Blog" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "Blog meni" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Seznam objav" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Nova objava" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "Uredi nastavitve" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Uredi objavo" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "Nov {0}" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "Ustvari nov {0} v {1}" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "Blog objave na %(site_name)s" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "naziv" + +#: liveblog/models.py:95 +msgid "image" +msgstr "" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "" + +#: models.py:95 +msgid "parent" +msgstr "starš" + +#: models.py:97 +msgid "created at" +msgstr "ustvarjeno na" + +#: models.py:98 +msgid "modified at" +msgstr "urejeno na" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "" + +#: models.py:102 +msgid "name" +msgstr "ime" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "" + +#: models.py:104 +msgid "category meta description" +msgstr "" + +#: models.py:132 +msgid "blog category" +msgstr "blog kategorija" + +#: models.py:133 +msgid "blog categories" +msgstr "blog kategorije" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "" + +#: models.py:196 +msgid "author" +msgstr "avtor" + +#: models.py:203 +msgid "created" +msgstr "ustvarjeno" + +#: models.py:204 +msgid "last modified" +msgstr "nazadnje urejeno" + +#: models.py:205 +msgid "published since" +msgstr "objavljeno od" + +#: models.py:206 +msgid "published until" +msgstr "objavljeno do" + +#: models.py:207 +msgid "featured date" +msgstr "" + +#: models.py:208 +msgid "publish" +msgstr "objavljeno" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "kategorija" + +#: models.py:214 +msgid "main image" +msgstr "glavna slika" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "glavna slika - predogled" + +#: models.py:230 +msgid "main image full" +msgstr "glavna slika - polna" + +#: models.py:237 +msgid "enable comments on post" +msgstr "Omogoči komentarje na objavi" + +#: models.py:241 +msgid "Site(s)" +msgstr "Stran(i)" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Izberi strani na katerih naj bo objava prikazana. Če ni izbrano nič, bo objava prikazana na vseh nastavljenih straneh." + +#: models.py:256 +msgid "subtitle" +msgstr "" + +#: models.py:257 +msgid "abstract" +msgstr "osnutek" + +#: models.py:258 +msgid "post meta description" +msgstr "meta opis objave" + +#: models.py:259 +msgid "post meta keywords" +msgstr "meta ključne besede objave" + +#: models.py:261 +msgid "post meta title" +msgstr "meta naziv objave" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "za uporabo v označbi naslova in deljenju na socialnih omrežjih" + +#: models.py:267 +msgid "text" +msgstr "tekst" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "" + +#: models.py:314 +msgid "blog article" +msgstr "blog objava" + +#: models.py:315 +msgid "blog articles" +msgstr "blog objave" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "" + +#: models.py:493 +msgid "current site" +msgstr "" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "" + +#: models.py:497 +msgid "Plugin template" +msgstr "" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "objave" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "Število najnovejših objav, ki naj bodo prikazane" + +#: models.py:541 +msgid "filter by tag" +msgstr "filtriraj po oznakah" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Prikaži le blog objave označene z izbranimi oznakami" + +#: models.py:549 +msgid "filter by category" +msgstr "filtriraj po kategoriji" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Prikaži le blog objave označene z izbranimi kategorijami" + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "%s najnovejših objav glede na oznako" + +#: models.py:574 +msgid "authors" +msgstr "avtorji" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "Število avtorjevih objav, ki naj bodo prikazane" + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "%s najnovejših objav glede na avtorja" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "generični blog vtičnik" + +#: settings.py:26 +msgid "Full date" +msgstr "Datum (poln)" + +#: settings.py:27 +msgid "Year / Month" +msgstr "Leto / Mesec" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Kategorija" + +#: settings.py:29 +msgid "Just slug" +msgstr "" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "Kategorije in objave" + +#: settings.py:60 +msgid "Categories only" +msgstr "Samo kategorije" + +#: settings.py:61 +msgid "Posts only" +msgstr "Samo objave" + +#: settings.py:62 +msgid "None" +msgstr "" + +#: settings.py:71 +msgid "always" +msgstr "vedno" + +#: settings.py:72 +msgid "hourly" +msgstr "vsako uro" + +#: settings.py:73 +msgid "daily" +msgstr "dnevno" + +#: settings.py:74 +msgid "weekly" +msgstr "tedensko" + +#: settings.py:75 +msgid "monthly" +msgstr "mesečno" + +#: settings.py:76 +msgid "yearly" +msgstr "letno" + +#: settings.py:77 +msgid "never" +msgstr "nikoli" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Najnovejše blog objave" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Avtorjeve blog objave" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Oznake" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Kategorije" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Arhiv" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "preberi več" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "%(articles)s objava" +msgstr[1] "%(articles)s objave" +msgstr[2] "%(articles)s objave" +msgstr[3] "%(articles)s objave" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 objav" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Nič objav" + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Avtorji" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Objave avtorja" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "Nič kategorij" + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Oznaka" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Nazaj" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "prejšnji/a" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Stran" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "od" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "naslednji" diff --git a/djangocms_blog/locale/tr/LC_MESSAGES/django.po b/djangocms_blog/locale/tr/LC_MESSAGES/django.po new file mode 100644 index 00000000..53e33836 --- /dev/null +++ b/djangocms_blog/locale/tr/LC_MESSAGES/django.po @@ -0,0 +1,763 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Rustam Mirzaev , 2016,2018 +# Rustam Mirzaev , 2015 +msgid "" +msgstr "" +"Project-Id-Version: djangocms-blog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 11:04+0200\n" +"PO-Revision-Date: 2014-03-29 15:29+0000\n" +"Last-Translator: Rustam Mirzaev , 2016,2018\n" +"Language-Team: Turkish (http://app.transifex.com/nephila/djangocms-blog/language/tr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: tr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Lokalize 1.5\n" + +#: admin.py:65 +msgid "site" +msgstr "" + +#: admin.py:131 +msgid "Info" +msgstr "" + +#: admin.py:138 +msgid "Images" +msgstr "" + +#: admin.py:141 +msgid "SEO" +msgstr "" + +#: admin.py:172 +msgid "Publish selection" +msgstr "" + +#: admin.py:194 +msgid "Unpublish selection" +msgstr "" + +#: admin.py:207 +msgid "Enable comments for selection" +msgstr "" + +#: admin.py:221 +msgid "Disable comments for selection " +msgstr "" + +#: admin.py:235 +msgid "Enable liveblog for selection" +msgstr "" + +#: admin.py:249 +msgid "Disable liveblog for selection " +msgstr "" + +#: admin.py:473 +msgid "Generic" +msgstr "" + +#: admin.py:485 +msgid "Layout" +msgstr "" + +#: admin.py:499 +msgid "Notifications" +msgstr "" + +#: admin.py:503 +msgid "Sitemap" +msgstr "" + +#: admin.py:512 +msgid "Meta" +msgstr "" + +#: admin.py:524 admin.py:535 admin.py:545 +msgid "You can provide plain strings, Post model attribute or method names" +msgstr "Düz dizgiler, makale modeli özniteliği veya metod adları sağlayabilirsiniz." + +#: apps.py:7 +msgid "django CMS Blog" +msgstr "django CMS Blogu" + +#: cms_appconfig.py:19 +msgid "application title" +msgstr "uygulama başlığı" + +#: cms_appconfig.py:20 +msgid "object name" +msgstr "nesne adı" + +#: cms_appconfig.py:24 +msgid "blog config" +msgstr "blog ayarı" + +#: cms_appconfig.py:25 +msgid "blog configs" +msgstr "blog ayarları" + +#: cms_appconfig.py:28 +msgid "untitled" +msgstr "isimsiz" + +#: cms_appconfig.py:54 +msgid "Post published by default" +msgstr "Varsayılan olarak yayınlanan makale" + +#: cms_appconfig.py:58 +msgid "Default size of full images" +msgstr "" + +#: cms_appconfig.py:61 +msgid "" +"If left empty the image size will have to be set for every newly created " +"post." +msgstr "" + +#: cms_appconfig.py:65 +msgid "Default size of thumbnail images" +msgstr "" + +#: cms_appconfig.py:68 +msgid "" +"If left empty the thumbnail image size will have to be set for every newly " +"created post." +msgstr "" + +#: cms_appconfig.py:72 +msgid "Permalink structure" +msgstr "Kalıcı bağlantı yapısı" + +#: cms_appconfig.py:79 +msgid "Use placeholder and plugins for article body" +msgstr "Makale gövdesi olarak yer tututcu veya eklenti kullan" + +#: cms_appconfig.py:83 +msgid "Use abstract field" +msgstr "Soyut alanı kullan" + +#: cms_appconfig.py:87 +msgid "Enable related posts" +msgstr "" + +#: cms_appconfig.py:91 +msgid "Set author" +msgstr "Yazarı ekle" + +#: cms_appconfig.py:93 +msgid "Set author by default" +msgstr "Varsayılan yazarı ekle" + +#: cms_appconfig.py:98 +msgid "Paginate size" +msgstr "Boyutu sayfala" + +#: cms_appconfig.py:101 +msgid "When paginating list views, how many articles per page?" +msgstr "" + +#: cms_appconfig.py:105 +msgid "Template prefix" +msgstr "" + +#: cms_appconfig.py:108 +msgid "Alternative directory to load the blog templates from" +msgstr "" + +#: cms_appconfig.py:112 +msgid "Menu structure" +msgstr "" + +#: cms_appconfig.py:116 +msgid "Structure of the django CMS menu" +msgstr "" + +#: cms_appconfig.py:120 +msgid "Show empty categories in menu" +msgstr "" + +#: cms_appconfig.py:123 +msgid "Show categories with no post attached in the menu" +msgstr "" + +#: cms_appconfig.py:128 +msgid "Sitemap changefreq" +msgstr "" + +#: cms_appconfig.py:132 +msgid "Changefreq attribute for sitemap items" +msgstr "" + +#: cms_appconfig.py:136 +msgid "Sitemap priority" +msgstr "" + +#: cms_appconfig.py:139 +msgid "Priority attribute for sitemap items" +msgstr "" + +#: cms_appconfig.py:143 +msgid "Object type" +msgstr "" + +#: cms_appconfig.py:147 +msgid "Facebook type" +msgstr "" + +#: cms_appconfig.py:151 +msgid "Facebook application ID" +msgstr "" + +#: cms_appconfig.py:155 +msgid "Facebook profile ID" +msgstr "" + +#: cms_appconfig.py:159 +msgid "Facebook page URL" +msgstr "" + +#: cms_appconfig.py:163 +msgid "Facebook author URL" +msgstr "" + +#: cms_appconfig.py:167 +msgid "Facebook author" +msgstr "" + +#: cms_appconfig.py:171 +msgid "Twitter type" +msgstr "" + +#: cms_appconfig.py:178 +msgid "Twitter site handle" +msgstr "" + +#: cms_appconfig.py:182 +msgid "Twitter author handle" +msgstr "" + +#: cms_appconfig.py:186 +msgid "Schema.org type" +msgstr "" + +#: cms_appconfig.py:193 +msgid "Schema.org author name" +msgstr "" + +#: cms_appconfig.py:197 +msgid "Send notifications on post publish" +msgstr "" + +#: cms_appconfig.py:200 +msgid "Emits a desktop notification -if enabled- when publishing a new post" +msgstr "" + +#: cms_appconfig.py:204 +msgid "Send notifications on post update" +msgstr "" + +#: cms_appconfig.py:207 +msgid "" +"Emits a desktop notification -if enabled- when editing a published post" +msgstr "" + +#: cms_apps.py:13 cms_toolbars.py:17 settings.py:485 +msgid "Blog" +msgstr "Blog" + +#: cms_menus.py:26 +msgid "Blog menu" +msgstr "" + +#: cms_toolbars.py:20 +msgid "Post list" +msgstr "Makale listesi" + +#: cms_toolbars.py:23 +msgid "Category list" +msgstr "" + +#: cms_toolbars.py:26 +msgid "Tag list" +msgstr "" + +#: cms_toolbars.py:29 +msgid "Add post" +msgstr "Makale ekle" + +#: cms_toolbars.py:33 +msgid "Edit configuration" +msgstr "" + +#: cms_toolbars.py:38 +msgid "Edit Post" +msgstr "Makaleyi düzenle" + +#: cms_toolbars.py:53 +#, python-brace-format +msgid "Publish {0} now" +msgstr "" + +#: cms_wizards.py:22 +msgid "Slug" +msgstr "" + +#: cms_wizards.py:25 +msgid "Leave empty for automatic slug, or override as required." +msgstr "" + +#: cms_wizards.py:105 +#, python-brace-format +msgid "New {0}" +msgstr "" + +#: cms_wizards.py:109 +#, python-brace-format +msgid "Create a new {0} in {1}" +msgstr "" + +#: feeds.py:39 +#, python-format +msgid "Blog articles on %(site_name)s" +msgstr "%(site_name)s 'ki blog makaleleri" + +#: liveblog/apps.py:7 +msgid "Liveblog" +msgstr "" + +#: liveblog/cms_plugins.py:13 +msgid "Liveblog item" +msgstr "" + +#: liveblog/models.py:27 +msgid "publish liveblog entry" +msgstr "" + +#: liveblog/models.py:28 +msgid "post date" +msgstr "" + +#: liveblog/models.py:31 liveblog/models.py:111 +msgid "liveblog entry" +msgstr "" + +#: liveblog/models.py:32 liveblog/models.py:112 +msgid "liveblog entries" +msgstr "" + +#: liveblog/models.py:93 models.py:252 +msgid "title" +msgstr "" + +#: liveblog/models.py:95 +msgid "image" +msgstr "" + +#: liveblog/models.py:103 +msgid "thumbnail size" +msgstr "" + +#: models.py:95 +msgid "parent" +msgstr "üst öğe" + +#: models.py:97 +msgid "created at" +msgstr "oluşturma tarihi" + +#: models.py:98 +msgid "modified at" +msgstr "düzenleme tarihi" + +#: models.py:99 models.py:249 models.py:491 +msgid "app. config" +msgstr "" + +#: models.py:102 +msgid "name" +msgstr "isim" + +#: models.py:103 models.py:254 +msgid "slug" +msgstr "okunaklı URL" + +#: models.py:104 +msgid "category meta description" +msgstr "" + +#: models.py:132 +msgid "blog category" +msgstr "blog kategorisi" + +#: models.py:133 +msgid "blog categories" +msgstr "blog kategorileri" + +#: models.py:169 +msgid "BlogCategory (no translation)" +msgstr "" + +#: models.py:196 +msgid "author" +msgstr "" + +#: models.py:203 +msgid "created" +msgstr "" + +#: models.py:204 +msgid "last modified" +msgstr "" + +#: models.py:205 +msgid "published since" +msgstr "" + +#: models.py:206 +msgid "published until" +msgstr "" + +#: models.py:207 +msgid "featured date" +msgstr "" + +#: models.py:208 +msgid "publish" +msgstr "" + +#: models.py:209 +msgid "include in RSS feed" +msgstr "" + +#: models.py:211 +msgid "category" +msgstr "kategori" + +#: models.py:214 +msgid "main image" +msgstr "" + +#: models.py:222 +msgid "main image thumbnail" +msgstr "" + +#: models.py:230 +msgid "main image full" +msgstr "" + +#: models.py:237 +msgid "enable comments on post" +msgstr "" + +#: models.py:241 +msgid "Site(s)" +msgstr "Web site(ler)" + +#: models.py:244 +msgid "" +"Select sites in which to show the post. If none is set it will be visible in" +" all the configured sites." +msgstr "Yayınlamak istediğiniz web siteleri seçiniz. Eğer herhangi seçim yapılmaz ise, yayında olan tüm web sitelerde görünecektir." + +#: models.py:256 +msgid "subtitle" +msgstr "" + +#: models.py:257 +msgid "abstract" +msgstr "" + +#: models.py:258 +msgid "post meta description" +msgstr "" + +#: models.py:259 +msgid "post meta keywords" +msgstr "" + +#: models.py:261 +msgid "post meta title" +msgstr "" + +#: models.py:262 +msgid "used in title tag and social sharing" +msgstr "başlık etiketinde veya sosyal paylaşımda kullanılır" + +#: models.py:267 +msgid "text" +msgstr "" + +#: models.py:273 +msgid "enable liveblog on post" +msgstr "" + +#: models.py:279 +msgid "" +"Type a tag and hit tab, or start typing and select from autocomplete list." +msgstr "" + +#: models.py:282 +msgid "Related Posts" +msgstr "" + +#: models.py:314 +msgid "blog article" +msgstr "blog makalesi" + +#: models.py:315 +msgid "blog articles" +msgstr "blog makaleleri" + +#: models.py:320 +msgid "Post (no translation)" +msgstr "" + +#: models.py:493 +msgid "current site" +msgstr "" + +#: models.py:493 +msgid "Select items from the current site only" +msgstr "" + +#: models.py:497 +msgid "Plugin template" +msgstr "" + +#: models.py:498 +msgid "Select plugin template to load for this instance" +msgstr "" + +#: models.py:536 models.py:578 +msgid "articles" +msgstr "" + +#: models.py:538 +msgid "The number of latests articles to be displayed." +msgstr "Ğörütülenecek olan makale sayısı" + +#: models.py:541 +msgid "filter by tag" +msgstr "" + +#: models.py:543 +msgid "Show only the blog articles tagged with chosen tags." +msgstr "Sadece seçili etiketlerle işaretlenmiş makaleleri göster." + +#: models.py:549 +msgid "filter by category" +msgstr "" + +#: models.py:550 +msgid "Show only the blog articles tagged with chosen categories." +msgstr "Sadece seçili kategorilerde bulunan makaleleri ğöster" + +#: models.py:554 +#, python-format +msgid "%s latest articles by tag" +msgstr "" + +#: models.py:574 +msgid "authors" +msgstr "" + +#: models.py:580 +msgid "The number of author articles to be displayed." +msgstr "İlgili yazarın gösterilecek olan makale sayısı." + +#: models.py:584 +#, python-format +msgid "%s latest articles by author" +msgstr "" + +#: models.py:605 models.py:608 +msgid "Featured posts" +msgstr "" + +#: models.py:623 +msgid "generic blog plugin" +msgstr "" + +#: settings.py:26 +msgid "Full date" +msgstr "" + +#: settings.py:27 +msgid "Year / Month" +msgstr "" + +#: settings.py:28 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Kategori" + +#: settings.py:29 +msgid "Just slug" +msgstr "" + +#: settings.py:59 +msgid "Categories and posts" +msgstr "" + +#: settings.py:60 +msgid "Categories only" +msgstr "" + +#: settings.py:61 +msgid "Posts only" +msgstr "" + +#: settings.py:62 +msgid "None" +msgstr "Yok" + +#: settings.py:71 +msgid "always" +msgstr "her zaman" + +#: settings.py:72 +msgid "hourly" +msgstr "her saat" + +#: settings.py:73 +msgid "daily" +msgstr "her gün" + +#: settings.py:74 +msgid "weekly" +msgstr "her hafta" + +#: settings.py:75 +msgid "monthly" +msgstr "her ay" + +#: settings.py:76 +msgid "yearly" +msgstr "her yıl" + +#: settings.py:77 +msgid "never" +msgstr "" + +#: settings.py:492 +msgid "Latest Blog Articles" +msgstr "Blog'un son makaleleri" + +#: settings.py:499 +msgid "Latest Blog Articles - Cache" +msgstr "" + +#: settings.py:506 +msgid "Author Blog Articles" +msgstr "Blog'un makale yazarı" + +#: settings.py:513 +msgid "Author Blog Articles List" +msgstr "" + +#: settings.py:520 templates/djangocms_blog/plugins/tags.html:5 +msgid "Tags" +msgstr "Etiketler" + +#: settings.py:527 templates/djangocms_blog/plugins/categories.html:3 +msgid "Categories" +msgstr "Kategoriler" + +#: settings.py:534 templates/djangocms_blog/plugins/archive.html:3 +#: templates/djangocms_blog/post_list.html:12 +msgid "Archive" +msgstr "Arşiv" + +#: settings.py:541 +msgid "Featured Posts" +msgstr "" + +#: settings.py:548 +msgid "Featured Posts - Cache" +msgstr "" + +#: settings.py:590 +msgid "Default template" +msgstr "" + +#: templates/djangocms_blog/includes/blog_item.html:32 +msgid "read more" +msgstr "devamını oku" + +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "tarafından" + +#: templates/djangocms_blog/plugins/archive.html:17 +#: templates/djangocms_blog/plugins/authors.html:10 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:12 +#, python-format +msgid "1 article" +msgid_plural "%(articles)s articles" +msgstr[0] "1 makale" +msgstr[1] "%(articles)s makale" + +#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/plugins/authors.html:11 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:13 +msgid "0 articles" +msgstr "0 makale" + +#: templates/djangocms_blog/plugins/archive.html:26 +#: templates/djangocms_blog/plugins/authors_posts.html:12 +#: templates/djangocms_blog/plugins/latest_entries.html:7 +#: templates/djangocms_blog/plugins/tags.html:17 +#: templates/djangocms_blog/post_list.html:21 +msgid "No article found." +msgstr "Makale bulunamadı." + +#: templates/djangocms_blog/plugins/authors.html:3 +#: templates/djangocms_blog/plugins/authors_posts.html:3 +msgid "Authors" +msgstr "Yazarlar" + +#: templates/djangocms_blog/plugins/authors.html:15 +msgid "No author found." +msgstr "" + +#: templates/djangocms_blog/plugins/authors_posts.html:7 +#: templates/djangocms_blog/post_list.html:11 +msgid "Articles by" +msgstr "Makaleler" + +#: templates/djangocms_blog/plugins/categories.html:15 +msgid "No categories found." +msgstr "" + +#: templates/djangocms_blog/post_list.html:13 +msgid "Tag" +msgstr "Etiket" + +#: templates/djangocms_blog/post_list.html:24 +msgid "Back" +msgstr "Geri" + +#: templates/djangocms_blog/post_list.html:29 +msgid "previous" +msgstr "önceki" + +#: templates/djangocms_blog/post_list.html:32 +msgid "Page" +msgstr "Sayfa" + +#: templates/djangocms_blog/post_list.html:32 +msgid "of" +msgstr "biri" + +#: templates/djangocms_blog/post_list.html:35 +msgid "next" +msgstr "sonraki" diff --git a/djangocms_blog/managers.py b/djangocms_blog/managers.py new file mode 100644 index 00000000..5ed465f5 --- /dev/null +++ b/djangocms_blog/managers.py @@ -0,0 +1,197 @@ +from collections import Counter + +from aldryn_apphooks_config.managers.parler import AppHookConfigTranslatableManager, AppHookConfigTranslatableQueryset +from django.contrib.contenttypes.models import ContentType +from django.contrib.sites.models import Site +from django.db import models +from django.utils.timezone import now + + +class TaggedFilterItem: + def tagged(self, other_model=None, queryset=None): + """ + Restituisce una queryset di elementi del model taggati, + o con gli stessi tag di un model o un queryset + """ + tags = self._taglist(other_model, queryset) + return self.get_queryset().filter(tags__in=tags).distinct() + + def _taglist(self, other_model=None, queryset=None): + """ + Restituisce una lista di id di tag comuni al model corrente e al model + o queryset passati come argomento + """ + from taggit.models import TaggedItem + + filters = None + if queryset is not None: + filters = set() + for item in queryset.all(): + filters.update(item.tags.all()) + filters = {tag.id for tag in filters} + elif other_model is not None: + filters = set( + TaggedItem.objects.filter(content_type__model=other_model.__name__.lower()).values_list( + "tag_id", flat=True + ) + ) + tags = set( + TaggedItem.objects.filter(content_type__model=self.model.__name__.lower()).values_list("tag_id", flat=True) + ) + if filters is not None: + tags = tags.intersection(filters) + return list(tags) + + def tag_list(self, other_model=None, queryset=None): + """ + Restituisce un queryset di tag comuni al model corrente e + al model o queryset passati come argomento + """ + from taggit.models import Tag + + return Tag.objects.filter(id__in=self._taglist(other_model, queryset)) + + def tag_list_slug(self, other_model=None, queryset=None): + queryset = self.tag_list(other_model, queryset) + return queryset.values("slug") + + def tag_cloud(self, other_model=None, queryset=None, published=True, on_site=False): + from taggit.models import TaggedItem + + if on_site: + queryset = queryset.on_site() + tag_ids = self._taglist(other_model, queryset) + kwargs = {} + if published: + kwargs = { + "object_id__in": self.model.objects.published(), + "content_type": ContentType.objects.get_for_model(self.model), + } + kwargs["tag_id__in"] = tag_ids + counted_tags = dict( + TaggedItem.objects.filter(**kwargs) + .values("tag") + .annotate(count=models.Count("tag")) + .values_list("tag", "count") + ) + tags = TaggedItem.tag_model().objects.filter(pk__in=counted_tags.keys()) + for tag in tags: + tag.count = counted_tags[tag.pk] + return sorted(tags, key=lambda x: -x.count) + + +class GenericDateQuerySet(AppHookConfigTranslatableQueryset): + start_date_field = "date_published" + fallback_date_field = "date_modified" + end_date_field = "date_published_end" + publish_field = "publish" + + def on_site(self, site=None): + if not site: + site = Site.objects.get_current() + return self.filter(models.Q(sites__isnull=True) | models.Q(sites=site.pk)) + + def published(self, current_site=True): + queryset = self.published_future(current_site) + if self.start_date_field: + return queryset.filter(**{"%s__lte" % self.start_date_field: now()}) + else: + return queryset + + def published_on_rss(self, current_site=True): + queryset = self.published_future(current_site) + return queryset.exclude(include_in_rss=False) + + def published_future(self, current_site=True): + if current_site: + queryset = self.on_site() + else: + queryset = self + if self.end_date_field: + qfilter = models.Q(**{"%s__gte" % self.end_date_field: now()}) | models.Q( + **{"%s__isnull" % self.end_date_field: True} + ) + queryset = queryset.filter(qfilter) + return queryset.filter(**{self.publish_field: True}) + + def archived(self, current_site=True): + if current_site: + queryset = self.on_site() + else: + queryset = self + if self.end_date_field: + qfilter = models.Q(**{"%s__lte" % self.end_date_field: now()}) + queryset = queryset.filter(qfilter) + return queryset.filter(**{self.publish_field: True}) + + def available(self, current_site=True): + if current_site: + return self.on_site().filter(**{self.publish_field: True}) + else: + return self.filter(**{self.publish_field: True}) + + def filter_by_language(self, language, current_site=True): + if current_site: + return self.active_translations(language_code=language).on_site() + else: + return self.active_translations(language_code=language) + + +class GenericDateTaggedManager(TaggedFilterItem, AppHookConfigTranslatableManager): + use_for_related_fields = True + + queryset_class = GenericDateQuerySet + + def get_queryset(self, *args, **kwargs): + return super().get_queryset(*args, **kwargs) + + def published(self, current_site=True): + return self.get_queryset().published(current_site) + + def published_on_rss(self, current_site=True): + return self.get_queryset().published_on_rss(current_site) + + def available(self, current_site=True): + return self.get_queryset().available(current_site) + + def archived(self, current_site=True): + return self.get_queryset().archived(current_site) + + def published_future(self, current_site=True): + return self.get_queryset().published_future(current_site) + + def filter_by_language(self, language, current_site=True): + return self.get_queryset().filter_by_language(language, current_site) + + def on_site(self, site=None): + return self.get_queryset().on_site(site) + + def get_months(self, queryset=None, current_site=True): + """ + Get months with aggregate count (how much posts is in the month). + Results are ordered by date. + """ + if queryset is None: + queryset = self.get_queryset() + if current_site: + queryset = queryset.on_site() + dates_qs = queryset.values_list(queryset.start_date_field, queryset.fallback_date_field) + dates = [] + for blog_dates in dates_qs: + if blog_dates[0]: + current_date = blog_dates[0] + else: + current_date = blog_dates[1] + dates.append( + ( + current_date.year, + current_date.month, + ) + ) + date_counter = Counter(dates) + dates = set(dates) + dates = sorted(dates, reverse=True) + return [ + {"date": now().replace(year=year, month=month, day=1), "count": date_counter[year, month]} + for year, month in dates + ] diff --git a/djangocms_blog/migrations/0001_initial.py b/djangocms_blog/migrations/0001_initial.py new file mode 100644 index 00000000..e9bd088a --- /dev/null +++ b/djangocms_blog/migrations/0001_initial.py @@ -0,0 +1,286 @@ +import cms.models.fields +import django.utils.timezone +import djangocms_text_ckeditor.fields +import filer.fields.image +import meta.models +import taggit_autosuggest.managers +from django.conf import settings +from django.db import migrations, models +from filer.settings import FILER_IMAGE_MODEL + +from djangocms_blog.models import thumbnail_model + +ACTUAL_FILER_IMAGE_MODEL = FILER_IMAGE_MODEL or "filer.Image" + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + migrations.swappable_dependency(ACTUAL_FILER_IMAGE_MODEL), + ("cms", "0020_old_tree_cleanup"), + ("taggit", "__first__"), + ("filer", "0003_thumbnailoption"), + ] + + operations = [ + migrations.CreateModel( + name="AuthorEntriesPlugin", + fields=[ + ( + "cmsplugin_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="cms.CMSPlugin", + on_delete=models.deletion.CASCADE, + ), + ), + ( + "latest_posts", + models.IntegerField( + default=5, help_text="The number of author articles to be displayed.", verbose_name="Articles" + ), + ), + ("authors", models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name="Authors")), + ], + options={ + "abstract": False, + }, + bases=("cms.cmsplugin",), + ), + migrations.CreateModel( + name="BlogCategory", + fields=[ + ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), + ("date_created", models.DateTimeField(auto_now_add=True, verbose_name="created at")), + ("date_modified", models.DateTimeField(auto_now=True, verbose_name="modified at")), + ( + "parent", + models.ForeignKey( + verbose_name="parent", + blank=True, + to="djangocms_blog.BlogCategory", + null=True, + on_delete=models.deletion.CASCADE, + ), + ), + ], + options={ + "verbose_name": "blog category", + "verbose_name_plural": "blog categories", + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name="BlogCategoryTranslation", + fields=[ + ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language", choices=settings.LANGUAGES + ), + ), + ("name", models.CharField(max_length=255, verbose_name="name")), + ("slug", models.SlugField(verbose_name="slug", blank=True)), + ( + "master", + models.ForeignKey( + related_name="translations", + editable=False, + to="djangocms_blog.BlogCategory", + null=True, + on_delete=models.deletion.CASCADE, + ), + ), + ], + options={ + "db_table": "djangocms_blog_blogcategory_translation", + "verbose_name": "blog category Translation", + "default_permissions": (), + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name="LatestPostsPlugin", + fields=[ + ( + "cmsplugin_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="cms.CMSPlugin", + on_delete=models.deletion.CASCADE, + ), + ), + ( + "latest_posts", + models.IntegerField( + default=5, help_text="The number of latests articles to be displayed.", verbose_name="Articles" + ), + ), + ( + "categories", + models.ManyToManyField( + help_text="Show only the blog articles tagged with chosen categories.", + to="djangocms_blog.BlogCategory", + blank=True, + ), + ), + ( + "tags", + models.ManyToManyField( + help_text="Show only the blog articles tagged with chosen tags.", to="taggit.Tag", blank=True + ), + ), + ], + options={ + "abstract": False, + }, + bases=("cms.cmsplugin",), + ), + migrations.CreateModel( + name="Post", + fields=[ + ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), + ("date_created", models.DateTimeField(auto_now_add=True)), + ("date_modified", models.DateTimeField(auto_now=True)), + ( + "date_published", + models.DateTimeField(default=django.utils.timezone.now, verbose_name="Published Since"), + ), + ("date_published_end", models.DateTimeField(null=True, verbose_name="Published Until", blank=True)), + ("publish", models.BooleanField(default=False, verbose_name="Publish")), + ("enable_comments", models.BooleanField(default=True, verbose_name="Enable comments on post")), + ( + "author", + models.ForeignKey( + related_name="djangocms_blog_post_author", + verbose_name="Author", + blank=True, + to=settings.AUTH_USER_MODEL, + null=True, + on_delete=models.deletion.PROTECT, + ), + ), + ( + "categories", + models.ManyToManyField( + related_name="blog_posts", verbose_name="category", to="djangocms_blog.BlogCategory" + ), + ), + ( + "content", + cms.models.fields.PlaceholderField( + slotname="post_content", editable=False, to="cms.Placeholder", null=True + ), + ), + ( + "main_image", + filer.fields.image.FilerImageField( + related_name="djangocms_blog_post_image", + verbose_name="Main image", + blank=True, + to=ACTUAL_FILER_IMAGE_MODEL, + null=True, + on_delete=models.deletion.SET_NULL, + ), + ), + ( + "main_image_full", + models.ForeignKey( + related_name="djangocms_blog_post_full", + verbose_name="Main image full", + blank=True, + to=thumbnail_model, + null=True, + on_delete=models.deletion.SET_NULL, + ), + ), + ( + "main_image_thumbnail", + models.ForeignKey( + related_name="djangocms_blog_post_thumbnail", + verbose_name="Main image thumbnail", + blank=True, + to=thumbnail_model, + null=True, + on_delete=models.deletion.SET_NULL, + ), + ), + ( + "tags", + taggit_autosuggest.managers.TaggableManager( + to="taggit.Tag", + through="taggit.TaggedItem", + blank=True, + help_text="A comma-separated list of tags.", + verbose_name="Tags", + ), + ), + ], + options={ + "ordering": ("-date_published", "-date_created"), + "get_latest_by": "date_published", + "verbose_name": "blog article", + "verbose_name_plural": "blog articles", + }, + bases=(meta.models.ModelMeta, models.Model), + ), + migrations.CreateModel( + name="PostTranslation", + fields=[ + ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language", choices=settings.LANGUAGES + ), + ), + ("title", models.CharField(max_length=255, verbose_name="Title")), + ("slug", models.SlugField(verbose_name="slug", blank=True)), + ("abstract", djangocms_text_ckeditor.fields.HTMLField(verbose_name="Abstract")), + ("meta_description", models.TextField(default="", verbose_name="Post meta description", blank=True)), + ("meta_keywords", models.TextField(default="", verbose_name="Post meta keywords", blank=True)), + ( + "meta_title", + models.CharField( + default="", + help_text="used in title tag and social sharing", + max_length=255, + verbose_name="Post meta title", + blank=True, + ), + ), + ("post_text", djangocms_text_ckeditor.fields.HTMLField(default="", verbose_name="Text", blank=True)), + ( + "master", + models.ForeignKey( + related_name="translations", + editable=False, + to="djangocms_blog.Post", + null=True, + on_delete=models.deletion.CASCADE, + ), + ), + ], + options={ + "db_table": "djangocms_blog_post_translation", + "verbose_name": "blog article Translation", + "default_permissions": (), + }, + bases=(models.Model,), + ), + migrations.AlterUniqueTogether( + name="posttranslation", + unique_together={("language_code", "master"), ("language_code", "slug")}, + ), + migrations.AlterUniqueTogether( + name="blogcategorytranslation", + unique_together={("language_code", "master"), ("language_code", "slug")}, + ), + ] diff --git a/djangocms_blog/migrations/0002_post_sites.py b/djangocms_blog/migrations/0002_post_sites.py new file mode 100644 index 00000000..469d490a --- /dev/null +++ b/djangocms_blog/migrations/0002_post_sites.py @@ -0,0 +1,24 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("sites", "__first__"), + ("djangocms_blog", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="post", + name="sites", + field=models.ManyToManyField( + help_text="Select sites in which to show the post. If none is set it will be " + "visible in all the configured sites.", + to="sites.Site", + null=True, + verbose_name="Site(s", + blank=True, + ), + preserve_default=True, + ), + ] diff --git a/djangocms_blog/migrations/0003_auto_20141201_2252.py b/djangocms_blog/migrations/0003_auto_20141201_2252.py new file mode 100644 index 00000000..8faab95e --- /dev/null +++ b/djangocms_blog/migrations/0003_auto_20141201_2252.py @@ -0,0 +1,23 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0002_post_sites"), + ] + + operations = [ + migrations.AlterField( + model_name="post", + name="sites", + field=models.ManyToManyField( + help_text="Select sites in which to show the post. If none is set it will " + "be visible in all the configured sites.", + to="sites.Site", + null=True, + verbose_name="Site(s)", + blank=True, + ), + preserve_default=True, + ), + ] diff --git a/djangocms_blog/migrations/0004_auto_20150108_1435.py b/djangocms_blog/migrations/0004_auto_20150108_1435.py new file mode 100644 index 00000000..245bb3fb --- /dev/null +++ b/djangocms_blog/migrations/0004_auto_20150108_1435.py @@ -0,0 +1,58 @@ +import django.db.models.deletion +import filer.fields.image +from django.db import migrations, models +from filer.settings import FILER_IMAGE_MODEL + +from djangocms_blog.models import thumbnail_model + +ACTUAL_FILER_IMAGE_MODEL = FILER_IMAGE_MODEL or "filer.Image" + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(ACTUAL_FILER_IMAGE_MODEL), + ("djangocms_blog", "0003_auto_20141201_2252"), + ("filer", "0003_thumbnailoption"), + ] + + operations = [ + migrations.AlterField( + model_name="post", + name="main_image", + field=filer.fields.image.FilerImageField( + related_name="djangocms_blog_post_image", + on_delete=django.db.models.deletion.SET_NULL, + verbose_name="Main image", + blank=True, + to=ACTUAL_FILER_IMAGE_MODEL, + null=True, + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="main_image_full", + field=models.ForeignKey( + related_name="djangocms_blog_post_full", + on_delete=django.db.models.deletion.SET_NULL, + verbose_name="Main image full", + blank=True, + to=thumbnail_model, + null=True, + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="main_image_thumbnail", + field=models.ForeignKey( + related_name="djangocms_blog_post_thumbnail", + on_delete=django.db.models.deletion.SET_NULL, + verbose_name="Main image thumbnail", + blank=True, + to=thumbnail_model, + null=True, + ), + preserve_default=True, + ), + ] diff --git a/djangocms_blog/migrations/0005_auto_20150212_1118.py b/djangocms_blog/migrations/0005_auto_20150212_1118.py new file mode 100644 index 00000000..f449070d --- /dev/null +++ b/djangocms_blog/migrations/0005_auto_20150212_1118.py @@ -0,0 +1,25 @@ +import cms.models.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0004_auto_20150108_1435"), + ] + + operations = [ + migrations.AlterField( + model_name="post", + name="content", + field=cms.models.fields.PlaceholderField( + slotname="post_content", to="cms.Placeholder", null=True, related_name="post_content", editable=False + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="enable_comments", + field=models.BooleanField(default=False, verbose_name="Enable comments on post"), + preserve_default=True, + ), + ] diff --git a/djangocms_blog/migrations/0006_auto_20150214_1907.py b/djangocms_blog/migrations/0006_auto_20150214_1907.py new file mode 100644 index 00000000..98906b61 --- /dev/null +++ b/djangocms_blog/migrations/0006_auto_20150214_1907.py @@ -0,0 +1,17 @@ +import djangocms_text_ckeditor.fields +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0005_auto_20150212_1118"), + ] + + operations = [ + migrations.AlterField( + model_name="posttranslation", + name="abstract", + field=djangocms_text_ckeditor.fields.HTMLField(verbose_name="Abstract", blank=True, default=""), + preserve_default=True, + ), + ] diff --git a/djangocms_blog/migrations/0007_auto_20150719_0933.py b/djangocms_blog/migrations/0007_auto_20150719_0933.py new file mode 100644 index 00000000..3bcd5f3a --- /dev/null +++ b/djangocms_blog/migrations/0007_auto_20150719_0933.py @@ -0,0 +1,216 @@ +import django.db.models.deletion +import django.utils.timezone +import djangocms_text_ckeditor.fields +import filer.fields.image +from django.conf import settings +from django.db import migrations, models +from filer.settings import FILER_IMAGE_MODEL + +from djangocms_blog.models import thumbnail_model + +ACTUAL_FILER_IMAGE_MODEL = FILER_IMAGE_MODEL or "filer.Image" + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0006_auto_20150214_1907"), + migrations.swappable_dependency(ACTUAL_FILER_IMAGE_MODEL), + ("filer", "0003_thumbnailoption"), + ] + + operations = [ + migrations.AlterModelOptions( + name="blogcategorytranslation", + options={"managed": True, "verbose_name": "blog category Translation", "default_permissions": ()}, + ), + migrations.AlterModelOptions( + name="posttranslation", + options={"managed": True, "verbose_name": "blog article Translation", "default_permissions": ()}, + ), + migrations.AlterField( + model_name="authorentriesplugin", + name="authors", + field=models.ManyToManyField(verbose_name="authors", to=settings.AUTH_USER_MODEL), + preserve_default=True, + ), + migrations.AlterField( + model_name="authorentriesplugin", + name="latest_posts", + field=models.IntegerField( + help_text="The number of author articles to be displayed.", verbose_name="articles", default=5 + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="blogcategorytranslation", + name="language_code", + field=models.CharField(db_index=True, verbose_name="Language", max_length=15), + preserve_default=True, + ), + migrations.AlterField( + model_name="latestpostsplugin", + name="categories", + field=models.ManyToManyField( + blank=True, + help_text="Show only the blog articles tagged with chosen categories.", + verbose_name="filter by category", + to="djangocms_blog.BlogCategory", + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="latestpostsplugin", + name="latest_posts", + field=models.IntegerField( + help_text="The number of latests articles to be displayed.", verbose_name="articles", default=5 + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="latestpostsplugin", + name="tags", + field=models.ManyToManyField( + blank=True, + help_text="Show only the blog articles tagged with chosen tags.", + verbose_name="filter by tag", + to="taggit.Tag", + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="author", + field=models.ForeignKey( + blank=True, + verbose_name="author", + to=settings.AUTH_USER_MODEL, + related_name="djangocms_blog_post_author", + null=True, + on_delete=models.deletion.PROTECT, + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="date_created", + field=models.DateTimeField(auto_now_add=True, verbose_name="created"), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="date_modified", + field=models.DateTimeField(auto_now=True, verbose_name="last modified"), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="date_published", + field=models.DateTimeField(verbose_name="published Since", default=django.utils.timezone.now), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="date_published_end", + field=models.DateTimeField(blank=True, verbose_name="published Until", null=True), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="enable_comments", + field=models.BooleanField(verbose_name="enable comments on post", default=True), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="main_image", + field=filer.fields.image.FilerImageField( + on_delete=django.db.models.deletion.SET_NULL, + blank=True, + verbose_name="main image", + to=ACTUAL_FILER_IMAGE_MODEL, + related_name="djangocms_blog_post_image", + null=True, + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="main_image_full", + field=models.ForeignKey( + on_delete=django.db.models.deletion.SET_NULL, + blank=True, + verbose_name="main image full", + to=thumbnail_model, + related_name="djangocms_blog_post_full", + null=True, + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="main_image_thumbnail", + field=models.ForeignKey( + on_delete=django.db.models.deletion.SET_NULL, + blank=True, + verbose_name="main image thumbnail", + to=thumbnail_model, + related_name="djangocms_blog_post_thumbnail", + null=True, + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="post", + name="publish", + field=models.BooleanField(verbose_name="publish", default=False), + preserve_default=True, + ), + migrations.AlterField( + model_name="posttranslation", + name="abstract", + field=djangocms_text_ckeditor.fields.HTMLField(verbose_name="abstract"), + preserve_default=True, + ), + migrations.AlterField( + model_name="posttranslation", + name="language_code", + field=models.CharField(db_index=True, verbose_name="Language", max_length=15), + preserve_default=True, + ), + migrations.AlterField( + model_name="posttranslation", + name="meta_description", + field=models.TextField(blank=True, verbose_name="post meta description", default=""), + preserve_default=True, + ), + migrations.AlterField( + model_name="posttranslation", + name="meta_keywords", + field=models.TextField(blank=True, verbose_name="post meta keywords", default=""), + preserve_default=True, + ), + migrations.AlterField( + model_name="posttranslation", + name="meta_title", + field=models.CharField( + blank=True, + help_text="used in title tag and social sharing", + verbose_name="post meta title", + max_length=255, + default="", + ), + preserve_default=True, + ), + migrations.AlterField( + model_name="posttranslation", + name="post_text", + field=djangocms_text_ckeditor.fields.HTMLField(blank=True, verbose_name="text", default=""), + preserve_default=True, + ), + migrations.AlterField( + model_name="posttranslation", + name="title", + field=models.CharField(verbose_name="title", max_length=255), + preserve_default=True, + ), + ] diff --git a/djangocms_blog/migrations/0008_auto_20150814_0831.py b/djangocms_blog/migrations/0008_auto_20150814_0831.py new file mode 100644 index 00000000..f2f9c74f --- /dev/null +++ b/djangocms_blog/migrations/0008_auto_20150814_0831.py @@ -0,0 +1,17 @@ +import djangocms_text_ckeditor.fields +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0007_auto_20150719_0933"), + ] + + operations = [ + migrations.AlterField( + model_name="posttranslation", + name="abstract", + field=djangocms_text_ckeditor.fields.HTMLField(default=b"", verbose_name="abstract", blank=True), + preserve_default=True, + ), + ] diff --git a/djangocms_blog/migrations/0009_latestpostsplugin_tags_new.py b/djangocms_blog/migrations/0009_latestpostsplugin_tags_new.py new file mode 100644 index 00000000..b52755e5 --- /dev/null +++ b/djangocms_blog/migrations/0009_latestpostsplugin_tags_new.py @@ -0,0 +1,59 @@ +import taggit_autosuggest.managers +from django.contrib.contenttypes.models import ContentType +from django.db import migrations + + +def migrate_tags(apps, schema_editor): + LatestPostsPlugin = apps.get_model("djangocms_blog", "LatestPostsPlugin") + Tag = apps.get_model("taggit", "Tag") + TaggedItem = apps.get_model("taggit", "TaggedItem") + plugin_content_type = ContentType.objects.get_for_model(LatestPostsPlugin) + for tag in Tag.objects.all(): + for plugin in tag.latestpostsplugin_set.all(): + if not TaggedItem.objects.filter( + tag=tag, object_id=plugin.pk, content_type_id=plugin_content_type.pk + ).exists(): + TaggedItem.objects.create(tag=tag, object_id=plugin.pk, content_type_id=plugin_content_type.pk) + + +def migrate_tags_reverse(apps, schema_editor): + LatestPostsPlugin = apps.get_model("djangocms_blog", "LatestPostsPlugin") + apps.get_model("taggit", "Tag") + TaggedItem = apps.get_model("taggit", "TaggedItem") + plugin_content_type = ContentType.objects.get_for_model(LatestPostsPlugin) + for tagged in TaggedItem.objects.filter(content_type_id=plugin_content_type.pk): + post = LatestPostsPlugin.objects.get(pk=tagged.object_id) + post.tags.add(tagged.tag) + tagged.delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("taggit", "__first__"), + ("djangocms_blog", "0008_auto_20150814_0831"), + ] + + operations = [ + migrations.AddField( + model_name="latestpostsplugin", + name="tags_new", + field=taggit_autosuggest.managers.TaggableManager( + to="taggit.Tag", + through="taggit.TaggedItem", + blank=True, + help_text="Show only the blog articles tagged with chosen tags.", + verbose_name="filter by tag", + ), + preserve_default=True, + ), + migrations.RunPython(migrate_tags, migrate_tags_reverse), + migrations.RemoveField( + model_name="latestpostsplugin", + name="tags", + ), + migrations.RenameField( + model_name="latestpostsplugin", + old_name="tags_new", + new_name="tags", + ), + ] diff --git a/djangocms_blog/migrations/0010_auto_20150923_1151.py b/djangocms_blog/migrations/0010_auto_20150923_1151.py new file mode 100644 index 00000000..32503536 --- /dev/null +++ b/djangocms_blog/migrations/0010_auto_20150923_1151.py @@ -0,0 +1,153 @@ +import aldryn_apphooks_config.fields +import app_data.fields +import djangocms_text_ckeditor.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("cms", "0020_old_tree_cleanup"), + ("djangocms_blog", "0009_latestpostsplugin_tags_new"), + ] + + operations = [ + migrations.CreateModel( + name="BlogConfig", + fields=[ + ("id", models.AutoField(auto_created=True, verbose_name="ID", serialize=False, primary_key=True)), + ("type", models.CharField(verbose_name="type", max_length=100)), + ( + "namespace", + models.CharField(default=None, verbose_name="instance namespace", unique=True, max_length=100), + ), + ("app_data", app_data.fields.AppDataField(editable=False, default="{}")), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="BlogConfigTranslation", + fields=[ + ("id", models.AutoField(auto_created=True, verbose_name="ID", serialize=False, primary_key=True)), + ("language_code", models.CharField(db_index=True, verbose_name="Language", max_length=15)), + ("app_title", models.CharField(verbose_name="application title", max_length=234)), + ( + "master", + models.ForeignKey( + editable=False, + to="djangocms_blog.BlogConfig", + related_name="translations", + null=True, + on_delete=models.deletion.CASCADE, + ), + ), + ], + options={ + "verbose_name": "blog config Translation", + "db_table": "djangocms_blog_blogconfig_translation", + "default_permissions": (), + "db_tablespace": "", + "managed": True, + }, + ), + migrations.CreateModel( + name="GenericBlogPlugin", + fields=[ + ( + "cmsplugin_ptr", + models.OneToOneField( + parent_link=True, + serialize=False, + primary_key=True, + auto_created=True, + to="cms.CMSPlugin", + on_delete=models.deletion.CASCADE, + ), + ), + ( + "app_config", + aldryn_apphooks_config.fields.AppHookConfigField( + verbose_name="app. config", + blank=True, + to="djangocms_blog.BlogConfig", + help_text="When selecting a value, the form is reloaded to get the updated default", + ), + ), + ], + options={ + "abstract": False, + }, + bases=("cms.cmsplugin",), + ), + migrations.AlterField( + model_name="posttranslation", + name="abstract", + field=djangocms_text_ckeditor.fields.HTMLField(default="", verbose_name="abstract", blank=True), + ), + migrations.AddField( + model_name="authorentriesplugin", + name="app_config", + field=aldryn_apphooks_config.fields.AppHookConfigField( + default=None, + blank=True, + verbose_name="app. config", + to="djangocms_blog.BlogConfig", + help_text="When selecting a value, the form is reloaded to get the updated default", + null=True, + ), + preserve_default=False, + ), + migrations.AddField( + model_name="blogcategory", + name="app_config", + field=aldryn_apphooks_config.fields.AppHookConfigField( + default=None, + verbose_name="app. config", + to="djangocms_blog.BlogConfig", + help_text="When selecting a value, the form is reloaded to get the updated default", + null=True, + ), + preserve_default=False, + ), + migrations.AddField( + model_name="latestpostsplugin", + name="app_config", + field=aldryn_apphooks_config.fields.AppHookConfigField( + default=None, + blank=True, + verbose_name="app. config", + to="djangocms_blog.BlogConfig", + help_text="When selecting a value, the form is reloaded to get the updated default", + null=True, + ), + preserve_default=False, + ), + migrations.AddField( + model_name="post", + name="app_config", + field=aldryn_apphooks_config.fields.AppHookConfigField( + default=None, + verbose_name="app. config", + to="djangocms_blog.BlogConfig", + help_text="When selecting a value, the form is reloaded to get the updated default", + null=True, + ), + preserve_default=False, + ), + migrations.AlterUniqueTogether( + name="blogconfigtranslation", + unique_together={("language_code", "master")}, + ), + migrations.AlterField( + model_name="post", + name="sites", + field=models.ManyToManyField( + to="sites.Site", + help_text="Select sites in which to show the post. " + "If none is set it will be visible in all the configured sites.", + blank=True, + verbose_name="Site(s)", + ), + ), + ] diff --git a/djangocms_blog/migrations/0011_auto_20151024_1809.py b/djangocms_blog/migrations/0011_auto_20151024_1809.py new file mode 100644 index 00000000..a5fa9551 --- /dev/null +++ b/djangocms_blog/migrations/0011_auto_20151024_1809.py @@ -0,0 +1,84 @@ +import aldryn_apphooks_config.fields +import django.utils.timezone +from django.db import migrations, models + +from djangocms_blog.settings import get_setting + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0010_auto_20150923_1151"), + ] + + operations = [ + migrations.AddField( + model_name="blogconfigtranslation", + name="object_name", + field=models.CharField( + verbose_name="object name", default=get_setting("DEFAULT_OBJECT_NAME"), max_length=234 + ), + ), + migrations.AlterField( + model_name="authorentriesplugin", + name="app_config", + field=aldryn_apphooks_config.fields.AppHookConfigField( + blank=True, + help_text="When selecting a value, the form is reloaded to get the updated default", + to="djangocms_blog.BlogConfig", + verbose_name="app. config", + null=True, + ), + ), + migrations.AlterField( + model_name="blogcategory", + name="app_config", + field=aldryn_apphooks_config.fields.AppHookConfigField( + help_text="When selecting a value, the form is reloaded to get the updated default", + to="djangocms_blog.BlogConfig", + verbose_name="app. config", + null=True, + ), + ), + migrations.AlterField( + model_name="genericblogplugin", + name="app_config", + field=aldryn_apphooks_config.fields.AppHookConfigField( + blank=True, + help_text="When selecting a value, the form is reloaded to get the updated default", + to="djangocms_blog.BlogConfig", + verbose_name="app. config", + null=True, + ), + ), + migrations.AlterField( + model_name="latestpostsplugin", + name="app_config", + field=aldryn_apphooks_config.fields.AppHookConfigField( + blank=True, + help_text="When selecting a value, the form is reloaded to get the updated default", + to="djangocms_blog.BlogConfig", + verbose_name="app. config", + null=True, + ), + ), + migrations.AlterField( + model_name="post", + name="app_config", + field=aldryn_apphooks_config.fields.AppHookConfigField( + help_text="When selecting a value, the form is reloaded to get the updated default", + to="djangocms_blog.BlogConfig", + verbose_name="app. config", + null=True, + ), + ), + migrations.AlterField( + model_name="post", + name="date_published", + field=models.DateTimeField(verbose_name="published since", default=django.utils.timezone.now), + ), + migrations.AlterField( + model_name="post", + name="date_published_end", + field=models.DateTimeField(blank=True, verbose_name="published until", null=True), + ), + ] diff --git a/djangocms_blog/migrations/0012_auto_20151220_1734.py b/djangocms_blog/migrations/0012_auto_20151220_1734.py new file mode 100644 index 00000000..f05ec93a --- /dev/null +++ b/djangocms_blog/migrations/0012_auto_20151220_1734.py @@ -0,0 +1,14 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0011_auto_20151024_1809"), + ] + + operations = [ + migrations.AlterModelOptions( + name="blogconfig", + options={"verbose_name": "blog config", "verbose_name_plural": "blog configs"}, + ), + ] diff --git a/djangocms_blog/migrations/0013_auto_20160201_2235.py b/djangocms_blog/migrations/0013_auto_20160201_2235.py new file mode 100644 index 00000000..d8ec9b1f --- /dev/null +++ b/djangocms_blog/migrations/0013_auto_20160201_2235.py @@ -0,0 +1,19 @@ +# Generated by Django 1.9.2 on 2016-02-01 21:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0012_auto_20151220_1734"), + ] + + operations = [ + migrations.AlterField( + model_name="post", + name="categories", + field=models.ManyToManyField( + blank=True, related_name="blog_posts", to="djangocms_blog.BlogCategory", verbose_name="category" + ), + ), + ] diff --git a/djangocms_blog/migrations/0014_auto_20160215_1331.py b/djangocms_blog/migrations/0014_auto_20160215_1331.py new file mode 100644 index 00000000..bc14a74d --- /dev/null +++ b/djangocms_blog/migrations/0014_auto_20160215_1331.py @@ -0,0 +1,41 @@ +from cms.models import Page +from cms.utils.i18n import get_language_list +from django.db import migrations + + +def forwards(apps, schema_editor): + BlogConfig = apps.get_model("djangocms_blog", "BlogConfig") + BlogConfigTranslation = apps.get_model("djangocms_blog", "BlogConfigTranslation") + Post = apps.get_model("djangocms_blog", "Post") + BlogCategory = apps.get_model("djangocms_blog", "BlogCategory") + GenericBlogPlugin = apps.get_model("djangocms_blog", "GenericBlogPlugin") + LatestPostsPlugin = apps.get_model("djangocms_blog", "LatestPostsPlugin") + AuthorEntriesPlugin = apps.get_model("djangocms_blog", "AuthorEntriesPlugin") + config = None + for page in Page.objects.drafts().filter(application_urls="BlogApp"): + config, created = BlogConfig.objects.get_or_create(namespace=page.application_namespace) + if not BlogConfigTranslation.objects.exists(): + for lang in get_language_list(): + title = page.get_title(lang) + BlogConfigTranslation.objects.create(language_code=lang, master_id=config.pk, app_title=title) + if config: + for model in (Post, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, AuthorEntriesPlugin): + for item in model.objects.filter(app_config__isnull=True): + item.app_config = config + item.save() + + +def backwards(apps, schema_editor): + # No need for backward data migration + pass + + +class Migration(migrations.Migration): + dependencies = [ + ("cms", "0004_auto_20140924_1038"), + ("djangocms_blog", "0013_auto_20160201_2235"), + ] + + operations = [ + migrations.RunPython(forwards, backwards), + ] diff --git a/djangocms_blog/migrations/0015_auto_20160408_1849.py b/djangocms_blog/migrations/0015_auto_20160408_1849.py new file mode 100644 index 00000000..4f1a3c96 --- /dev/null +++ b/djangocms_blog/migrations/0015_auto_20160408_1849.py @@ -0,0 +1,17 @@ +# Generated by Django 1.9.5 on 2016-04-08 16:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0014_auto_20160215_1331"), + ] + + operations = [ + migrations.AlterField( + model_name="post", + name="date_published", + field=models.DateTimeField(blank=True, null=True, verbose_name="published since"), + ), + ] diff --git a/djangocms_blog/migrations/0016_auto_20160502_1741.py b/djangocms_blog/migrations/0016_auto_20160502_1741.py new file mode 100644 index 00000000..26803eee --- /dev/null +++ b/djangocms_blog/migrations/0016_auto_20160502_1741.py @@ -0,0 +1,20 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0015_auto_20160408_1849"), + ] + + operations = [ + migrations.AlterField( + model_name="blogcategorytranslation", + name="slug", + field=models.SlugField(max_length=255, verbose_name="slug", blank=True), + ), + migrations.AlterField( + model_name="posttranslation", + name="slug", + field=models.SlugField(max_length=255, verbose_name="slug", blank=True), + ), + ] diff --git a/djangocms_blog/migrations/0017_thumbnail_move.py b/djangocms_blog/migrations/0017_thumbnail_move.py new file mode 100644 index 00000000..ea49bbab --- /dev/null +++ b/djangocms_blog/migrations/0017_thumbnail_move.py @@ -0,0 +1,10 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("filer", "0003_thumbnailoption"), + ("djangocms_blog", "0016_auto_20160502_1741"), + ] + + operations = [] diff --git a/djangocms_blog/migrations/0018_thumbnail_move2.py b/djangocms_blog/migrations/0018_thumbnail_move2.py new file mode 100644 index 00000000..2ba21a5c --- /dev/null +++ b/djangocms_blog/migrations/0018_thumbnail_move2.py @@ -0,0 +1,45 @@ +from django.db import migrations, models + +from djangocms_blog.models import thumbnail_model + + +class Migration(migrations.Migration): + if "cmsplugin_filer" not in thumbnail_model: + dependencies = [ + ("djangocms_blog", "0017_thumbnail_move"), + ] + + operations = [ + migrations.AddField( + model_name="post", + name="main_image_full_new", + field=models.ForeignKey( + related_name="djangocms_blog_post_full", + verbose_name="Main image full", + blank=True, + to=thumbnail_model, + null=True, + on_delete=models.deletion.SET_NULL, + ), + ), + migrations.AddField( + model_name="post", + name="main_image_thumbnail_new", + field=models.ForeignKey( + related_name="djangocms_blog_post_thumbnail", + verbose_name="Main image thumbnail", + blank=True, + to=thumbnail_model, + null=True, + on_delete=models.deletion.SET_NULL, + ), + ), + ] + else: + dependencies = [ + ("filer", "__first__"), + ("cmsplugin_filer_image", "__first__"), + ("djangocms_blog", "0017_thumbnail_move"), + ] + + operations = [] diff --git a/djangocms_blog/migrations/0019_thumbnail_move3.py b/djangocms_blog/migrations/0019_thumbnail_move3.py new file mode 100644 index 00000000..1266884e --- /dev/null +++ b/djangocms_blog/migrations/0019_thumbnail_move3.py @@ -0,0 +1,38 @@ +from django.db import migrations + +from djangocms_blog.models import thumbnail_model + + +def move_thumbnail_opt_to_filer(apps, schema_editor): + Post = apps.get_model("djangocms_blog", "Post") + for post in Post.objects.all(): + post.main_image_full_new_id = post.main_image_full_id + post.main_image_thumbnail_new_id = post.main_image_thumbnail_id + post.save() + + +def move_thumbnail_opt_to_plugin(apps, schema_editor): + Post = apps.get_model("djangocms_blog", "Post") + for post in Post.objects.all(): + post.main_image_full_id = post.main_image_full_new_id + post.main_image_thumbnail_id = post.main_image_thumbnail_new_id + post.save() + + +class Migration(migrations.Migration): + if "cmsplugin_filer" not in thumbnail_model: + dependencies = [ + ("djangocms_blog", "0018_thumbnail_move2"), + ] + + operations = [ + migrations.RunPython(move_thumbnail_opt_to_filer, move_thumbnail_opt_to_plugin), + ] + else: + dependencies = [ + ("filer", "__first__"), + ("cmsplugin_filer_image", "__first__"), + ("djangocms_blog", "0018_thumbnail_move2"), + ] + + operations = [] diff --git a/djangocms_blog/migrations/0020_thumbnail_move4.py b/djangocms_blog/migrations/0020_thumbnail_move4.py new file mode 100644 index 00000000..82774edd --- /dev/null +++ b/djangocms_blog/migrations/0020_thumbnail_move4.py @@ -0,0 +1,36 @@ +from django.db import migrations + +from djangocms_blog.models import thumbnail_model + + +class Migration(migrations.Migration): + if "cmsplugin_filer" not in thumbnail_model: + dependencies = [ + ("djangocms_blog", "0019_thumbnail_move3"), + ] + + operations = [ + migrations.RemoveField(model_name="post", name="main_image_full"), + migrations.RemoveField( + model_name="post", + name="main_image_thumbnail", + ), + migrations.RenameField( + model_name="post", + old_name="main_image_full_new", + new_name="main_image_full", + ), + migrations.RenameField( + model_name="post", + old_name="main_image_thumbnail_new", + new_name="main_image_thumbnail", + ), + ] + else: + dependencies = [ + ("filer", "__first__"), + ("cmsplugin_filer_image", "__first__"), + ("djangocms_blog", "0019_thumbnail_move3"), + ] + + operations = [] diff --git a/djangocms_blog/migrations/0021_auto_20160823_2008.py b/djangocms_blog/migrations/0021_auto_20160823_2008.py new file mode 100644 index 00000000..4d9efb3f --- /dev/null +++ b/djangocms_blog/migrations/0021_auto_20160823_2008.py @@ -0,0 +1,76 @@ +import django.db.models.deletion +from django.db import migrations, models + +from djangocms_blog.models import thumbnail_model + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0020_thumbnail_move4"), + ] + + operations = [ + migrations.AlterField( + model_name="authorentriesplugin", + name="cmsplugin_ptr", + field=models.OneToOneField( + parent_link=True, + related_name="djangocms_blog_authorentriesplugin", + auto_created=True, + primary_key=True, + serialize=False, + to="cms.CMSPlugin", + on_delete=models.deletion.CASCADE, + ), + ), + migrations.AlterField( + model_name="genericblogplugin", + name="cmsplugin_ptr", + field=models.OneToOneField( + parent_link=True, + related_name="djangocms_blog_genericblogplugin", + auto_created=True, + primary_key=True, + serialize=False, + to="cms.CMSPlugin", + on_delete=models.deletion.CASCADE, + ), + ), + migrations.AlterField( + model_name="latestpostsplugin", + name="cmsplugin_ptr", + field=models.OneToOneField( + parent_link=True, + related_name="djangocms_blog_latestpostsplugin", + auto_created=True, + primary_key=True, + serialize=False, + to="cms.CMSPlugin", + on_delete=models.deletion.CASCADE, + ), + ), + migrations.AlterField( + model_name="post", + name="main_image_full", + field=models.ForeignKey( + related_name="djangocms_blog_post_full", + on_delete=django.db.models.deletion.SET_NULL, + verbose_name="main image full", + blank=True, + to=thumbnail_model, + null=True, + ), + ), + migrations.AlterField( + model_name="post", + name="main_image_thumbnail", + field=models.ForeignKey( + related_name="djangocms_blog_post_thumbnail", + on_delete=django.db.models.deletion.SET_NULL, + verbose_name="main image thumbnail", + blank=True, + to=thumbnail_model, + null=True, + ), + ), + ] diff --git a/djangocms_blog/migrations/0021_post_liveblog.py b/djangocms_blog/migrations/0021_post_liveblog.py new file mode 100644 index 00000000..de21c1c1 --- /dev/null +++ b/djangocms_blog/migrations/0021_post_liveblog.py @@ -0,0 +1,19 @@ +import cms.models.fields +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("cms", "0020_old_tree_cleanup"), + ("djangocms_blog", "0020_thumbnail_move4"), + ] + + operations = [ + migrations.AddField( + model_name="post", + name="liveblog", + field=cms.models.fields.PlaceholderField( + related_name="live_blog", slotname="live_blog", editable=False, to="cms.Placeholder", null=True + ), + ), + ] diff --git a/djangocms_blog/migrations/0022_auto_20160605_2305.py b/djangocms_blog/migrations/0022_auto_20160605_2305.py new file mode 100644 index 00000000..43ccb771 --- /dev/null +++ b/djangocms_blog/migrations/0022_auto_20160605_2305.py @@ -0,0 +1,17 @@ +# Generated by Django 1.9.7 on 2016-06-05 21:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0021_post_liveblog"), + ] + + operations = [ + migrations.AddField( + model_name="post", + name="enable_liveblog", + field=models.BooleanField(default=False, verbose_name="enable liveblog on post"), + ), + ] diff --git a/djangocms_blog/migrations/0022_auto_20170304_1040.py b/djangocms_blog/migrations/0022_auto_20170304_1040.py new file mode 100644 index 00000000..9945d58e --- /dev/null +++ b/djangocms_blog/migrations/0022_auto_20170304_1040.py @@ -0,0 +1,26 @@ +# Generated by Django 1.9.12 on 2017-03-04 09:40 + +from distutils.version import LooseVersion + +import aldryn_apphooks_config +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0021_auto_20160823_2008"), + ] + + if LooseVersion(aldryn_apphooks_config.__version__) > LooseVersion("0.3.0"): + operations = [ + migrations.AlterField( + model_name="blogconfig", + name="namespace", + field=models.CharField(default=None, max_length=100, unique=True, verbose_name="Instance namespace"), + ), + migrations.AlterField( + model_name="blogconfig", + name="type", + field=models.CharField(max_length=100, verbose_name="Type"), + ), + ] diff --git a/djangocms_blog/migrations/0023_auto_20160626_1539.py b/djangocms_blog/migrations/0023_auto_20160626_1539.py new file mode 100644 index 00000000..11f6db46 --- /dev/null +++ b/djangocms_blog/migrations/0023_auto_20160626_1539.py @@ -0,0 +1,33 @@ +# Generated by Django 1.9.6 on 2016-06-26 13:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0022_auto_20160605_2305"), + ] + + operations = [ + migrations.AddField( + model_name="authorentriesplugin", + name="current_site", + field=models.BooleanField( + default=True, help_text="Select items from the current site only", verbose_name="current site" + ), + ), + migrations.AddField( + model_name="genericblogplugin", + name="current_site", + field=models.BooleanField( + default=True, help_text="Select items from the current site only", verbose_name="current site" + ), + ), + migrations.AddField( + model_name="latestpostsplugin", + name="current_site", + field=models.BooleanField( + default=True, help_text="Select items from the current site only", verbose_name="current site" + ), + ), + ] diff --git a/djangocms_blog/migrations/0024_auto_20160706_1524.py b/djangocms_blog/migrations/0024_auto_20160706_1524.py new file mode 100644 index 00000000..267d41ef --- /dev/null +++ b/djangocms_blog/migrations/0024_auto_20160706_1524.py @@ -0,0 +1,47 @@ +from django.db import migrations, models + +from djangocms_blog.settings import get_setting + +BLOG_PLUGIN_TEMPLATE_FOLDERS = get_setting("PLUGIN_TEMPLATE_FOLDERS") + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0023_auto_20160626_1539"), + ] + + operations = [ + migrations.AddField( + model_name="authorentriesplugin", + name="template_folder", + field=models.CharField( + default=BLOG_PLUGIN_TEMPLATE_FOLDERS[0][0], + verbose_name="Plugin template", + max_length=200, + help_text="Select plugin template to load for this instance", + choices=BLOG_PLUGIN_TEMPLATE_FOLDERS, + ), + ), + migrations.AddField( + model_name="genericblogplugin", + name="template_folder", + field=models.CharField( + default=BLOG_PLUGIN_TEMPLATE_FOLDERS[0][0], + verbose_name="Plugin template", + max_length=200, + help_text="Select plugin template to load for this instance", + choices=BLOG_PLUGIN_TEMPLATE_FOLDERS, + ), + ), + migrations.AddField( + model_name="latestpostsplugin", + name="template_folder", + field=models.CharField( + default=BLOG_PLUGIN_TEMPLATE_FOLDERS[0][0], + verbose_name="Plugin template", + max_length=200, + help_text="Select plugin template to load for this instance", + choices=BLOG_PLUGIN_TEMPLATE_FOLDERS, + ), + ), + ] diff --git a/djangocms_blog/migrations/0025_auto_20160803_0858.py b/djangocms_blog/migrations/0025_auto_20160803_0858.py new file mode 100644 index 00000000..755af986 --- /dev/null +++ b/djangocms_blog/migrations/0025_auto_20160803_0858.py @@ -0,0 +1,25 @@ +# Generated by Django 1.9.9 on 2016-08-03 06:58 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0024_auto_20160706_1524"), + ] + + operations = [ + migrations.AlterField( + model_name="blogcategory", + name="parent", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="children", + to="djangocms_blog.BlogCategory", + verbose_name="parent", + ), + ), + ] diff --git a/djangocms_blog/migrations/0026_merge.py b/djangocms_blog/migrations/0026_merge.py new file mode 100644 index 00000000..4c11c80e --- /dev/null +++ b/djangocms_blog/migrations/0026_merge.py @@ -0,0 +1,12 @@ +# Generated by Django 1.9.9 on 2016-08-25 20:24 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0021_auto_20160823_2008"), + ("djangocms_blog", "0025_auto_20160803_0858"), + ] + + operations = [] diff --git a/djangocms_blog/migrations/0027_post_date_featured.py b/djangocms_blog/migrations/0027_post_date_featured.py new file mode 100644 index 00000000..359ae45d --- /dev/null +++ b/djangocms_blog/migrations/0027_post_date_featured.py @@ -0,0 +1,17 @@ +# Generated by Django 1.10.5 on 2017-01-25 11:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0026_merge"), + ] + + operations = [ + migrations.AddField( + model_name="post", + name="date_featured", + field=models.DateTimeField(blank=True, null=True, verbose_name="featured date"), + ), + ] diff --git a/djangocms_blog/migrations/0028_auto_20170304_1040.py b/djangocms_blog/migrations/0028_auto_20170304_1040.py new file mode 100644 index 00000000..151210e8 --- /dev/null +++ b/djangocms_blog/migrations/0028_auto_20170304_1040.py @@ -0,0 +1,26 @@ +# Generated by Django 1.9.12 on 2017-03-04 09:40 + +from distutils.version import LooseVersion + +import aldryn_apphooks_config +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0027_post_date_featured"), + ] + + if LooseVersion(aldryn_apphooks_config.__version__) > LooseVersion("0.3.0"): + operations = [ + migrations.AlterField( + model_name="blogconfig", + name="namespace", + field=models.CharField(default=None, max_length=100, unique=True, verbose_name="Instance namespace"), + ), + migrations.AlterField( + model_name="blogconfig", + name="type", + field=models.CharField(max_length=100, verbose_name="Type"), + ), + ] diff --git a/djangocms_blog/migrations/0029_post_related.py b/djangocms_blog/migrations/0029_post_related.py new file mode 100644 index 00000000..7043ea67 --- /dev/null +++ b/djangocms_blog/migrations/0029_post_related.py @@ -0,0 +1,17 @@ +# Generated by Django 1.10.5 on 2017-04-07 07:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0028_auto_20170304_1040"), + ] + + operations = [ + migrations.AddField( + model_name="post", + name="related", + field=models.ManyToManyField(blank=True, to="djangocms_blog.Post", verbose_name="Related Posts"), + ), + ] diff --git a/djangocms_blog/migrations/0030_auto_20170509_1831.py b/djangocms_blog/migrations/0030_auto_20170509_1831.py new file mode 100644 index 00000000..dcc979c0 --- /dev/null +++ b/djangocms_blog/migrations/0030_auto_20170509_1831.py @@ -0,0 +1,19 @@ +# Generated by Django 1.10.5 on 2017-07-22 05:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0029_post_related"), + ] + + operations = [ + migrations.AlterField( + model_name="posttranslation", + name="slug", + field=models.SlugField( + allow_unicode=True, blank=True, db_index=False, max_length=255, verbose_name="slug" + ), + ), + ] diff --git a/djangocms_blog/migrations/0031_auto_20170610_1744.py b/djangocms_blog/migrations/0031_auto_20170610_1744.py new file mode 100644 index 00000000..956e2824 --- /dev/null +++ b/djangocms_blog/migrations/0031_auto_20170610_1744.py @@ -0,0 +1,21 @@ +# Generated by Django 1.10.5 on 2017-06-10 12:14 + +import sortedm2m.fields +from django.db import migrations +from sortedm2m.operations import AlterSortedManyToManyField + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0030_auto_20170509_1831"), + ] + + operations = [ + AlterSortedManyToManyField( + model_name="post", + name="related", + field=sortedm2m.fields.SortedManyToManyField( + blank=True, help_text=None, to="djangocms_blog.Post", verbose_name="Related Posts" + ), + ), + ] diff --git a/djangocms_blog/migrations/0032_auto_20180109_0023.py b/djangocms_blog/migrations/0032_auto_20180109_0023.py new file mode 100644 index 00000000..c6588027 --- /dev/null +++ b/djangocms_blog/migrations/0032_auto_20180109_0023.py @@ -0,0 +1,50 @@ +# Generated by Django 1.10.8 on 2018-01-08 23:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0031_auto_20170610_1744"), + ] + + operations = [ + migrations.AddField( + model_name="blogcategorytranslation", + name="meta_description", + field=models.TextField(blank=True, default="", verbose_name="post meta description"), + ), + migrations.AlterField( + model_name="blogcategorytranslation", + name="name", + field=models.CharField(max_length=752, verbose_name="name"), + ), + migrations.AlterField( + model_name="blogcategorytranslation", + name="slug", + field=models.SlugField(blank=True, max_length=752, verbose_name="slug"), + ), + migrations.AlterField( + model_name="posttranslation", + name="meta_title", + field=models.CharField( + blank=True, + default="", + help_text="used in title tag and social sharing", + max_length=2000, + verbose_name="post meta title", + ), + ), + migrations.AlterField( + model_name="posttranslation", + name="slug", + field=models.SlugField( + allow_unicode=True, blank=True, db_index=False, max_length=752, verbose_name="slug" + ), + ), + migrations.AlterField( + model_name="posttranslation", + name="title", + field=models.CharField(max_length=752, verbose_name="title"), + ), + ] diff --git a/djangocms_blog/migrations/0033_auto_20180226_1410.py b/djangocms_blog/migrations/0033_auto_20180226_1410.py new file mode 100644 index 00000000..33107ae5 --- /dev/null +++ b/djangocms_blog/migrations/0033_auto_20180226_1410.py @@ -0,0 +1,15 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0032_auto_20180109_0023"), + ] + + operations = [ + migrations.AlterField( + model_name="blogcategorytranslation", + name="meta_description", + field=models.TextField(blank=True, verbose_name="category meta description", default=""), + ), + ] diff --git a/djangocms_blog/migrations/0034_merge.py b/djangocms_blog/migrations/0034_merge.py new file mode 100644 index 00000000..29f0a071 --- /dev/null +++ b/djangocms_blog/migrations/0034_merge.py @@ -0,0 +1,10 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0022_auto_20170304_1040"), + ("djangocms_blog", "0033_auto_20180226_1410"), + ] + + operations = [] diff --git a/djangocms_blog/migrations/0035_posttranslation_subtitle.py b/djangocms_blog/migrations/0035_posttranslation_subtitle.py new file mode 100644 index 00000000..01aad1f6 --- /dev/null +++ b/djangocms_blog/migrations/0035_posttranslation_subtitle.py @@ -0,0 +1,17 @@ +# Generated by Django 1.11 on 2018-04-13 14:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0034_merge"), + ] + + operations = [ + migrations.AddField( + model_name="posttranslation", + name="subtitle", + field=models.CharField(blank=True, default="", max_length=767, verbose_name="subtitle"), + ), + ] diff --git a/djangocms_blog/migrations/0036_auto_20180913_1809.py b/djangocms_blog/migrations/0036_auto_20180913_1809.py new file mode 100644 index 00000000..8b3fe7b5 --- /dev/null +++ b/djangocms_blog/migrations/0036_auto_20180913_1809.py @@ -0,0 +1,17 @@ +# Generated by Django 1.11.15 on 2018-09-13 18:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0035_posttranslation_subtitle"), + ] + + operations = [ + migrations.AlterField( + model_name="posttranslation", + name="slug", + field=models.SlugField(allow_unicode=True, blank=True, max_length=752, verbose_name="slug"), + ), + ] diff --git a/djangocms_blog/migrations/0037_auto_20190806_0743.py b/djangocms_blog/migrations/0037_auto_20190806_0743.py new file mode 100644 index 00000000..13e3501d --- /dev/null +++ b/djangocms_blog/migrations/0037_auto_20190806_0743.py @@ -0,0 +1,82 @@ +# Generated by Django 2.1.11 on 2019-08-06 05:43 + +import django.db.models.deletion +import parler.fields +import taggit_autosuggest.managers +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0036_auto_20180913_1809"), + ] + + operations = [ + migrations.AlterField( + model_name="authorentriesplugin", + name="authors", + field=models.ManyToManyField( + limit_choices_to={"djangocms_blog_post_author__publish": True}, + to=settings.AUTH_USER_MODEL, + verbose_name="authors", + ), + ), + migrations.AlterField( + model_name="blogcategorytranslation", + name="master", + field=parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="djangocms_blog.BlogCategory", + ), + ), + migrations.AlterField( + model_name="blogconfigtranslation", + name="master", + field=parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="djangocms_blog.BlogConfig", + ), + ), + migrations.AlterField( + model_name="latestpostsplugin", + name="tags", + field=taggit_autosuggest.managers.TaggableManager( + blank=True, + help_text="Show only the blog articles tagged with chosen tags.", + related_name="djangocms_blog_latest_post", + through="taggit.TaggedItem", + to="taggit.Tag", + verbose_name="filter by tag", + ), + ), + migrations.AlterField( + model_name="post", + name="tags", + field=taggit_autosuggest.managers.TaggableManager( + blank=True, + help_text="Type a tag and hit tab, or start typing and select from autocomplete list.", + related_name="djangocms_blog_tags", + through="taggit.TaggedItem", + to="taggit.Tag", + verbose_name="Tags", + ), + ), + migrations.AlterField( + model_name="posttranslation", + name="master", + field=parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="djangocms_blog.Post", + ), + ), + ] diff --git a/djangocms_blog/migrations/0038_post_media.py b/djangocms_blog/migrations/0038_post_media.py new file mode 100644 index 00000000..a8490ca6 --- /dev/null +++ b/djangocms_blog/migrations/0038_post_media.py @@ -0,0 +1,27 @@ +# Generated by Django 1.11.24 on 2019-09-14 10:45 + +import cms.models.fields +import django.db.models.deletion +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("cms", "0020_old_tree_cleanup"), + ("djangocms_blog", "0037_auto_20190806_0743"), + ] + + operations = [ + migrations.AddField( + model_name="post", + name="media", + field=cms.models.fields.PlaceholderField( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="media", + slotname="media", + to="cms.Placeholder", + ), + ), + ] diff --git a/djangocms_blog/migrations/0039_auto_20200331_2227.py b/djangocms_blog/migrations/0039_auto_20200331_2227.py new file mode 100644 index 00000000..fa4445c8 --- /dev/null +++ b/djangocms_blog/migrations/0039_auto_20200331_2227.py @@ -0,0 +1,32 @@ +# Generated by Django 2.2.11 on 2020-03-31 20:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0038_post_media"), + ] + + operations = [ + migrations.AlterField( + model_name="blogcategorytranslation", + name="name", + field=models.CharField(max_length=752, verbose_name="name"), + ), + migrations.AlterField( + model_name="blogcategorytranslation", + name="slug", + field=models.SlugField(blank=True, max_length=752, verbose_name="slug"), + ), + migrations.AlterField( + model_name="posttranslation", + name="slug", + field=models.SlugField(allow_unicode=True, blank=True, max_length=752, verbose_name="slug"), + ), + migrations.AlterField( + model_name="posttranslation", + name="title", + field=models.CharField(max_length=752, verbose_name="title"), + ), + ] diff --git a/djangocms_blog/migrations/0040_post_include_in_rss.py b/djangocms_blog/migrations/0040_post_include_in_rss.py new file mode 100644 index 00000000..241bc57a --- /dev/null +++ b/djangocms_blog/migrations/0040_post_include_in_rss.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2 on 2021-10-25 12:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0039_auto_20200331_2227"), + ] + + operations = [ + migrations.AddField( + model_name="post", + name="include_in_rss", + field=models.BooleanField(default=True, verbose_name="include in RSS feed"), + ), + ] diff --git a/djangocms_blog/migrations/0041_auto_20230720_1508.py b/djangocms_blog/migrations/0041_auto_20230720_1508.py new file mode 100644 index 00000000..510230c0 --- /dev/null +++ b/djangocms_blog/migrations/0041_auto_20230720_1508.py @@ -0,0 +1,42 @@ +# Generated by Django 3.2.18 on 2023-07-20 13:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("djangocms_blog", "0040_post_include_in_rss"), + ] + + operations = [ + migrations.AlterField( + model_name="blogcategory", + name="id", + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + migrations.AlterField( + model_name="blogcategorytranslation", + name="id", + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + migrations.AlterField( + model_name="blogconfig", + name="id", + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + migrations.AlterField( + model_name="blogconfigtranslation", + name="id", + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + migrations.AlterField( + model_name="post", + name="id", + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + migrations.AlterField( + model_name="posttranslation", + name="id", + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID"), + ), + ] diff --git a/djangocms_blog/migrations/0042_alter_authorentriesplugin_cmsplugin_ptr_and_more.py b/djangocms_blog/migrations/0042_alter_authorentriesplugin_cmsplugin_ptr_and_more.py new file mode 100644 index 00000000..66924c8f --- /dev/null +++ b/djangocms_blog/migrations/0042_alter_authorentriesplugin_cmsplugin_ptr_and_more.py @@ -0,0 +1,109 @@ +# Generated by Django 4.2.3 on 2023-08-04 09:10 + +import aldryn_apphooks_config.fields +import django.db.models.deletion +import sortedm2m.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("cms", "0022_auto_20180620_1551"), + ("djangocms_blog", "0041_auto_20230720_1508"), + ] + + operations = [ + migrations.AlterField( + model_name="authorentriesplugin", + name="cmsplugin_ptr", + field=models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + related_name="%(app_label)s_%(class)s", + serialize=False, + to="cms.cmsplugin", + ), + ), + migrations.AlterField( + model_name="genericblogplugin", + name="cmsplugin_ptr", + field=models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + related_name="%(app_label)s_%(class)s", + serialize=False, + to="cms.cmsplugin", + ), + ), + migrations.AlterField( + model_name="latestpostsplugin", + name="cmsplugin_ptr", + field=models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + related_name="%(app_label)s_%(class)s", + serialize=False, + to="cms.cmsplugin", + ), + ), + migrations.CreateModel( + name="FeaturedPostsPlugin", + fields=[ + ( + "cmsplugin_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + related_name="%(app_label)s_%(class)s", + serialize=False, + to="cms.cmsplugin", + ), + ), + ( + "current_site", + models.BooleanField( + default=True, help_text="Select items from the current site only", verbose_name="current site" + ), + ), + ( + "template_folder", + models.CharField( + choices=[("plugins", "Default template")], + default="plugins", + help_text="Select plugin template to load for this instance", + max_length=200, + verbose_name="Plugin template", + ), + ), + ( + "app_config", + aldryn_apphooks_config.fields.AppHookConfigField( + blank=True, + help_text="When selecting a value, the form is reloaded to get the updated default", + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="djangocms_blog.blogconfig", + verbose_name="app. config", + ), + ), + ( + "posts", + sortedm2m.fields.SortedManyToManyField( + help_text=None, to="djangocms_blog.post", verbose_name="Featured posts" + ), + ), + ], + options={ + "abstract": False, + }, + bases=("cms.cmsplugin",), + ), + ] diff --git a/djangocms_blog/migrations/__init__.py b/djangocms_blog/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py new file mode 100644 index 00000000..7237421b --- /dev/null +++ b/djangocms_blog/models.py @@ -0,0 +1,637 @@ +import hashlib + +from aldryn_apphooks_config.fields import AppHookConfigField +from aldryn_apphooks_config.managers.parler import AppHookConfigTranslatableManager +from cms.models import CMSPlugin, PlaceholderField +from django.conf import settings as dj_settings +from django.contrib.auth import get_user_model +from django.contrib.sites.shortcuts import get_current_site +from django.core.cache import cache +from django.db import models +from django.db.models.signals import post_save, pre_delete +from django.dispatch import receiver +from django.urls import reverse +from django.utils import timezone +from django.utils.encoding import force_bytes, force_str +from django.utils.functional import cached_property +from django.utils.html import strip_tags +from django.utils.translation import get_language, gettext, gettext_lazy as _ +from djangocms_text_ckeditor.fields import HTMLField +from easy_thumbnails.files import get_thumbnailer +from filer.fields.image import FilerImageField +from filer.models import ThumbnailOption +from meta.models import ModelMeta +from parler.models import TranslatableModel, TranslatedFields +from parler.utils.context import switch_language +from sortedm2m.fields import SortedManyToManyField +from taggit_autosuggest.managers import TaggableManager + +from .cms_appconfig import BlogConfig +from .fields import slugify +from .managers import GenericDateTaggedManager +from .settings import get_setting + +BLOG_CURRENT_POST_IDENTIFIER = get_setting("CURRENT_POST_IDENTIFIER") +BLOG_CURRENT_NAMESPACE = get_setting("CURRENT_NAMESPACE") +BLOG_PLUGIN_TEMPLATE_FOLDERS = get_setting("PLUGIN_TEMPLATE_FOLDERS") + + +thumbnail_model = "{}.{}".format(ThumbnailOption._meta.app_label, ThumbnailOption.__name__) + + +try: + from knocker.mixins import KnockerModel +except ImportError: # pragma: no cover + + class KnockerModel: + """ + Stub class if django-knocker is not installed + """ + + pass + + +def _get_language(instance, language): + available_languages = instance.get_available_languages() + if language and language in available_languages: + return language + language = get_language() + if language and language in available_languages: + return language + language = instance.get_current_language() + if language and language in available_languages: + return language + if get_setting("USE_FALLBACK_LANGUAGE_IN_URL"): + for fallback_language in instance.get_fallback_languages(): + if fallback_language in available_languages: + return fallback_language + return language + + +class BlogMetaMixin(ModelMeta): + def get_meta_attribute(self, param): + """ + Retrieves django-meta attributes from apphook config instance + :param param: django-meta attribute passed as key + """ + return self._get_meta_value(param, getattr(self.app_config, param)) or "" + + def get_locale(self): + return self.get_current_language() + + def get_full_url(self): + """ + Return the url with protocol and domain url + """ + return self.build_absolute_uri(self.get_absolute_url()) + + +class BlogCategory(BlogMetaMixin, TranslatableModel): + """ + Blog category + """ + + parent = models.ForeignKey( + "self", verbose_name=_("parent"), null=True, blank=True, related_name="children", on_delete=models.CASCADE + ) + date_created = models.DateTimeField(_("created at"), auto_now_add=True) + date_modified = models.DateTimeField(_("modified at"), auto_now=True) + app_config = AppHookConfigField(BlogConfig, null=True, verbose_name=_("app. config")) + + translations = TranslatedFields( + name=models.CharField(_("name"), max_length=752), + slug=models.SlugField(_("slug"), max_length=752, blank=True, db_index=True), + meta_description=models.TextField(verbose_name=_("category meta description"), blank=True, default=""), + meta={"unique_together": (("language_code", "slug"),)}, + ) + + objects = AppHookConfigTranslatableManager() + + _metadata = { + "title": "get_title", + "description": "get_description", + "og_description": "get_description", + "twitter_description": "get_description", + "schemaorg_description": "get_description", + "schemaorg_type": "get_meta_attribute", + "locale": "get_locale", + "object_type": "get_meta_attribute", + "og_type": "get_meta_attribute", + "og_app_id": "get_meta_attribute", + "og_profile_id": "get_meta_attribute", + "og_publisher": "get_meta_attribute", + "og_author_url": "get_meta_attribute", + "og_author": "get_meta_attribute", + "twitter_type": "get_meta_attribute", + "twitter_site": "get_meta_attribute", + "twitter_author": "get_meta_attribute", + "url": "get_absolute_url", + } + + class Meta: + verbose_name = _("blog category") + verbose_name_plural = _("blog categories") + + def descendants(self): + children = [] + if self.children.exists(): + children.extend(self.children.all()) + for child in self.children.all(): + children.extend(child.descendants()) + return children + + @cached_property + def linked_posts(self): + return self.blog_posts.namespace(self.app_config.namespace) + + @cached_property + def count(self): + return self.linked_posts.published().count() + + @cached_property + def count_all_sites(self): + return self.linked_posts.published(current_site=False).count() + + def get_absolute_url(self, lang=None): + lang = _get_language(self, lang) + if self.has_translation(lang): + slug = self.safe_translation_getter("slug", language_code=lang) + return reverse( + "%s:posts-category" % self.app_config.namespace, + kwargs={"category": slug}, + current_app=self.app_config.namespace, + ) + # in case category doesn't exist in this language, gracefully fallback + # to posts-latest + return reverse("%s:posts-latest" % self.app_config.namespace, current_app=self.app_config.namespace) + + def __str__(self): + default = gettext("BlogCategory (no translation)") + return self.safe_translation_getter("name", any_language=True, default=default) + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + for lang in self.get_available_languages(): + self.set_current_language(lang) + if not self.slug and self.name: + self.slug = slugify(force_str(self.name)) + self.save_translations() + + def get_title(self): + title = self.safe_translation_getter("name", any_language=True) + return title.strip() + + def get_description(self): + description = self.safe_translation_getter("meta_description", any_language=True) + return strip_tags(description).strip() + + +class Post(KnockerModel, BlogMetaMixin, TranslatableModel): + """ + Blog post + """ + + author = models.ForeignKey( + dj_settings.AUTH_USER_MODEL, + verbose_name=_("author"), + null=True, + blank=True, + related_name="djangocms_blog_post_author", + on_delete=models.PROTECT, + ) + + date_created = models.DateTimeField(_("created"), auto_now_add=True) + date_modified = models.DateTimeField(_("last modified"), auto_now=True) + date_published = models.DateTimeField(_("published since"), null=True, blank=True) + date_published_end = models.DateTimeField(_("published until"), null=True, blank=True) + date_featured = models.DateTimeField(_("featured date"), null=True, blank=True) + publish = models.BooleanField(_("publish"), default=False) + include_in_rss = models.BooleanField(_("include in RSS feed"), default=True) + categories = models.ManyToManyField( + "djangocms_blog.BlogCategory", verbose_name=_("category"), related_name="blog_posts", blank=True + ) + main_image = FilerImageField( + verbose_name=_("main image"), + blank=True, + null=True, + on_delete=models.SET_NULL, + related_name="djangocms_blog_post_image", + ) + main_image_thumbnail = models.ForeignKey( + thumbnail_model, + verbose_name=_("main image thumbnail"), + related_name="djangocms_blog_post_thumbnail", + on_delete=models.SET_NULL, + blank=True, + null=True, + ) + main_image_full = models.ForeignKey( + thumbnail_model, + verbose_name=_("main image full"), + related_name="djangocms_blog_post_full", + on_delete=models.SET_NULL, + blank=True, + null=True, + ) + enable_comments = models.BooleanField( + verbose_name=_("enable comments on post"), default=get_setting("ENABLE_COMMENTS") + ) + sites = models.ManyToManyField( + "sites.Site", + verbose_name=_("Site(s)"), + blank=True, + help_text=_( + "Select sites in which to show the post. " + "If none is set it will be " + "visible in all the configured sites." + ), + ) + app_config = AppHookConfigField(BlogConfig, null=True, verbose_name=_("app. config")) + + translations = TranslatedFields( + title=models.CharField(_("title"), max_length=752), + slug=models.SlugField( + _("slug"), max_length=752, blank=True, db_index=True, allow_unicode=get_setting("UNICODE_SLUGS") + ), + subtitle=models.CharField(verbose_name=_("subtitle"), max_length=767, blank=True, default=""), + abstract=HTMLField(_("abstract"), blank=True, default="", configuration="BLOG_ABSTRACT_CKEDITOR"), + meta_description=models.TextField(verbose_name=_("post meta description"), blank=True, default=""), + meta_keywords=models.TextField(verbose_name=_("post meta keywords"), blank=True, default=""), + meta_title=models.CharField( + verbose_name=_("post meta title"), + help_text=_("used in title tag and social sharing"), + max_length=2000, + blank=True, + default="", + ), + post_text=HTMLField(_("text"), default="", blank=True, configuration="BLOG_POST_TEXT_CKEDITOR"), + meta={"unique_together": (("language_code", "slug"),)}, + ) + media = PlaceholderField("media", related_name="media") + content = PlaceholderField("post_content", related_name="post_content") + liveblog = PlaceholderField("live_blog", related_name="live_blog") + enable_liveblog = models.BooleanField(verbose_name=_("enable liveblog on post"), default=False) + + objects = GenericDateTaggedManager() + tags = TaggableManager( + blank=True, + related_name="djangocms_blog_tags", + help_text=_("Type a tag and hit tab, or start typing and select from autocomplete list."), + ) + + related = SortedManyToManyField("self", verbose_name=_("Related Posts"), blank=True, symmetrical=False) + + _metadata = { + "title": "get_title", + "description": "get_description", + "keywords": "get_keywords", + "og_description": "get_description", + "twitter_description": "get_description", + "schemaorg_description": "get_description", + "locale": "get_locale", + "image": "get_image_full_url", + "image_width": "get_image_width", + "image_height": "get_image_height", + "object_type": "get_meta_attribute", + "og_type": "get_meta_attribute", + "og_app_id": "get_meta_attribute", + "og_profile_id": "get_meta_attribute", + "og_publisher": "get_meta_attribute", + "og_author_url": "get_meta_attribute", + "og_author": "get_meta_attribute", + "twitter_type": "get_meta_attribute", + "twitter_site": "get_meta_attribute", + "twitter_author": "get_meta_attribute", + "schemaorg_type": "get_meta_attribute", + "published_time": "date_published", + "modified_time": "date_modified", + "expiration_time": "date_published_end", + "tag": "get_tags", + "url": "get_absolute_url", + } + + class Meta: + verbose_name = _("blog article") + verbose_name_plural = _("blog articles") + ordering = ("-date_published", "-date_created") + get_latest_by = "date_published" + + def __str__(self): + default = gettext("Post (no translation)") + return self.safe_translation_getter("title", any_language=True, default=default) + + @property + def guid(self, language=None): + if not language: + language = self.get_current_language() + base_string = "-{0}-{2}-{1}-".format( + language, + self.app_config.namespace, + self.safe_translation_getter("slug", language_code=language, any_language=True), + ) + return hashlib.sha256(force_bytes(base_string)).hexdigest() + + @property + def date(self): + if self.date_featured: + return self.date_featured + return self.date_published + + def save(self, *args, **kwargs): + """ + Handle some auto configuration during save + """ + if self.publish and self.date_published is None: + self.date_published = timezone.now() + if not self.slug and self.title: + self.slug = slugify(self.title) + super().save(*args, **kwargs) + + def save_translation(self, translation, *args, **kwargs): + """ + Handle some auto configuration during save + """ + if not translation.slug and translation.title: + translation.slug = slugify(translation.title) + super().save_translation(translation, *args, **kwargs) + + def get_absolute_url(self, lang=None): + lang = _get_language(self, lang) + with switch_language(self, lang): + category = self.categories.first() + kwargs = {} + if self.date_published: + current_date = self.date_published + else: + current_date = self.date_created + urlconf = get_setting("PERMALINK_URLS")[self.app_config.url_patterns] + if "" in urlconf: + kwargs["year"] = current_date.year + if "" in urlconf: + kwargs["month"] = "%02d" % current_date.month + if "" in urlconf: + kwargs["day"] = "%02d" % current_date.day + if "" in urlconf or "" in urlconf: + kwargs["slug"] = self.safe_translation_getter("slug", language_code=lang, any_language=True) # NOQA + if "" in urlconf or "" in urlconf: + kwargs["category"] = category.safe_translation_getter( + "slug", language_code=lang, any_language=True + ) # NOQA + return reverse( + "%s:post-detail" % self.app_config.namespace, kwargs=kwargs, current_app=self.app_config.namespace + ) + + def get_title(self): + title = self.safe_translation_getter("meta_title", any_language=True) + if not title: + title = self.safe_translation_getter("title", any_language=True) + return title.strip() + + def get_keywords(self): + """ + Returns the list of keywords (as python list) + :return: list + """ + return self.safe_translation_getter("meta_keywords", default="").strip().split(",") + + def get_description(self): + description = self.safe_translation_getter("meta_description", any_language=True) + if not description: + description = self.safe_translation_getter("abstract", any_language=True) + return strip_tags(description).strip() + + def get_image_full_url(self): + if self.main_image: + thumbnail_options = get_setting("META_IMAGE_SIZE") + if thumbnail_options: + thumbnail_url = get_thumbnailer(self.main_image).get_thumbnail(thumbnail_options).url + return self.build_absolute_uri(thumbnail_url) + return self.build_absolute_uri(self.main_image.url) + return "" + + def get_image_width(self): + if self.main_image: + thumbnail_options = get_setting("META_IMAGE_SIZE") + if thumbnail_options: + return get_thumbnailer(self.main_image).get_thumbnail(thumbnail_options).width + return self.main_image.width + + def get_image_height(self): + if self.main_image: + thumbnail_options = get_setting("META_IMAGE_SIZE") + if thumbnail_options: + return get_thumbnailer(self.main_image).get_thumbnail(thumbnail_options).height + return self.main_image.height + + def get_tags(self): + """ + Returns the list of object tags as comma separated list + """ + taglist = [tag.name for tag in self.tags.all()] + return ",".join(taglist) + + def get_author(self): + """ + Return the author (user) objects + """ + return self.author + + def _set_default_author(self, current_user): + if not self.author_id and self.app_config.set_author: + if get_setting("AUTHOR_DEFAULT") is True: + user = current_user + else: + user = get_user_model().objects.get(username=get_setting("AUTHOR_DEFAULT")) + self.author = user + + def thumbnail_options(self): + if self.main_image_thumbnail_id: + return self.main_image_thumbnail.as_dict + else: + return get_setting("IMAGE_THUMBNAIL_SIZE") + + def full_image_options(self): + if self.main_image_full_id: + return self.main_image_full.as_dict + else: + return get_setting("IMAGE_FULL_SIZE") + + @property + def is_published(self): + """ + Checks wether the blog post is *really* published by checking publishing dates too + """ + return ( + self.publish + and (self.date_published and self.date_published <= timezone.now()) + and (self.date_published_end is None or self.date_published_end > timezone.now()) + ) + + def should_knock(self, signal_type, created=False): + """ + Returns whether to emit knocks according to the post state + """ + new = self.app_config.send_knock_create and self.is_published and self.date_published == self.date_modified + updated = self.app_config.send_knock_update and self.is_published + return (new or updated) and signal_type in ("post_save", "post_delete") + + def get_cache_key(self, language, prefix): + return "djangocms-blog:{2}:{0}:{1}".format(language, self.guid, prefix) + + @property + def liveblog_group(self): + return "liveblog-{apphook}-{lang}-{post}".format( + lang=self.get_current_language(), + apphook=self.app_config.namespace, + post=self.safe_translation_getter("slug", any_language=True), + ) + + +class BasePostPlugin(CMSPlugin): + app_config = AppHookConfigField(BlogConfig, null=True, verbose_name=_("app. config"), blank=True) + current_site = models.BooleanField( + _("current site"), default=True, help_text=_("Select items from the current site only") + ) + template_folder = models.CharField( + max_length=200, + verbose_name=_("Plugin template"), + help_text=_("Select plugin template to load for this instance"), + default=BLOG_PLUGIN_TEMPLATE_FOLDERS[0][0], + choices=BLOG_PLUGIN_TEMPLATE_FOLDERS, + ) + + class Meta: + abstract = True + + def optimize(self, qs): + """ + Apply select_related / prefetch_related to optimize the view queries + :param qs: queryset to optimize + :return: optimized queryset + """ + return qs.select_related("app_config").prefetch_related( + "translations", "categories", "categories__translations", "categories__app_config" + ) + + def post_queryset(self, request=None, published_only=True, selected_posts=None): + language = get_language() + posts = Post.objects if not selected_posts else selected_posts + if self.app_config: + posts = posts.namespace(self.app_config.namespace) + if self.current_site: + posts = posts.on_site(get_current_site(request)) + posts = posts.active_translations(language_code=language) + if ( + published_only + or not request + or not getattr(request, "toolbar", False) + or not request.toolbar.edit_mode_active + ): + posts = posts.published(current_site=self.current_site) + return self.optimize(posts.all()) + + +class LatestPostsPlugin(BasePostPlugin): + latest_posts = models.IntegerField( + _("articles"), + default=get_setting("LATEST_POSTS"), + help_text=_("The number of latests " "articles to be displayed."), + ) + tags = TaggableManager( + _("filter by tag"), + blank=True, + help_text=_("Show only the blog articles tagged with chosen tags."), + related_name="djangocms_blog_latest_post", + ) + categories = models.ManyToManyField( + "djangocms_blog.BlogCategory", + blank=True, + verbose_name=_("filter by category"), + help_text=_("Show only the blog articles tagged " "with chosen categories."), + ) + + def __str__(self): + return force_str(_("%s latest articles by tag") % self.latest_posts) + + def copy_relations(self, oldinstance): + for tag in oldinstance.tags.all(): + self.tags.add(tag) + for category in oldinstance.categories.all(): + self.categories.add(category) + + def get_posts(self, request, published_only=True): + posts = self.post_queryset(request, published_only) + if self.tags.exists(): + posts = posts.filter(tags__in=list(self.tags.all())) + if self.categories.exists(): + posts = posts.filter(categories__in=list(self.categories.all())) + return self.optimize(posts.distinct())[: self.latest_posts] + + +class AuthorEntriesPlugin(BasePostPlugin): + authors = models.ManyToManyField( + dj_settings.AUTH_USER_MODEL, + verbose_name=_("authors"), + limit_choices_to={"djangocms_blog_post_author__publish": True}, + ) + latest_posts = models.IntegerField( + _("articles"), + default=get_setting("LATEST_POSTS"), + help_text=_("The number of author articles to be displayed."), + ) + + def __str__(self): + return force_str(_("%s latest articles by author") % self.latest_posts) + + def copy_relations(self, oldinstance): + self.authors.set(oldinstance.authors.all()) + + def get_posts(self, request, published_only=True): + posts = self.post_queryset(request, published_only) + return posts + + def get_authors(self, request): + authors = self.authors.all() + for author in authors: + qs = self.get_posts(request).filter(author=author) + # total nb of articles + author.count = qs.count() + # "the number of author articles to be displayed" + author.posts = qs[: self.latest_posts] + return authors + + +class FeaturedPostsPlugin(BasePostPlugin): + posts = SortedManyToManyField(Post, verbose_name=_("Featured posts")) + + def __str__(self): + return force_str(_("Featured posts")) + + def copy_relations(self, oldinstance): + self.posts.set(oldinstance.posts.all()) + + def get_posts(self, request, published_only=True): + posts = self.post_queryset(request, published_only, selected_posts=self.posts.all()) + return posts + + +class GenericBlogPlugin(BasePostPlugin): + class Meta: + abstract = False + + def __str__(self): + return force_str(_("generic blog plugin")) + + +@receiver(pre_delete, sender=Post) +def pre_delete_post(sender, instance, **kwargs): + for language in instance.get_available_languages(): + key = instance.get_cache_key(language, "feed") + cache.delete(key) + + +@receiver(post_save, sender=Post) +def post_save_post(sender, instance, **kwargs): + for language in instance.get_available_languages(): + key = instance.get_cache_key(language, "feed") + cache.delete(key) diff --git a/djangocms_blog/settings.py b/djangocms_blog/settings.py new file mode 100644 index 00000000..b365015c --- /dev/null +++ b/djangocms_blog/settings.py @@ -0,0 +1,635 @@ +""" +List of settings that can be set in project django settings. +""" + +from django.utils.translation import gettext_lazy as _ +from meta import settings as meta_settings + +MENU_TYPE_COMPLETE = "complete" +MENU_TYPE_CATEGORIES = "categories" +MENU_TYPE_POSTS = "posts" +MENU_TYPE_NONE = "none" +DATE_FORMAT = "%a %d %b %Y %H:%M" + +PERMALINK_TYPE_FULL_DATE = "full_date" +PERMALINK_TYPE_SHORT_DATE = "short_date" +PERMALINK_TYPE_CATEGORY = "category" +PERMALINK_TYPE_SLUG = "slug" + +BLOG_DEFAULT_PUBLISHED = False +""" +.. _DEFAULT_PUBLISHED: + +Default post published status. +""" + +PERMALINKS = ( # noqa + (PERMALINK_TYPE_FULL_DATE, _("Full date")), + (PERMALINK_TYPE_SHORT_DATE, _("Year / Month")), + (PERMALINK_TYPE_CATEGORY, _("Category")), + (PERMALINK_TYPE_SLUG, _("Just slug")), +) +""" +.. _PERMALINKS: + +Permalinks styles. +""" + +BLOG_UNICODE_SLUGS = True +""" +.. _UNICODE_SLUGS: + +Allow unicode chars in auto-generated slugs. +""" + + +PERMALINKS_URLS = { # noqa + PERMALINK_TYPE_FULL_DATE: "////", + PERMALINK_TYPE_SHORT_DATE: "///", + PERMALINK_TYPE_CATEGORY: "//", + PERMALINK_TYPE_SLUG: "/", +} + +""" +.. _PERMALINKS_URLS: + +Permalinks urlconfs. +""" + +MENU_TYPES = ( # noqa + (MENU_TYPE_COMPLETE, _("Categories and posts")), + (MENU_TYPE_CATEGORIES, _("Categories only")), + (MENU_TYPE_POSTS, _("Posts only")), + (MENU_TYPE_NONE, _("None")), +) +""" +.. _DEFAULT_MENU_TYPES: + +Types of menu structure. +""" + +SITEMAP_CHANGEFREQ_LIST = ( # noqa + ("always", _("always")), + ("hourly", _("hourly")), + ("daily", _("daily")), + ("weekly", _("weekly")), + ("monthly", _("monthly")), + ("yearly", _("yearly")), + ("never", _("never")), +) +""" +.. _SITEMAP_CHANGEFREQ_LIST: + +List of changefreqs defined in sitemaps. +""" + +BLOG_IMAGE_THUMBNAIL_SIZE = {"size": "120x120", "crop": True, "upscale": False} +""" +.. _IMAGE_THUMBNAIL_SIZE: + +Easy-thumbnail alias configuration for the post main image when shown on the post lists; +it's a dictionary with ``size``, ``crop`` and ``upscale`` keys. +""" + +BLOG_IMAGE_FULL_SIZE = {"size": "640", "crop": True, "upscale": False} +""" +.. _IMAGE_FULL_SIZE: + +Easy-thumbnail alias configuration for the post main image when shown on the post detail; +it's a dictionary with ``size``, ``crop`` and ``upscale`` keys. +""" + +BLOG_META_IMAGE_SIZE = None +""" +.. _META_IMAGE_SIZE: + +Easy-thumbnail alias configuration for the post meta image; +it's a dictionary with ``size``, ``crop`` and ``upscale`` keys. +Recommended values are {"size": (1200, 630), "crop": True, "upscale": False} +""" + +BLOG_URLCONF = "djangocms_blog.urls" +""" +.. _URLCONF: + +Standard Apphook URLConf. +""" + +BLOG_PAGINATION = 10 +""" +.. _PAGINATION: + +Number of post per page. +""" + +BLOG_LATEST_POSTS = 5 +""" +.. _LATEST_POSTS: + +Default number of post in the **Latest post** plugin. +""" + +BLOG_POSTS_LIST_TRUNCWORDS_COUNT = 100 +""" +.. _POSTS_LIST_TRUNCWORDS_COUNT: + +Default number of words shown for abstract in the post list. +""" + +BLOG_META_DESCRIPTION_LENGTH = 320 +""" +.. _META_DESCRIPTION_LENGTH: + +Maximum length for the Meta description field. +""" + +BLOG_META_TITLE_LENGTH = 70 +""" +.. _META_TITLE_LENGTH: + +Maximum length for the Meta title field. +""" + +BLOG_MENU_TYPES = MENU_TYPES +""" +.. _MENU_TYPES: + +List of available menu types. +""" + +BLOG_MENU_EMPTY_CATEGORIES = True +""" +.. _MENU_EMPTY_CATEGORIES: + +Flag to show / hide categories without posts attached from the menu. +""" + +BLOG_TYPE = "Article" +""" +.. _TYPE: + +Generic type for the post object. +""" + +BLOG_TYPES = meta_settings.OBJECT_TYPES +""" +.. _TYPES: + +Choices of available blog types. + +Available values are defined in to ``META_OBJECT_TYPES`` defined in `django-meta settings`_. +""" + +BLOG_FB_TYPE = "Article" +""" +.. _FB_TYPE: + +Open Graph type for the post object. +""" + +BLOG_FB_TYPES = meta_settings.FB_TYPES +""" +.. _FB_TYPES: + +Choices of available blog types. + +Available values are defined in to ``META_FB_TYPES`` defined in `django-meta settings`_. +""" + +BLOG_FB_APPID = meta_settings.get_setting("FB_APPID") +""" +.. _FB_APPID: + +Facebook Application ID. + +Default from ``FB_APPID`` defined in `django-meta settings`_. +""" + +BLOG_FB_PROFILE_ID = meta_settings.get_setting("FB_PROFILE_ID") +""" +.. _FB_PROFILE_ID: + +Facebook profile ID of the post author. + +Default from ``FB_PROFILE_ID`` defined in `django-meta settings`_. +""" + +BLOG_FB_PUBLISHER = meta_settings.get_setting("FB_PUBLISHER") +""" +.. _FB_PUBLISHER: + +Facebook URL of the blog publisher. + +Default from ``FB_PUBLISHER`` defined in `django-meta settings`_. +""" + +BLOG_FB_AUTHOR_URL = "get_author_url" +""" +.. _FB_AUTHOR_URL: + +Facebook profile URL of the post author. +""" + +BLOG_FB_AUTHOR = "get_author_name" +""" +.. _FB_AUTHOR: + +Facebook profile URL of the post author. +""" + +BLOG_TWITTER_TYPE = "summary" +""" +.. _TWITTER_TYPE: + +Twitter Card type for the post object. +""" + +BLOG_TWITTER_TYPES = meta_settings.TWITTER_TYPES +""" +.. _TWITTER_TYPES: + +Choices of available blog types for twitter. + +Default from ``TWITTER_TYPES`` defined in `django-meta settings`_. +""" + +BLOG_TWITTER_SITE = meta_settings.get_setting("TWITTER_SITE") +""" +.. _TWITTER_SITE: + +Twitter account of the site. + +Default from ``TWITTER_SITE`` defined in `django-meta settings`_. +""" + +BLOG_TWITTER_AUTHOR = "get_author_twitter" +""" +.. _TWITTER_AUTHOR: + +Twitter account of the post author. +""" + +BLOG_SCHEMAORG_TYPE = "Blog" +""" +.. _SCHEMAORG_TYPE: + +Schema.org type for the post object. +""" + +BLOG_SCHEMAORG_TYPES = meta_settings.SCHEMAORG_TYPES +""" +.. _SCHEMAORG_TYPES: + +Choices of available Schema.org types. + +Default from ``SCHEMAORG_TYPES`` defined in `django-meta settings`_. +""" + +BLOG_SCHEMAORG_AUTHOR = "get_author_schemaorg" +""" +.. _SCHEMAORG_AUTHOR: + +Google+ account of the post author (deprecated). +""" + +BLOG_ENABLE_COMMENTS = True +""" +.. _ENABLE_COMMENTS: + +Whether to enable comments by default on posts + +While ``djangocms_blog`` does not ship any comment system, this flag +can be used to control the chosen comments framework. +""" + +BLOG_USE_ABSTRACT = True +""" +.. _USE_ABSTRACT: + +Use an abstract field for the post. + +If ``False`` no abstract field is available for posts. +""" + +BLOG_USE_PLACEHOLDER = True +""" +.. _USE_PLACEHOLDER: + +Post content is managed via placeholder + +If ``False`` a HTMLField is provided instead. +""" + +BLOG_USE_RELATED = True +""" +.. _USE_RELATED: + +Enable related posts to link one post to others. +""" + +BLOG_MULTISITE = True +""" +.. _MULTISITE: + +Add support for multisite setup. +""" + +BLOG_AUTHOR_DEFAULT = True +""" +.. _AUTHOR_DEFAULT: + +Use a default if not specified: + +* ``True``: the current user is set as the default author; +* ``False``: no default author is set; +* any other string: the user with the provided username is used; +""" + +BLOG_ADMIN_POST_FIELDSET_FILTER = False +""" +.. _ADMIN_POST_FIELDSET_FILTER: + +Callable function to change (add or filter) fields to fieldsets for admin post edit form. + +See :ref:`admin_filter_function` for more details. +""" + +BLOG_AVAILABLE_PERMALINK_STYLES = PERMALINKS +""" +.. _AVAILABLE_PERMALINK_STYLES: + +Choices of permalinks styles. +""" + +BLOG_PERMALINK_URLS = PERMALINKS_URLS +""" +.. _PERMALINK_URLS: + +URLConf corresponding to :ref:`BLOG_AVAILABLE_PERMALINK_STYLES `. +""" + +BLOG_DEFAULT_OBJECT_NAME = "Article" +""" +.. _DEFAULT_OBJECT_NAME: + +Default label for Blog item (used in django CMS Wizard). +""" + +BLOG_AUTO_SETUP = True +""" +.. _AUTO_SETUP: + +Enable the blog :ref:`auto_setup` feature. +""" + +BLOG_AUTO_HOME_TITLE = "Home" +""" +.. _AUTO_HOME_TITLE: + +Title of the home page created by :ref:`auto_setup`. +""" + +BLOG_AUTO_BLOG_TITLE = "Blog" +""" +.. _AUTO_BLOG_TITLE: + +Title of the blog page created by :ref:`auto_setup`. +""" + +BLOG_AUTO_APP_TITLE = "Blog" +""" +.. _AUTO_APP_TITLE: + +Title of the ``BlogConfig`` instance created by :ref:`auto_setup`. +""" + +BLOG_AUTO_NAMESPACE = "Blog" +""" +.. _AUTO_NAMESPACE: + +Namespace of the ``BlogConfig`` instance created by :ref:`auto_setup`. +""" + +BLOG_SITEMAP_PRIORITY_DEFAULT = "0.5" +""" +.. _SITEMAP_PRIORITY_DEFAULT: + +Default priority for sitemap items. +""" + +BLOG_SITEMAP_CHANGEFREQ = SITEMAP_CHANGEFREQ_LIST +""" +.. _SITEMAP_CHANGEFREQ: + +List for available changefreqs for sitemap items. +""" + +BLOG_SITEMAP_CHANGEFREQ_DEFAULT = "monthly" +""" +.. _SITEMAP_CHANGEFREQ_DEFAULT: + +Default changefreq for sitemap items. +""" + +BLOG_ABSTRACT_CKEDITOR = True +""" +.. _ABSTRACT_CKEDITOR: + +Configuration for the CKEditor of the abstract field. + +See https://github.com/divio/djangocms-text-ckeditor/#customizing-htmlfield-editor for details. +""" + +BLOG_POST_TEXT_CKEDITOR = True +""" +.. _POST_TEXT_CKEDITOR: + +Configuration for the CKEditor of the post content field. + +See https://github.com/divio/djangocms-text-ckeditor/#customizing-htmlfield-editor for details. +""" + +BLOG_ENABLE_SEARCH = True +""" +.. _ENABLE_SEARCH: + +Enable ``aldryn-search`` (i.e.: ``django-haystack``) indexes. +""" + +BLOG_CURRENT_POST_IDENTIFIER = "djangocms_post_current" +""" +.. _CURRENT_POST_IDENTIFIER: + +Current post identifier in request. + +Name of the request attribute used in :py:class:`djangocms_blog.cms_toolbars.BlogToolbar` to detect if +request match a post detail. +""" + +BLOG_CURRENT_NAMESPACE = "djangocms_post_current_config" +""" +.. _CURRENT_NAMESPACE: + +Current post config identifier in request. + +Name of the request attribute used in :py:class:`djangocms_blog.cms_toolbars.BlogToolbar` to detect the +current apphook namespace. +""" + +BLOG_ENABLE_THROUGH_TOOLBAR_MENU = False +""" +.. _ENABLE_THROUGH_TOOLBAR_MENU: + +Show djangocms-blog toolbar in any page, even when outside the blog apphooks. +""" + +BLOG_PLUGIN_MODULE_NAME = _("Blog") +""" +.. _PLUGIN_MODULE_NAME: + +Name of the djangocms-blog plugins module (group). +""" + +BLOG_LATEST_ENTRIES_PLUGIN_NAME = _("Latest Blog Articles") +""" +.. _LATEST_ENTRIES_PLUGIN_NAME: + +Name of the plugin showing the list of latest posts. +""" + +BLOG_LATEST_ENTRIES_PLUGIN_NAME_CACHED = _("Latest Blog Articles - Cache") +""" +.. _LATEST_ENTRIES_PLUGIN_NAME_CACHED: + +Name of the plugin showing the list of latest posts (cached version). +""" + +BLOG_AUTHOR_POSTS_PLUGIN_NAME = _("Author Blog Articles") +""" +.. _AUTHOR_POSTS_PLUGIN_NAME: + +Name of the plugin showing the list of blog posts authors. +""" + +BLOG_AUTHOR_POSTS_LIST_PLUGIN_NAME = _("Author Blog Articles List") +""" +.. _AUTHOR_POSTS_LIST_PLUGIN_NAME: + +Name of the plugin showing the list of posts per authors. +""" + +BLOG_TAGS_PLUGIN_NAME = _("Tags") +""" +.. _TAGS_PLUGIN_NAME: + +Name of the plugin showing the tag blog cloud. +""" + +BLOG_CATEGORY_PLUGIN_NAME = _("Categories") +""" +.. _CATEGORY_PLUGIN_NAME: + +Name of the plugin showing the list of blog categories. +""" + +BLOG_ARCHIVE_PLUGIN_NAME = _("Archive") +""" +.. _ARCHIVE_PLUGIN_NAME: + +Name of the plugin showing the blog archive index. +""" + +BLOG_FEATURED_POSTS_PLUGIN_NAME = _("Featured Posts") +""" +.. _FEATURED_POSTS_PLUGIN_NAME: + +Name of the plugin showing the selected posts. +""" + +BLOG_FEATURED_POSTS_PLUGIN_NAME_CACHED = _("Featured Posts - Cache") +""" +.. _FEATURED_POSTS_PLUGIN_NAME_CACHED: + +Name of the plugin showing the selected posts (cached version). +""" + +BLOG_FEED_CACHE_TIMEOUT = 3600 +""" +.. _FEED_CACHE_TIMEOUT: + +Cache timeout for RSS feeds. +""" + +BLOG_FEED_INSTANT_ITEMS = 50 +""" +.. _FEED_INSTANT_ITEMS: + +Number of items in Instant Article feed. +""" + +BLOG_FEED_LATEST_ITEMS = 10 +""" +.. _FEED_LATEST_ITEMS: + +Number of items in latest items feed. +""" + +BLOG_FEED_TAGS_ITEMS = 10 +""" +.. _FEED_TAGS_ITEMS: + +Number of items in per tags feed. +""" + +BLOG_LIVEBLOG_PLUGINS = ("LiveblogPlugin",) +""" +.. _LIVEBLOG_PLUGINS: + +List of plugins implementing the :ref:`liveblog` feature. +""" + +BLOG_PLUGIN_TEMPLATE_FOLDERS = (("plugins", _("Default template")),) +""" +.. _PLUGIN_TEMPLATE_FOLDERS: + +(Sub-)folder from which the plugin templates are loaded. + +The default folder is ``plugins``. + +See :ref:`plugin_templates` for more details. +""" + +BLOG_USE_FALLBACK_LANGUAGE_IN_URL = False +""" +.. _USE_FALLBACK_LANGUAGE_IN_URL: + +When displaying URL, prefer URL in the fallback language if an article or category is not available in the +current language. +""" + +BLOG_WIZARD_CONTENT_PLUGIN = "TextPlugin" +""" +.. _WIZARD_CONTENT_PLUGIN: + +Name of the plugin created by wizard for the text content. +""" + +BLOG_WIZARD_CONTENT_PLUGIN_BODY = "body" +""" +.. _WIZARD_CONTENT_PLUGIN_BODY: + +Name of the plugin field to add wizard text. +""" + +params = {param: value for param, value in locals().items() if param.startswith("BLOG_")} + +""" +.. _django-meta settings: https://github.com/nephila/django-meta#settings +""" + + +def get_setting(name): + """Get setting value from django settings with fallback to globals defaults.""" + from django.conf import settings + + return getattr(settings, "BLOG_%s" % name, params["BLOG_%s" % name]) diff --git a/djangocms_blog/sitemaps/__init__.py b/djangocms_blog/sitemaps/__init__.py new file mode 100644 index 00000000..bea10b9e --- /dev/null +++ b/djangocms_blog/sitemaps/__init__.py @@ -0,0 +1,51 @@ +from cms.utils import get_language_list +from django.contrib.sitemaps import Sitemap +from django.urls.exceptions import NoReverseMatch +from parler.utils.context import smart_override + +from ..models import Post +from ..settings import get_setting + + +class BlogSitemap(Sitemap): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.url_cache = {} + + def priority(self, obj): + if obj and obj.app_config: + return obj.app_config.sitemap_priority + return get_setting("SITEMAP_PRIORITY_DEFAULT") + + def changefreq(self, obj): + if obj and obj.app_config: + return obj.app_config.sitemap_changefreq + return get_setting("SITEMAP_CHANGEFREQ_DEFAULT") + + def location(self, obj): + with smart_override(obj.get_current_language()): + return self.url_cache[obj.get_current_language()][obj] + + def items(self): + items = [] + self.url_cache.clear() + for lang in get_language_list(): + self.url_cache[lang] = {} + posts = Post.objects.translated(lang).language(lang).published() + for post in posts: + # check if the post actually has a url before appending + # if a post is published but the associated app config is not + # then this post will not have a url + try: + with smart_override(post.get_current_language()): + self.url_cache[lang][post] = post.get_absolute_url() + except NoReverseMatch: + # couldn't determine the url of the post so pass on it + continue + + items.append(post) + + return items + + def lastmod(self, obj): + return obj.date_modified diff --git a/djangocms_blog/static/djangocms_blog/css/djangocms_blog.css b/djangocms_blog/static/djangocms_blog/css/djangocms_blog.css new file mode 100644 index 00000000..e69de29b diff --git a/djangocms_blog/static/djangocms_blog/css/djangocms_blog_admin.css b/djangocms_blog/static/djangocms_blog/css/djangocms_blog_admin.css new file mode 100644 index 00000000..476fe5e0 --- /dev/null +++ b/djangocms_blog/static/djangocms_blog/css/djangocms_blog_admin.css @@ -0,0 +1,58 @@ +.select2-container-multi .select2-choices .select2-search-field input { + min-width: 100px; +} + +.selector select[multiple] { + display: block; +} + +.selector { + width: auto; + margin-right: 12px; +} + +.selector-available, .selector-chosen { + width: auto; +} + +.selector .selector-available input, .selector .selector-chosen input { + margin: 0; +} + +form select { + max-width: 266px; +} + +.as-selections.vTextField { + width: 100%; +} + +.as-results ul.as-list { + margin-left: 0; + margin-top: 0; + padding-left: 0; + box-shadow: none !important; + border: 1px solid #888888 !important; +} +.as-results ul.as-list li { + list-style: none; +} +ul.as-selections.vTextField { + padding-left: 10px; + margin-left: 0; + margin-bottom: 0; +} + +ul.as-selections { + border-top-width: 1px; +} + +ul.as-selections li { + list-style: none; + padding: 0; +} + +ul.as-selections li.as-original { + border-top: 0; + width: 100%; +} diff --git a/djangocms_blog/taggit_urls.py b/djangocms_blog/taggit_urls.py new file mode 100644 index 00000000..de43fd65 --- /dev/null +++ b/djangocms_blog/taggit_urls.py @@ -0,0 +1,7 @@ +# This file is used on divio cloud only during automatic setup +from django.urls import path # pragma: no cover +from django.urls import include + +urlpatterns = [ # pragma: no cover + path("taggit_autosuggest/", include("taggit_autosuggest.urls")), +] diff --git a/djangocms_blog/templates/djangocms_blog/base.html b/djangocms_blog/templates/djangocms_blog/base.html new file mode 100644 index 00000000..d034e396 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/base.html @@ -0,0 +1,22 @@ +{% extends CMS_TEMPLATE %} + +{% block meta %} + {% comment %} + This is needed if you can't add ``{% include "meta/meta.html" %}`` in the django CMS template attached to the blog + page or if you use djangocms-page-meta. + + Please check the documentation at https://djangocms-blog.readthedocs.io/en/latest/features/meta.html for more + details about the correct template configuration for social media tags rendering. + {% endcomment %} + {% if meta %} + {% include "meta/meta.html" %} + {% else %} + {{ block.super }} + {% endif %} +{% endblock meta %} + +{% block content %} +
+ {% block content_blog %}{% endblock %} +
+{% endblock content %} diff --git a/djangocms_blog/templates/djangocms_blog/includes/blog_item.html b/djangocms_blog/templates/djangocms_blog/includes/blog_item.html new file mode 100644 index 00000000..2f4ada3f --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/includes/blog_item.html @@ -0,0 +1,34 @@ +{% load djangocms_blog i18n easy_thumbnails_tags cms_tags %} + +
+
+

{{ post.title }}

+ {% if post.subtitle %} +

{{ post.subtitle }}

+ {% endif %} + {% block blog_meta %} + {% include "djangocms_blog/includes/blog_meta.html" %} + {% endblock %} +
+ {% if image and post.main_image %} +
+ {% thumbnail post.main_image post.thumbnail_options.size crop=post.thumbnail_options.crop upscale=post.thumbnail_options.upscale subject_location=post.main_image.subject_location as thumb %} + {{ post.main_image.default_alt_text }} +
+ {% else %} + {% media_images post as previews %} +
+ {% for preview in previews %}{% endfor %} +
+ {% endif %} +
+ {% if not TRUNCWORDS_COUNT %} + {% render_model post "abstract" "" "" "safe" %} + {% else %} + {% render_model post "abstract" "" "" "truncatewords_html:TRUNCWORDS_COUNT|safe" %} + {% endif %} +
+ +
diff --git a/djangocms_blog/templates/djangocms_blog/includes/blog_meta.html b/djangocms_blog/templates/djangocms_blog/includes/blog_meta.html new file mode 100644 index 00000000..93132ce3 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/includes/blog_meta.html @@ -0,0 +1,33 @@ +{% load i18n easy_thumbnails_tags cms_tags apphooks_config_tags %} + + +
    + {% if post.categories.exists %} + {% for category in post.categories.all %} + {% if category.slug %} +
  • {{ category.name }}{% if not forloop.last %}, {% endif %}
  • + {% endif %} + {% endfor %} + {% endif %} + {% if post.tags.exists %} + {% for tag in post.tags.all %} +
  • {{ tag.name }}{% if not forloop.last %}, {% endif %}
  • + {% endfor %} + {% endif %} +
diff --git a/djangocms_blog/templates/djangocms_blog/plugins/archive.html b/djangocms_blog/templates/djangocms_blog/plugins/archive.html new file mode 100644 index 00000000..8c71ec34 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/plugins/archive.html @@ -0,0 +1,31 @@ +{% load i18n apphooks_config_tags %}{% spaceless %} +
+

{% trans "Archive" %}

+ {% regroup dates by date.year as years %} + {% with current_year=year current_month=month %} + + {% endwith %} +
+{% endspaceless %} diff --git a/djangocms_blog/templates/djangocms_blog/plugins/authors.html b/djangocms_blog/templates/djangocms_blog/plugins/authors.html new file mode 100644 index 00000000..799814b3 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/plugins/authors.html @@ -0,0 +1,19 @@ +{% load i18n easy_thumbnails_tags apphooks_config_tags %}{% spaceless %} + +{% endspaceless %} diff --git a/djangocms_blog/templates/djangocms_blog/plugins/authors_posts.html b/djangocms_blog/templates/djangocms_blog/plugins/authors_posts.html new file mode 100644 index 00000000..91f7e5c5 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/plugins/authors_posts.html @@ -0,0 +1,19 @@ +{% load i18n %}{% spaceless %} +
+

{% trans "Authors" %}

+
    + {% for author in authors_list %} +
  • +

    {% trans "Articles by" %} {{ author.get_full_name }}

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

    {% trans "No article found." %}

    + {% endfor %} +
    +
  • + {% endfor %} +
+
+{% endspaceless %} diff --git a/djangocms_blog/templates/djangocms_blog/plugins/categories.html b/djangocms_blog/templates/djangocms_blog/plugins/categories.html new file mode 100644 index 00000000..5f7192c7 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/plugins/categories.html @@ -0,0 +1,19 @@ +{% load i18n %}{% spaceless %} + +{% endspaceless %} diff --git a/djangocms_blog/templates/djangocms_blog/plugins/featured_posts.html b/djangocms_blog/templates/djangocms_blog/plugins/featured_posts.html new file mode 100644 index 00000000..1b33cd0e --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/plugins/featured_posts.html @@ -0,0 +1,9 @@ +{% load i18n %}{% spaceless %} +
+ +
+{% endspaceless %} diff --git a/djangocms_blog/templates/djangocms_blog/plugins/latest_entries.html b/djangocms_blog/templates/djangocms_blog/plugins/latest_entries.html new file mode 100644 index 00000000..c2276d58 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/plugins/latest_entries.html @@ -0,0 +1,11 @@ +{% load i18n %}{% spaceless %} +
+
+ {% for post in posts_list %} + {% include "djangocms_blog/includes/blog_item.html" with post=post image="true" TRUNCWORDS_COUNT=TRUNCWORDS_COUNT %} + {% empty %} +

{% trans "No article found." %}

+ {% endfor %} +
+
+{% endspaceless %} diff --git a/djangocms_blog/templates/djangocms_blog/plugins/tags.html b/djangocms_blog/templates/djangocms_blog/plugins/tags.html new file mode 100644 index 00000000..0622f6f6 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/plugins/tags.html @@ -0,0 +1,21 @@ +{% load i18n apphooks_config_tags %} + +{% spaceless %} + +{% endspaceless %} diff --git a/djangocms_blog/templates/djangocms_blog/post_archive.html b/djangocms_blog/templates/djangocms_blog/post_archive.html new file mode 100644 index 00000000..0d56bc99 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/post_archive.html @@ -0,0 +1 @@ +{% extends "djangocms_blog/post_list.html" %} diff --git a/djangocms_blog/templates/djangocms_blog/post_detail.html b/djangocms_blog/templates/djangocms_blog/post_detail.html new file mode 100644 index 00000000..f34f704b --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/post_detail.html @@ -0,0 +1,43 @@ +{% extends "djangocms_blog/base.html" %} +{% load i18n easy_thumbnails_tags cms_tags %} + +{% block canonical_url %}{% endblock canonical_url %} +{% block title %}{{ post.get_title }}{% endblock %} + +{% block content_blog %}{% spaceless %} +
+
+

{% render_model post "title" %}

+ {% if post.subtitle %} +

{% render_model post "subtitle" %}

+ {% endif %} + {% block blog_meta %} + {% include "djangocms_blog/includes/blog_meta.html" %} + {% endblock %} +
+ {% if not post.main_image_id %} +
{% render_placeholder post.media %}
+ {% else %} +
+ {% thumbnail post.main_image post.full_image_options.size crop=post.full_image_options.crop upscale=post.full_image_options.upscale subject_location=post.main_image.subject_location as thumb %} + {{ post.main_image.default_alt_text }} +
+ {% endif %} + {% endspaceless %} + {% if post.app_config.use_placeholder %} +
{% render_placeholder post.content %}
+ {% else %} +
{% render_model post "post_text" "post_text" "" "safe" %}
+ {% endif %} + {% if view.liveblog_enabled %} + {% include "liveblog/includes/post_detail.html" %} + {% endif %} + {% if post.related.exists %} +
+ {% for related in post.related.all %} + {% include "djangocms_blog/includes/blog_item.html" with post=related image="true" TRUNCWORDS_COUNT=TRUNCWORDS_COUNT %} + {% endfor %} +
+ {% endif %} +
+{% endblock content_blog %} diff --git a/djangocms_blog/templates/djangocms_blog/post_instant_article.html b/djangocms_blog/templates/djangocms_blog/post_instant_article.html new file mode 100644 index 00000000..628010ea --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/post_instant_article.html @@ -0,0 +1,34 @@ +{% load easy_thumbnails_tags cms_tags %} + + + + + {% block canonical_url %}{% endblock canonical_url %} + + + +
+
+

{{ post.title }}

+ {% if post.subtitle %} +

{{ post.subtitle }}

+ {% endif %} + + +
+ {{ post.get_author_name }} +
+
+ {{ post.main_image.default_alt_text|default:'' }} + {% if post.main_image.default_caption %}
{{ post.main_image.default_caption }}
{% endif %} +
+

{{ post.abstract|striptags|safe }}

+
+ {% if post.app_config.use_placeholder %} +
{% render_placeholder post.content %}
+ {% else %} +
{% render_model post "post_text" "post_text" "" "safe" %}
+ {% endif %} +
+ + diff --git a/djangocms_blog/templates/djangocms_blog/post_list.html b/djangocms_blog/templates/djangocms_blog/post_list.html new file mode 100644 index 00000000..a36625e7 --- /dev/null +++ b/djangocms_blog/templates/djangocms_blog/post_list.html @@ -0,0 +1,41 @@ +{% extends "djangocms_blog/base.html" %} +{% load i18n easy_thumbnails_tags %}{% spaceless %} + +{% block canonical_url %}{% endblock canonical_url %} + +{% block content_blog %} +
+ {% block blog_title %} +
+

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

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

{% trans "No article found." %}

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

{% trans "Back" %}

+ {% endif %} + {% if is_paginated %} + + {% endif %} +
+{% endblock %} +{% endspaceless %} diff --git a/djangocms_blog/templatetags/__init__.py b/djangocms_blog/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/djangocms_blog/templatetags/djangocms_blog.py b/djangocms_blog/templatetags/djangocms_blog.py new file mode 100644 index 00000000..c2bac0e7 --- /dev/null +++ b/djangocms_blog/templatetags/djangocms_blog.py @@ -0,0 +1,80 @@ +from cms.utils.plugins import get_plugins +from django import template + +register = template.Library() + + +@register.simple_tag(name="media_plugins", takes_context=True) +def media_plugins(context, post): + """ + Extract :py:class:`djangocms_blog.media.base.MediaAttachmentPluginMixin` + plugins from the ``media`` placeholder of the provided post. + + They can be rendered with ``render_plugin`` templatetag: + + .. code-block: python + + {% media_plugins post as media_plugins %} + {% for plugin in media_plugins %}{% render_plugin plugin %}{% endfor %} + + :param context: template context + :type context: dict + :param post: post instance + :type post: :py:class:`djangocms_blog.models.Post` + :return: list of :py:class:`djangocms_blog.media.base.MediaAttachmentPluginMixin` plugins + :rtype: List[djangocms_blog.media.base.MediaAttachmentPluginMixin] + """ + request = context["request"] + if post.media.get_plugins().exists(): + return get_plugins(request, post.media, None) + return [] + + +@register.simple_tag(name="media_images", takes_context=True) +def media_images(context, post, main=True): + """ + Extract images of the given size from all the + :py:class:`djangocms_blog.media.base.MediaAttachmentPluginMixin` + plugins in the ``media`` placeholder of the provided post. + + Support ``djangocms-video`` ``poster`` field in case the plugin + does not implement ``MediaAttachmentPluginMixin`` API. + + Usage: + + .. code-block: python + + {% media_images post False as thumbs %} + {% for thumb in thumbs %}{% endfor %} + + :param context: template context + :type context: dict + :param post: post instance + :type post: :py:class:`djangocms_blog.models.Post` + :param main: retrieve main image or thumbnail + :type main: bool + :return: list of images urls + :rtype: list + """ + plugins = media_plugins(context, post) + if main: + image_method = "get_main_image" + else: + image_method = "get_thumb_image" + images = [] + for plugin in plugins: + try: + images.append(getattr(plugin, image_method)()) + except Exception: + try: + image = plugin.poster + if image: + images.append(image.url) + except AttributeError: + pass + return images diff --git a/djangocms_blog/urls.py b/djangocms_blog/urls.py new file mode 100644 index 00000000..2f40d678 --- /dev/null +++ b/djangocms_blog/urls.py @@ -0,0 +1,39 @@ +from django.urls import path + +from .feeds import FBInstantArticles, LatestEntriesFeed, TagFeed +from .settings import get_setting +from .views import ( + AuthorEntriesView, + CategoryEntriesView, + PostArchiveView, + PostDetailView, + PostListView, + TaggedListView, +) + + +def get_urls(): + urls = get_setting("PERMALINK_URLS") + details = [] + for urlconf in urls.values(): + details.append( + path(urlconf, PostDetailView.as_view(), name="post-detail"), + ) + return details + + +detail_urls = get_urls() + +# module-level app_name attribute as per django 1.9+ +app_name = "djangocms_blog" +urlpatterns = [ + path("", PostListView.as_view(), name="posts-latest"), + path("feed/", LatestEntriesFeed(), name="posts-latest-feed"), + path("feed/fb/", FBInstantArticles(), name="posts-latest-feed-fb"), + path("/", PostArchiveView.as_view(), name="posts-archive"), + path("//", PostArchiveView.as_view(), name="posts-archive"), + path("author//", AuthorEntriesView.as_view(), name="posts-author"), + path("category//", CategoryEntriesView.as_view(), name="posts-category"), + path("tag//", TaggedListView.as_view(), name="posts-tagged"), + path("tag//feed/", TagFeed(), name="posts-tagged-feed"), +] + detail_urls diff --git a/djangocms_blog/views.py b/djangocms_blog/views.py new file mode 100644 index 00000000..b5121814 --- /dev/null +++ b/djangocms_blog/views.py @@ -0,0 +1,191 @@ +import os.path + +from aldryn_apphooks_config.mixins import AppConfigMixin +from django.apps import apps +from django.contrib.auth import get_user_model +from django.core.exceptions import ImproperlyConfigured +from django.http import Http404 +from django.shortcuts import get_object_or_404 +from django.urls import reverse +from django.utils.timezone import now +from django.utils.translation import get_language +from django.views.generic import DetailView, ListView +from parler.views import TranslatableSlugMixin, ViewUrlMixin + +from .models import BlogCategory, Post +from .settings import get_setting + +User = get_user_model() + + +class BaseBlogView(AppConfigMixin, ViewUrlMixin): + model = Post + + def optimize(self, qs): + """ + Apply select_related / prefetch_related to optimize the view queries + :param qs: queryset to optimize + :return: optimized queryset + """ + return qs.select_related("app_config").prefetch_related( + "translations", "categories", "categories__translations", "categories__app_config" + ) + + def get_view_url(self): + if not self.view_url_name: + raise ImproperlyConfigured("Missing `view_url_name` attribute on {}".format(self.__class__.__name__)) + + url = reverse(self.view_url_name, args=self.args, kwargs=self.kwargs, current_app=self.namespace) + return self.request.build_absolute_uri(url) + + def get_queryset(self): + language = get_language() + queryset = self.model._default_manager.namespace(self.namespace).active_translations(language_code=language) + if not getattr(self.request, "toolbar", None) or not self.request.toolbar.edit_mode_active: + queryset = queryset.published() + setattr(self.request, get_setting("CURRENT_NAMESPACE"), self.config) + return self.optimize(queryset.on_site()) + + def get_template_names(self): + template_path = (self.config and self.config.template_prefix) or "djangocms_blog" + return os.path.join(template_path, self.base_template_name) + + +class BaseBlogListView(BaseBlogView): + context_object_name = "post_list" + base_template_name = "post_list.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["TRUNCWORDS_COUNT"] = get_setting("POSTS_LIST_TRUNCWORDS_COUNT") + return context + + def get_paginate_by(self, queryset): + return (self.config and self.config.paginate_by) or get_setting("PAGINATION") + + +class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView): + context_object_name = "post" + base_template_name = "post_detail.html" + slug_field = "slug" + view_url_name = "djangocms_blog:post-detail" + instant_article = False + + def liveblog_enabled(self): + return self.object.enable_liveblog and apps.is_installed("djangocms_blog.liveblog") + + def get_template_names(self): + if self.instant_article: + template_path = (self.config and self.config.template_prefix) or "djangocms_blog" + return os.path.join(template_path, "post_instant_article.html") + else: + return super().get_template_names() + + def get_queryset(self): + queryset = self.model._default_manager.all() + if not getattr(self.request, "toolbar", None) or not self.request.toolbar.edit_mode_active: + queryset = queryset.published() + return self.optimize(queryset.on_site()) + + def get(self, *args, **kwargs): + # submit object to cms to get corrent language switcher and selected category behavior + if hasattr(self.request, "toolbar"): + self.request.toolbar.set_object(self.get_object()) + return super().get(*args, **kwargs) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["meta"] = self.get_object().as_meta() + context["instant_article"] = self.instant_article + context["use_placeholder"] = get_setting("USE_PLACEHOLDER") + setattr(self.request, get_setting("CURRENT_POST_IDENTIFIER"), self.get_object()) + return context + + +class PostListView(BaseBlogListView, ListView): + view_url_name = "djangocms_blog:posts-latest" + + +class PostArchiveView(BaseBlogListView, ListView): + date_field = "date_published" + allow_empty = True + allow_future = True + view_url_name = "djangocms_blog:posts-archive" + + def get_queryset(self): + qs = super().get_queryset() + if "month" in self.kwargs: + qs = qs.filter(**{"%s__month" % self.date_field: self.kwargs["month"]}) + if "year" in self.kwargs: + qs = qs.filter(**{"%s__year" % self.date_field: self.kwargs["year"]}) + return self.optimize(qs) + + def get_context_data(self, **kwargs): + kwargs["month"] = int(self.kwargs.get("month")) if "month" in self.kwargs else None + kwargs["year"] = int(self.kwargs.get("year")) if "year" in self.kwargs else None + if kwargs["year"]: + kwargs["archive_date"] = now().replace(kwargs["year"], kwargs["month"] or 1, 1) + context = super().get_context_data(**kwargs) + return context + + +class TaggedListView(BaseBlogListView, ListView): + view_url_name = "djangocms_blog:posts-tagged" + + def get_queryset(self): + qs = super().get_queryset() + return self.optimize(qs.filter(tags__slug=self.kwargs["tag"])) + + def get_context_data(self, **kwargs): + kwargs["tagged_entries"] = self.kwargs.get("tag") if "tag" in self.kwargs else None + context = super().get_context_data(**kwargs) + return context + + +class AuthorEntriesView(BaseBlogListView, ListView): + view_url_name = "djangocms_blog:posts-author" + + def get_queryset(self): + qs = super().get_queryset() + if "username" in self.kwargs: + qs = qs.filter(**{"author__%s" % User.USERNAME_FIELD: self.kwargs["username"]}) + return self.optimize(qs) + + def get_context_data(self, **kwargs): + kwargs["author"] = get_object_or_404(User, **{User.USERNAME_FIELD: self.kwargs.get("username")}) + context = super().get_context_data(**kwargs) + return context + + +class CategoryEntriesView(BaseBlogListView, ListView): + _category = None + view_url_name = "djangocms_blog:posts-category" + + @property + def category(self): + if not self._category: + try: + self._category = BlogCategory.objects.active_translations( + get_language(), slug=self.kwargs["category"] + ).get() + except BlogCategory.DoesNotExist: + raise Http404 + return self._category + + def get(self, *args, **kwargs): + # submit object to cms toolbar to get correct language switcher behavior + if hasattr(self.request, "toolbar"): + self.request.toolbar.set_object(self.category) + return super().get(*args, **kwargs) + + def get_queryset(self): + qs = super().get_queryset() + if "category" in self.kwargs: + qs = qs.filter(categories=self.category.pk) + return self.optimize(qs) + + def get_context_data(self, **kwargs): + kwargs["category"] = kwargs["object"] = self.category + context = super().get_context_data(**kwargs) + context["meta"] = self.category.as_meta() + return context