Advanced usage

This document is used to gather more advanced usage examples.

Optional settings

These settings are optional (default values are shown):

FIBER_LOGIN_STRING = '@fiber'

FIBER_DEFAULT_TEMPLATE = 'base.html'
FIBER_TEMPLATE_CHOICES = []
FIBER_CONTENT_TEMPLATE_CHOICES = []

FIBER_EXCLUDE_URLS = []

FIBER_IMAGES_DIR = 'uploads/images'
FIBER_FILES_DIR = 'uploads/files'

FIBER_EDITOR = 'fiber.editor_definitions.ckeditor.EDITOR'

FIBER_PAGE_MANAGER = 'fiber.managers.PageManager'
FIBER_CONTENT_ITEM_MANAGER = 'fiber.managers.ContentItemManager'

FIBER_METADATA_PAGE_SCHEMA = {}
FIBER_METADATA_CONTENT_SCHEMA = {}

FIBER_AUTO_CREATE_CONTENT_ITEMS = False

COMPRESS = [the opposite of DEBUG]

API_RENDER_HTML = False  # If set to True, you must include 'djangorestframework' in INSTALLED_APPS as well

FIBER_IMAGE_PREVIEW = True  # If set to False, you don't need 'easy_thumbnails' in INSTALLED_APPS
FIBER_LIST_THUMBNAIL_OPTIONS = {'size': (111, 111)}
FIBER_DETAIL_THUMBNAIL_OPTIONS = {'size': (228, 228)}

Set or override fiber_page in a view

In this example, the news_item_detail view looks up the Page of the news_item_list by looking up its named URL. This way, you can reuse the content you have placed on the news_item_list Page for each news_item_detail Page.

def news_item_detail(request, news_item_slug):
    news_item = get_object_or_404(NewsItem, slug=news_item_slug)

    fiber_page = Page.objects.get(url__exact='"news_item_list"')

    t = loader.get_template('news_item_detail.html')
    c = RequestContext(request, {
        'fiber_page': fiber_page,
        'news_item': news_item
    })
    return HttpResponse(t.render(c))

Set or override fiber_page in a class based view

In this example, the NewsItemDetailView’s context is enriched with fiber_page and fiber_current_pages.

from django.core.urlresolvers import reverse
from django.views.generic import DetailView
from fiber.views import FiberPageMixin


class NewsItemDetailView(FiberPageMixin, DetailView):

    def get_fiber_page_url(self):
        return reverse('news_item_list')

Templates

In this example 4 page-templates will be available in the front- and backend-admin:

FIBER_TEMPLATE_CHOICES = (
    ('', 'Default template'),
    ('tpl-home.html', 'Home template'),
    ('tpl-intro.html', 'Intro template'),
    ('tpl-with-sidebar.html', 'With sidebar template'),
)

The first choice ‘’ will load the FIBER_DEFAULT_TEMPLATE, default this is ‘base.html’

In this example 2 content-templates will be available in the front- and backend-admin:

FIBER_CONTENT_TEMPLATE_CHOICES = (
    ('', 'Default template'),
    ('special-content-template.html', 'Special template'),
)

The first choice ‘’ will load the default content-template, this is ‘fiber/content_item.html’

Metadata

In this example metadata (key-value pairs) for pages will be available in the backend-admin:

FIBER_METADATA_PAGE_SCHEMA = {
    'title': {
        'widget': 'select',
        'values': ['option1', 'option2', 'option3',],
    },
    'bgcolor': {
        'widget': 'combobox',
        'values': ['#ffffff', '#fff000', '#ff00cc'],
        'prefill_from_db': True,
    },
    'description': {
        'widget': 'textarea',
    },
}

The first key key is ‘title’. Because it has widget ‘select’ you will have 3 fixed values to choose from.

The second key is ‘bgcolor’. Because it has widget ‘combobox’ you will have 3 fixed values to choose from and the choice to add your own ‘bgcolor’. By setting prefill_from_db to True, the custom values you have chosen will also appear in the selectbox of fixed values.

The third key is ‘description’. Because it has widget ‘textarea’ you can enter the value in a big textarea field.

Available widgets are:
select combobox textarea textfield (default widget)

Only the combobox can prefill from the database by setting prefill_from_db = True (default=False)

The same metadata schema is available for metadata for content:

FIBER_METADATA_CONTENT_SCHEMA

CKEditor config settings

Some default CKEditor config settings can be altered by creating a file called admin-extra.js, which should be placed in a folder structure like this:

appname/static/fiber/js/admin-extra.js

Make sure ‘appname’ is placed _before_ ‘fiber’ in settings.INSTALLED_APPS, otherwise the admin-extra.js file won’t override the default admin-extra.js provided by Django Fiber.

The following config settings can be used in admin-extra.js to override default CKEditor behavior:

window.CKEDITOR_CONFIG_FORMAT_TAGS = 'p;h1;h2;h3;h4';
window.CKEDITOR_CONFIG_STYLES_SET = [
    { name: 'intro paragraph', element: 'p', attributes: { 'class': 'intro' } }
];
window.CKEDITOR_CONFIG_EXTRA_PLUGINS = 'fpagelink,ffilelink,fimagelink,fcustomlink,funlink,fimage,table,tabletools';
window.CKEDITOR_CONFIG_REMOVE_PLUGINS = 'scayt,language,menubutton,forms,image,link';
window.CKEDITOR_CONFIG_ALLOWED_CONTENT = false;
window.CKEDITOR_CONFIG_EXTRA_ALLOWED_CONTENT = 'a[*]{*}(*);img[*]{*}(*);iframe[*];object[*];param[*];embed[*]';
window.CKEDITOR_TOOLBAR_CAN_COLLAPSE = false;
window.CKEDITOR_CONFIG_MAX_WIDTH = 610;
window.CKEDITOR_BASE_FLOAT_Z_INDEX = 1100;

You can also override the entire CKEditor toolbar, by setting the variable:

window.CKEDITOR_CONFIG_TOOLBAR

To see how this works, check the fiber.ckeditor.js file in the Django Fiber source: https://github.com/django-fiber/django-fiber/blob/master/fiber/static/fiber/js/fiber.ckeditor.js

Custom permissions

Fiber provides a fiber.permissions module. The Permission class defined here can be overridden by writing a custom permission class and pointing PERMISSION_CLASS in your settings module to that class.

Here’s an example module that implements object level permissions:

"""
    >>> from django.contrib.auth.models import User
    >>> u = User.objects.get(username='example-user')
    >>> from guardian.shortcuts import assign
    >>> from fiber.models import Page
    >>> p = Page.objects.get(title='A')
    >>> assign('change_page', u, p)
"""

from django.contrib.auth.models import User
from django.db.models import Q
from django.contrib.contenttypes.models import ContentType
from django.db.models.signals import pre_delete

from guardian.shortcuts import get_objects_for_user, get_perms, assign
from guardian.models import UserObjectPermission

from fiber.permissions import Permissions
from fiber.models import Image, File, Page, PageContentItem, ContentItem


PAGE_PERMISSIONS = ('change_page', 'delete_page')
CONTENTITEM_PERMISSIONS = ('change_contentitem', 'delete_contentitem')


def remove_obj_perms_connected_with_object(sender, instance, **kwargs):
    filters = Q(content_type=ContentType.objects.get_for_model(instance),
        object_pk=instance.pk)
    UserObjectPermission.objects.filter(filters).delete()


class CustomPermissions(Permissions):

    def __init__(self):
        """
        Since guardian does not delete permission-objects, when the objects that
        they point to are deleted, we must take care of deleting them our selves.
        See http://packages.python.org/django-guardian/userguide/caveats.html?highlight=caveat
        """
        pre_delete.connect(remove_obj_perms_connected_with_object, sender=Image)
        pre_delete.connect(remove_obj_perms_connected_with_object, sender=File)
        pre_delete.connect(remove_obj_perms_connected_with_object, sender=Page)
        pre_delete.connect(remove_obj_perms_connected_with_object, sender=PageContentItem)
        pre_delete.connect(remove_obj_perms_connected_with_object, sender=ContentItem)

    def filter_objects(self, user, qs):
        """
        Returns all objects that `user` is allowed to change, based on guardian permissions.
        Returns all objects if user is superuser.
        """
        if user.is_superuser:
            return qs
        return qs.filter(id__in=get_objects_for_user(user, 'change_%s' % qs.model.__name__.lower(), qs.model))

    def can_edit(self, user, obj):
        """
        Returns True if `user` is allowed to edit `obj` based on guardian permissions.
        """
        return 'change_%s' % obj.__class__.__name__.lower() in get_perms(user, obj)

    def can_move_page(self, user, page):
        """
        A user with change-permissions is allowed to move the page.
        A superuser always has all permissions as far as guardian is concerned.
        """
        return 'change_page' in get_perms(user, page)

    def object_created(self, user, obj):
        """
        Create 'change' permission to `obj` for `user`.
        """
        assign('change_%s' % obj.__class__.__name__.lower(), user, obj)

    def _filter_user_and_superuser(self, user, qs):
        """
        A user should see files and images owned by him and by the superuser.
        Files uploaded by other non-superusers should not be listed.

        Note - there should be only one superuser in the User model.
        """
        superuser = User.objects.get(is_superuser=True)

        qs = qs.filter(Q(id__in=get_objects_for_user(user, 'change_%s' % qs.model.__name__.lower(), qs.model)) |
            Q(id__in=get_objects_for_user(superuser, 'change_%s' % qs.model.__name__.lower(), qs.model)))
        return qs

    def filter_images(self, user, qs):
        return self._filter_user_and_superuser(user, qs)

    def filter_files(self, user, qs):
        return self._filter_user_and_superuser(user, qs)

Sitemap

The Sitemaps protocol allows a webmaster to inform search engines about URLs on a website that are available for crawling. Django comes with a high-level framework that makes generating sitemap XML files easy.

Install the sitemap application as per the instructions in the django documentation, then edit your project’s urls.py and add a reference to Fiber’s Sitemap class in order to included all the publicly-viewable Fiber pages:

...
from fiber.sitemaps import FiberSitemap
...
(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'fiber': FiberSitemap,
                                                              ... other sitemaps... })
...