Example #1
0
    def entries(self):
        """
        Return the entries that are published under this node.
        """
        # Since there is currently no filtering in place, return all entries.
        EntryModel = get_entry_model()
        qs = get_entry_model().objects.order_by('-publication_date')

        # Only limit to current language when this makes sense.
        if issubclass(EntryModel, TranslatableModel):
            admin_form_language = self.get_current_language()  # page object is in current language tab.
            qs = qs.active_translations(admin_form_language).language(admin_form_language)

        return qs
Example #2
0
 def get_queryset(self):
     # NOTE: This is also workaround, defining the queryset static somehow caused results to remain cached.
     qs = get_entry_model().objects.filter(parent_site=self.request.site)
     qs = qs.published()
     if self.prefetch_translations:
         qs = qs.prefetch_related('translations')
     return qs
Example #3
0
 def get_object(self, queryset=None):
     if issubclass(get_entry_model(), TranslatableModel):
         # Filter by slug and language
         return self._translated_get_object(queryset)
     else:
         # Regular slug check
         return super(BaseDetailMixin, self).get_object(queryset)
Example #4
0
 def get_queryset(self):
     # NOTE: This is also workaround, defining the queryset static somehow caused results to remain cached.
     qs = get_entry_model().objects.filter(parent_site=self.request.site)
     qs = qs.published()
     if self.prefetch_translations:
         qs = qs.prefetch_related('translations')
     return qs
class GetEntriesNode(BlogAssignmentOrInclusionNode):
    """
    Query the entries in the database, and render them.
    This template tag supports the following syntax:

    .. code-block:: html+django

        {% get_entries category='slug' year=2012 as entries %}
        {% for entry in entries %}...{% endfor %}

        {% get_entries category='slug' year=2012 template="name/of/template.html" %}

    The allowed query parameters are:

    * ``category``: The slug or ID of a category
    * ``tag``: The slug or ID of a tag
    * ``author``: The username or ID of an author
    * ``year``: The full year.
    * ``month``: The month number to display
    * ``day``: The day of the month to display.
    * ``order``: Which field to order on, this can be:

     * ``slug``: The URL name of the entry.
     * ``title``: The title of the entry.
     * ``author``: The author full name
     * ``author_slug``: The author URL name.
     * ``category``: The category name.
     * ``category_slug``: The category URL name.
     * ``tag``: The tag name
     * ``tag_slug``: The tag URL name.
     * ``date``: The publication date of the entry.

    * ``orderby``: can be ASC/ascending or DESC/descending. The default depends on the ``order`` field.
    * ``limit``: The maximum number of entries to return.
    """
    template_name = "fluent_blogs/templatetags/entries.html"
    context_value_name = 'entries'
    allowed_kwargs = (
        'category',
        'tag',
        'author',
        'year',
        'month',
        'day',
        'orderby',
        'order',
        'limit',
        'parent_site',
    )
    model = get_entry_model()

    def get_value(self, context, *tag_args, **tag_kwargs):
        # Query happens in the backend,
        # the templatetag is considered to be a frontend.
        qs = self.model.objects.all()
        qs = query_entries(qs, **tag_kwargs)
        return qs
Example #6
0
 def get_object(self, queryset=None):
     if issubclass(get_entry_model(), TranslatableModel):
         # Filter by slug and language
         # Note that translation support is still optional,
         # even though the class inheritance includes it.
         return TranslatableSlugMixin.get_object(self, queryset)
     else:
         # Regular slug check, skip TranslatableSlugMixin
         return SingleObjectMixin.get_object(self, queryset)
Example #7
0
 def get_object(self, queryset=None):
     if issubclass(get_entry_model(), TranslatableModel):
         # Filter by slug and language
         # Note that translation support is still optional,
         # even though the class inheritance includes it.
         return TranslatableSlugMixin.get_object(self, queryset)
     else:
         # Regular slug check, skip TranslatableSlugMixin
         return SingleObjectMixin.get_object(self, queryset)
Example #8
0
 def description_template(self):
     EntryModel = get_entry_model()
     templates = [
         "{0}/{1}_feed_description.html".format(EntryModel._meta.app_label, EntryModel._meta.object_name.lower()),
         "fluent_blogs/entry_feed_description.html",  # New name
         "fluent_blogs/feeds/entry/description.html"  # Old name
     ]
     # The value is passed to get_template by the Feed class, so reduce the list here manually.
     for name in templates:
         try:
             get_template(name)
         except TemplateDoesNotExist:
             pass
         else:
             setattr(self.__class__, 'description_template', name)
             return templates
     return None
Example #9
0
 def description_template(self):
     EntryModel = get_entry_model()
     templates = [
         "{0}/{1}_feed_description.html".format(EntryModel._meta.app_label, EntryModel._meta.object_name.lower()),
         "fluent_blogs/entry_feed_description.html",  # New name
         "fluent_blogs/feeds/entry/description.html"  # Old name
     ]
     # The value is passed to get_template by the Feed class, so reduce the list here manually.
     for name in templates:
         try:
             get_template(name)
         except TemplateDoesNotExist:
             pass
         else:
             setattr(self.__class__, 'description_template', name)
             return templates
     return None
    def get_queryset(self):
        # The DetailView redefines get_queryset() to show detail pages for staff members.
        # All other overviews won't show the draft pages yet.
        qs = get_entry_model().objects.published(for_user=self.request.user)
        if self.prefetch_translations:
            qs = qs.prefetch_related('translations')

        # Allow same slug in different dates
        # The available arguments depend on the FLUENT_BLOGS_ENTRY_LINK_STYLE setting.
        year = int(self.kwargs['year']) if 'year' in self.kwargs else None
        month = int(self.kwargs['month']) if 'month' in self.kwargs else None
        day = int(self.kwargs['day']) if 'day' in self.kwargs else None

        range = get_date_range(year, month, day)
        if range:
            qs = qs.filter(publication_date__range=range)

        return qs
    def get_queryset(self):
        # The DetailView redefines get_queryset() to show detail pages for staff members.
        # All other overviews won't show the draft pages yet.
        qs = get_entry_model().objects.published(for_user=self.request.user)
        if self.prefetch_translations:
            qs = qs.prefetch_related("translations")

        # Allow same slug in different dates
        # The available arguments depend on the FLUENT_BLOGS_ENTRY_LINK_STYLE setting.
        year = int(self.kwargs["year"]) if "year" in self.kwargs else None
        month = int(self.kwargs["month"]) if "month" in self.kwargs else None
        day = int(self.kwargs["day"]) if "day" in self.kwargs else None

        range = get_date_range(year, month, day)
        if range:
            qs = qs.filter(publication_date__range=range)

        return qs
Example #12
0
from django.contrib.admin import widgets
from fluent_blogs.admin.abstractbase import AbstractEntryBaseAdmin, AbstractTranslatableEntryBaseAdmin, SeoEntryAdminMixin
from fluent_blogs.models import get_entry_model, Entry_Translation
from parler.models import TranslatableModel


EntryModel = get_entry_model()

_model_fields = EntryModel._meta.get_all_field_names()
if issubclass(EntryModel, TranslatableModel):
    _entry_admin_base = AbstractTranslatableEntryBaseAdmin
    _model_fields += Entry_Translation.get_translated_fields()
else:
    _entry_admin_base = AbstractEntryBaseAdmin


class EntryAdmin(SeoEntryAdminMixin, _entry_admin_base):
    """
    The Django admin class for the default blog :class:`~fluent_blogs.models.Entry` model.
    When using a custom model, you can use :class:`AbstractEntryBaseAdmin`, which isn't attached to any of the optional fields.
    """
    # Redefine the fieldset, because it will be extended with auto-detected fields.
    FIELDSET_GENERAL = (None, {
        'fields': ('title', 'slug', 'status',),  # is filled with ('intro', 'contents', 'categories', 'tags', 'enable_comments') below
    })

    # For Django 1.4, the fieldsets shouldn't be declared with 'fieldsets ='
    # as the admin validation won't recognize the translated fields.
    # The 1.4 validation didn't check the form at all, but only checks the model fields.
    # As of Django 1.5, using 'fieldsets = ..' with translated fields just works.
    declared_fieldsets = (
 def test_get_entry_model(self):
     self.assertIs(get_entry_model(), Entry)
Example #14
0
 def get_queryset(self):
     # NOTE: This is a workaround, defining the queryset static somehow caused results to remain cached.
     return get_entry_model().objects.published()
Example #15
0
 def test_get_entry_model(self):
     self.assertIs(get_entry_model(), Entry)
Example #16
0
from django.core.exceptions import ImproperlyConfigured
from django.urls import NoReverseMatch
from django.utils.html import format_html
from django.utils.timezone import now
from django.utils.translation import ugettext, ugettext_lazy as _
from django.utils.safestring import mark_safe
from fluent_blogs import appsettings
from fluent_blogs.admin.forms import AbstractEntryBaseAdminForm, AbstractTranslatableEntryBaseAdminForm
from fluent_blogs.base_models import AbstractEntryBase
from fluent_blogs.models import get_entry_model
from fluent_utils.dry.admin import MultiSiteAdminMixin
from fluent_contents.admin import PlaceholderFieldAdmin
from parler.admin import TranslatableAdmin
from parler.models import TranslationDoesNotExist

EntryModel = get_entry_model()


class AbstractEntryBaseAdmin(MultiSiteAdminMixin, PlaceholderFieldAdmin):
    """
    The base functionality of the admin, which only uses the fields of the
    :class:`~fluent_blogs.base_models.AbstractEntryBase` model.
    Everything else is branched off in the :class:`EntryAdmin` class.
    """
    filter_site = appsettings.FLUENT_BLOGS_FILTER_SITE_ID
    list_display = ('title', 'status_column', 'modification_date',
                    'actions_column')
    list_filter = ('status', )
    date_hierarchy = 'publication_date'
    search_fields = ('slug', 'title')
    actions = ['make_published']
def blog_entry_admin_change_url(entry):
    model = get_entry_model()
    return reverse('admin:{0}_{1}_change'.format(model._meta.app_label, model._meta.module_name), args=(entry.pk,))
    def handle(self, *args, **options):
        if args:
            raise CommandError("Command doesn't accept any arguments")

        Entry = get_entry_model()
        CategoryM2M = Entry.categories.through
        old_fk = CategoryM2M._meta.get_field('category')
        CurrentModel = old_fk.remote_field.model
        self.stdout.write("Current Entry.categories model: <{0}.{1}>".format(
            CurrentModel._meta.app_label, CurrentModel._meta.object_name
        ))

        old = options['from']
        new = options['to']
        if not old or not new:
            raise CommandError("Expected --from and --to options")

        if old.lower() == 'categories.category' and 'categories' not in settings.INSTALLED_APPS:
            # Can't import it in a Django 1.8+ project.
            OldModel = DummyCategoryBase
        else:
            try:
                OldModel = apps.get_model(old)
            except LookupError as e:
                raise CommandError("Invalid --from value: {0}".format(e))

        if not issubclass(OldModel, MPTTModel):
            raise CommandError("Expected MPTT model for --from value")

        try:
            NewModel = apps.get_model(new)
        except LookupError as e:
            raise CommandError("Invalid --to value: {0}".format(e))

        if not issubclass(NewModel, MPTTModel):
            raise CommandError("Expected MPTT model for --to value")

        if NewModel.objects.all().exists():
            raise CommandError("New model already has records, it should be an empty table!")

        old_i18n = issubclass(OldModel, TranslatableModel)
        new_i18n = issubclass(NewModel, TranslatableModel)
        old_title = _detect_title_field(OldModel)
        new_title = _detect_title_field(NewModel)
        mptt_fields = "lft, rght, tree_id, level, parent_id"

        with transaction.atomic():
            if not old_i18n and not new_i18n:
                # Untranslated to untranslated
                self.stdout.write("* Copying category fields...")
                with connection.cursor() as cursor:
                    cursor.execute(
                        'INSERT INTO {new_model}(id, slug, {new_title}, {mptt_fields}})'
                        ' SELECT id, slug, {old_title}, {mptt_fields} FROM {old_model}'.format(
                            new_model=NewModel._meta.db_table,
                            new_title=new_title,
                            old_model=OldModel._meta.db_table,
                            old_title=old_title,
                            mptt_fields=mptt_fields,
                        ))
            elif not old_i18n and new_i18n:
                # Untranslated to translated
                # - base table fields
                with connection.cursor() as cursor:
                    self.stdout.write("* Copying category base fields...")
                    cursor.execute(
                        'INSERT INTO {new_model}(id, {mptt_fields})'
                        ' SELECT id, {mptt_fields} FROM {old_model}'.format(
                            new_model=NewModel._meta.db_table,
                            old_model=OldModel._meta.db_table,
                            mptt_fields=mptt_fields,
                        ))
                    # - create translations on fallback language
                    self.stdout.write("* Creating category translations...")
                    cursor.execute(
                        'INSERT INTO {new_translations}(master_id, language_code, slug, {new_title})'
                        ' SELECT id, %s, slug, {old_title} FROM {old_model}'.format(
                            new_translations=NewModel._parler_meta.root_model._meta.db_table,
                            new_title=new_title,
                            old_model=OldModel._meta.db_table,
                            old_title=old_title,
                        ), [appsettings.FLUENT_BLOGS_DEFAULT_LANGUAGE_CODE])
            elif old_i18n and not new_i18n:
                # Reverse, translated to untranslated. Take fallback only
                # Convert all fields back to the single-language table.
                self.stdout.write("* Copying category fields and fallback language fields...")
                for old_category in OldModel.objects.all():
                    translations = old_category.translations.all()
                    try:
                        # Try default translation
                        old_translation = translations.get(language_code=appsettings.FLUENT_BLOGS_DEFAULT_LANGUAGE_CODE)
                    except ObjectDoesNotExist:
                        try:
                            # Try internal fallback
                            old_translation = translations.get(language_code__in=('en-us', 'en'))
                        except ObjectDoesNotExist:
                            # Hope there is a single translation
                            old_translation = translations.get()

                    fields = dict(
                        id=old_category.id,
                        lft=old_category.lft,
                        rght=old_category.rght,
                        tree_id=old_category.tree_id,
                        level=old_category.level,
                        # parler fields
                        _language_code=old_translation.language_code,
                        slug=old_category.slug
                    )
                    fields[new_title] = getattr(old_translation, old_title)
                    NewModel.objects.create(**fields)

            elif old_i18n and new_i18n:
                # Translated to translated
                # - base table
                with connection.cursor() as cursor:
                    self.stdout.write("* Copying category base fields...")
                    cursor.execute(
                        'INSERT INTO {new_model}(id, {mptt_fields})'
                        ' SELECT id, {mptt_fields} FROM {old_model}'.format(
                            new_model=NewModel._meta.db_table,
                            old_model=OldModel._meta.db_table,
                            mptt_fields=mptt_fields,
                        ))
                    # - all translations
                    self.stdout.write("* Copying category translations...")
                    cursor.execute(
                        'INSERT INTO {new_translations}(master_id, language_code, slug, {new_title})'
                        ' SELECT id, languag_code, slug, {old_title} FROM {old_translations}'.format(
                            new_translations=NewModel._parler_meta.root_model._meta.db_table,
                            new_title=new_title,
                            old_translations=OldModel._parler_meta.root_model._meta.db_table,
                            old_title=old_title,
                        ), [appsettings.FLUENT_BLOGS_DEFAULT_LANGUAGE_CODE])
            else:
                raise NotImplementedError()  # impossible combination

            self.stdout.write("* Switching M2M foreign key constraints...")
            __, __, __, kwargs = old_fk.deconstruct()
            kwargs['to'] = NewModel
            new_fk = models.ForeignKey(**kwargs)
            new_fk.set_attributes_from_name(old_fk.name)
            with connection.schema_editor() as schema_editor:
                schema_editor.alter_field(CategoryM2M, old_fk, new_fk)

        self.stdout.write("Done.\n")
        self.stdout.write("You may now remove the old category app from your project, INSTALLED_APPS and database.\n")
Example #19
0
def blog_entry_admin_change_url(entry):
    model = get_entry_model()
    return reverse('admin:{0}_{1}_change'.format(model._meta.app_label,
                                                 model._meta.model_name),
                   args=(entry.pk, ))
Example #20
0
def get_entry_queryset():
    # Avoid being cached at module level, always return a new queryset.
    return get_entry_model().objects.published().order_by('-publication_date')
Example #21
0
def get_entry_queryset():
    # Avoid being cached at module level, always return a new queryset.
    return get_entry_model().objects.published().active_translations(
    ).order_by('-publication_date')
    def handle_noargs(self, **options):
        try:
            from django.apps import apps
        except ImportError:
            # Don't bother migrating old south tables, first migrate to Django 1.7 please.
            raise CommandError("This is a Django 1.7+ command only")

        Entry = get_entry_model()
        CategoryM2M = Entry.categories.through
        old_fk = CategoryM2M._meta.get_field('category')
        CurrentModel = old_fk.rel.to
        self.stdout.write("Current Entry.categories model: <{0}.{1}>".format(
            CurrentModel._meta.app_label, CurrentModel._meta.object_name
        ))

        old = options['from']
        new = options['to']
        if not old or not new:
            raise CommandError("Expected --from and --to options")

        if old.lower() == 'categories.category' and 'categories' not in settings.INSTALLED_APPS:
            # Can't import it in a Django 1.8+ project.
            OldModel = DummyCategoryBase
        else:
            try:
                OldModel = apps.get_model(old)
            except LookupError as e:
                raise CommandError("Invalid --from value: {0}".format(e))

        if not issubclass(OldModel, MPTTModel):
            raise CommandError("Expected MPTT model for --from value")

        try:
            NewModel = apps.get_model(new)
        except LookupError as e:
            raise CommandError("Invalid --to value: {0}".format(e))

        if not issubclass(NewModel, MPTTModel):
            raise CommandError("Expected MPTT model for --to value")

        if NewModel.objects.all().exists():
            raise CommandError("New model already has records, it should be an empty table!")

        old_i18n = issubclass(OldModel, TranslatableModel)
        new_i18n = issubclass(NewModel, TranslatableModel)
        old_title = _detect_title_field(OldModel)
        new_title = _detect_title_field(NewModel)
        mptt_fields = "lft, rght, tree_id, level, parent_id"

        with transaction.atomic():
            if not old_i18n and not new_i18n:
                # Untranslated to untranslated
                self.stdout.write("* Copying category fields...")
                with connection.cursor() as cursor:
                    cursor.execute(
                        'INSERT INTO {new_model}(id, slug, {new_title}, {mptt_fields}})'
                        ' SELECT id, slug, {old_title}, {mptt_fields} FROM {old_model}'.format(
                            new_model=NewModel._meta.db_table,
                            new_title=new_title,
                            old_model=OldModel._meta.db_table,
                            old_title=old_title,
                            mptt_fields=mptt_fields,
                        ))
            elif not old_i18n and new_i18n:
                # Untranslated to translated
                # - base table fields
                with connection.cursor() as cursor:
                    self.stdout.write("* Copying category base fields...")
                    cursor.execute(
                        'INSERT INTO {new_model}(id, {mptt_fields})'
                        ' SELECT id, {mptt_fields} FROM {old_model}'.format(
                            new_model=NewModel._meta.db_table,
                            old_model=OldModel._meta.db_table,
                            mptt_fields=mptt_fields,
                        ))
                    # - create translations on fallback language
                    self.stdout.write("* Creating category translations...")
                    cursor.execute(
                        'INSERT INTO {new_translations}(master_id, language_code, slug, {new_title})'
                        ' SELECT id, %s, slug, {old_title} FROM {old_model}'.format(
                            new_translations=NewModel._parler_meta.root_model._meta.db_table,
                            new_title=new_title,
                            old_model=OldModel._meta.db_table,
                            old_title=old_title,
                        ), [appsettings.FLUENT_BLOGS_DEFAULT_LANGUAGE_CODE])
            elif old_i18n and not new_i18n:
                # Reverse, translated to untranslated. Take fallback only
                # Convert all fields back to the single-language table.
                self.stdout.write("* Copying category fields and fallback language fields...")
                for old_category in OldModel.objects.all():
                    translations = old_category.translations.all()
                    try:
                        # Try default translation
                        old_translation = translations.get(language_code=appsettings.FLUENT_BLOGS_DEFAULT_LANGUAGE_CODE)
                    except ObjectDoesNotExist:
                        try:
                            # Try internal fallback
                            old_translation = translations.get(language_code__in=('en-us', 'en'))
                        except ObjectDoesNotExist:
                            # Hope there is a single translation
                            old_translation = translations.get()

                    fields = dict(
                        id=old_category.id,
                        lft=old_category.lft,
                        rght=old_category.rght,
                        tree_id=old_category.tree_id,
                        level=old_category.level,
                        # parler fields
                        _language_code=old_translation.language_code,
                        slug=old_category.slug
                    )
                    fields[new_title] = getattr(old_translation, old_title)
                    NewModel.objects.create(**fields)

            elif old_i18n and new_i18n:
                # Translated to translated
                # - base table
                with connection.cursor() as cursor:
                    self.stdout.write("* Copying category base fields...")
                    cursor.execute(
                        'INSERT INTO {new_model}(id, {mptt_fields})'
                        ' SELECT id, {mptt_fields} FROM {old_model}'.format(
                            new_model=NewModel._meta.db_table,
                            old_model=OldModel._meta.db_table,
                            mptt_fields=mptt_fields,
                        ))
                    # - all translations
                    self.stdout.write("* Copying category translations...")
                    cursor.execute(
                        'INSERT INTO {new_translations}(master_id, language_code, slug, {new_title})'
                        ' SELECT id, languag_code, slug, {old_title} FROM {old_translations}'.format(
                            new_translations=NewModel._parler_meta.root_model._meta.db_table,
                            new_title=new_title,
                            old_translations=OldModel._parler_meta.root_model._meta.db_table,
                            old_title=old_title,
                        ), [appsettings.FLUENT_BLOGS_DEFAULT_LANGUAGE_CODE])
            else:
                raise NotImplementedError()  # impossible combination

            self.stdout.write("* Switching M2M foreign key constraints...")
            __, __, __, kwargs = old_fk.deconstruct()
            kwargs['to'] = NewModel
            new_fk = models.ForeignKey(**kwargs)
            new_fk.set_attributes_from_name(old_fk.name)
            with connection.schema_editor() as schema_editor:
                schema_editor.alter_field(CategoryM2M, old_fk, new_fk)

        self.stdout.write("Done.\n")
        self.stdout.write("You may now remove the old category app from your project, INSTALLED_APPS and database.\n")
Example #23
0
 def get_queryset(self):
     # NOTE: This is a workaround, defining the queryset static somehow caused results to remain cached.
     return get_entry_model().objects.published()
Example #24
0
from django.contrib import admin
from fluent_blogs.admin.entryadmin import AbstractEntryBaseAdminForm, AbstractTranslatableEntryBaseAdminForm, AbstractEntryBaseAdmin, AbstractTranslatableEntryBaseAdmin, SeoEntryAdminMixin, EntryAdmin
from fluent_blogs.models import get_entry_model, Entry

__all__ = (
    'AbstractEntryBaseAdminForm',
    'AbstractTranslatableEntryBaseAdminForm',

    'AbstractEntryBaseAdmin',
    'AbstractTranslatableEntryBaseAdmin',
    'SeoEntryAdminMixin',
    'EntryAdmin',
)


# Ony register the admin when the entry model is not customized.
if get_entry_model() is Entry:
    admin.site.register(Entry, EntryAdmin)
Example #25
0
def get_entry_queryset(site):
    # Avoid being cached at module level, always return a new queryset.
    return get_entry_model().objects.filter(parent_site=site).published().active_translations().order_by('-publication_date')