PageRenderTime 50ms CodeModel.GetById 14ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

/docs/page.rst

http://github.com/feincms/feincms
ReStructuredText | 417 lines | 282 code | 135 blank | 0 comment | 0 complexity | 6cd8b08cec1529672233783098f3f44c MD5 | raw file
  1.. _page:
  2
  3========================
  4The built-in page module
  5========================
  6
  7.. module:: feincms.module.page
  8
  9FeinCMS is primarily a system to work with lists of content blocks which
 10you can assign to arbitrary other objects. You do not necessarily have to
 11use it with a hierarchical page structure, but that's the most common use
 12case of course. Being able to put content together in small manageable
 13pieces is interesting for other uses too, e.g. for weblog entries where you
 14have rich text content interspersed with images, videos or maybe even galleries.
 15
 16
 17Activating the page module and creating content types
 18=====================================================
 19
 20To activate the page module, you need to follow the instructions in
 21:ref:`installation` and afterwards add :mod:`feincms.module.page` to your
 22:data:`INSTALLED_APPS`.
 23
 24Before proceeding with ``manage.py makemigrations`` and ``./manage.py migrate``,
 25it might be a good idea to take
 26a look at :ref:`page-extensions` -- the page module does have the minimum of
 27features in the default configuration and you will probably want to enable
 28several extensions.
 29
 30You need to create some content models too. No models are created by default,
 31because there is no possibility to unregister models. A sane default might
 32be to create :class:`~feincms.content.medialibrary.models.MediaFileContent` and
 33:class:`~feincms.content.richtext.models.RichTextContent` models; you can do this
 34by adding the following lines somewhere into your project, for example in a
 35``models.py`` file that will be processed anyway::
 36
 37    from django.utils.translation import gettext_lazy as _
 38
 39    from feincms.module.page.models import Page
 40    from feincms.contents import RichTextContent
 41    from feincms.module.medialibrary.contents import MediaFileContent
 42
 43    Page.register_extensions(
 44        'feincms.extensions.datepublisher',
 45        'feincms.extensions.translations'
 46    )  # Example set of extensions
 47
 48    Page.register_templates({
 49        'title': _('Standard template'),
 50        'path': 'base.html',
 51        'regions': (
 52            ('main', _('Main content area')),
 53            ('sidebar', _('Sidebar'), 'inherited'),
 54        ),
 55    })
 56
 57    Page.create_content_type(RichTextContent)
 58    Page.create_content_type(MediaFileContent, TYPE_CHOICES=(
 59        ('default', _('default')),
 60        ('lightbox', _('lightbox')),
 61    ))
 62
 63
 64It will be a good idea most of the time to register the
 65:class:`~feincms.content.richtext.models.RichTextContent`
 66first, because it's the most used content type for many applications. The
 67content type dropdown will contain content types in the same order as they
 68were registered.
 69
 70Please note that you should put these statements into a ``models.py`` file
 71of an app contained in ``INSTALLED_APPS``. That file is executed at Django startup time.
 72
 73
 74Setting up the admin interface
 75==============================
 76
 77The customized admin interface code is contained inside the :class:`ModelAdmin`
 78subclass, so you do not need to do anything special here.
 79
 80If you use the :class:`~feincms.content.richtext.models.RichTextContent`, you
 81need to download `TinyMCE <http://www.tinymce.com/>`_ and configure FeinCMS'
 82richtext support::
 83
 84    FEINCMS_RICHTEXT_INIT_CONTEXT = {
 85        'TINYMCE_JS_URL': STATIC_URL + 'your_custom_path/tiny_mce.js',
 86    }
 87
 88If you want to use a different admin site, or want to apply customizations to
 89the admin class used, add the following setting to your site-wide settings::
 90
 91    FEINCMS_USE_PAGE_ADMIN = False
 92
 93
 94Wiring up the views
 95===================
 96
 97Just add the following lines to your ``urls.py`` to get a catch-all URL pattern:
 98
 99::
100
101    urlpatterns += [
102        url(r'', include('feincms.urls')),
103    ]
104
105
106If you want to define a page as home page for the whole site, you can give it
107an :attr:`~Page.override_url` value of ``'/'``.
108
109More information can be found in :ref:`integration`
110
111
112Adding another content type
113===========================
114
115Imagine you've got a third-party gallery application and you'd like to include
116excerpts of galleries inside your content. You'd need to write a :class:`GalleryContent`
117base class and let FeinCMS create a model class for you with some important
118attributes added.
119
120::
121
122    from django.db import models
123    from django.template.loader import render_to_string
124    from feincms.module.page.models import Page
125    from gallery.models import Gallery
126
127    class GalleryContent(models.Model):
128        gallery = models.ForeignKey(Gallery)
129
130        class Meta:
131            abstract = True # Required by FeinCMS, content types must be abstract
132
133        def render(self, **kwargs):
134            return render_to_string('gallery/gallerycontent.html', {
135                'content': self, # Not required but a convention followed by
136                                 # all of FeinCMS' bundled content types
137                'images': self.gallery.image_set.order_by('?')[:5],
138            })
139
140    Page.create_content_type(GalleryContent)
141
142
143The newly created :class:`GalleryContent` for :class:`~feincms.module.page.models.Page`
144will live in the database table ``page_page_gallerycontent``.
145
146.. note::
147
148   FeinCMS requires your content type model to be abstract.
149
150More information about content types is available in :ref:`contenttypes`.
151
152
153.. _page-extensions:
154
155Page extension modules
156======================
157
158.. module:: feincms.module.page.extension
159
160Extensions are a way to put often-used functionality easily accessible without
161cluttering up the core page model for those who do not need them. The extensions
162are standard python modules with a :func:`register` method which will be called
163upon registering the extension. The :func:`register` method receives the
164:class:`~feincms.module.page.models.Page` class itself and the model admin class
165:class:`~feincms.module.page.modeladmins.PageAdmin` as arguments. The extensions can
166be activated as follows::
167
168     Page.register_extensions(
169        'feincms.module.page.extensions.navigation',
170        'feincms.module.page.extensions.titles',
171        'feincms.extensions.translations')
172
173
174The following extensions are available currently:
175
176* :mod:`feincms.extensions.changedate` --- Creation and modification dates
177
178  Adds automatically maintained creation and modification date fields
179  to the page.
180
181
182* :mod:`feincms.extensions.ct_tracker` --- Content type cache
183
184  Helps reduce database queries if you have three or more content types by
185  caching in the database which content types are available on each page.
186  If this extension is used, ``Page._ct_inventory`` has to be nullified
187  after adding and/or removing content blocks, otherwise changes might not
188  be visible in the frontend. Saving the page instance accomplishes this.
189
190
191* :mod:`feincms.extensions.datepublisher` --- Date-based publishing
192
193  Adds publication date and end date fields to the page, thereby enabling the
194  administrator to define a date range where a page will be available to
195  website visitors.
196
197
198* :mod:`feincms.page.extensions.excerpt` --- Page summary
199
200  Add a brief excerpt summarizing the content of this page.
201
202
203* :mod:`feincms.extensions.featured` --- Simple featured flag for a page
204
205  Lets administrators set a featured flag that lets you treat that page special.
206
207
208* :mod:`feincms.module.page.extensions.navigation` --- Navigation extensions
209
210  Adds navigation extensions to the page model. You can define subclasses of
211  ``NavigationExtension``, which provide submenus to the navigation generation
212  mechanism. See :ref:`page-ext-navigation` for more information on how to use
213  this extension.
214
215
216* :mod:`feincms.module.page.extensions.navigationgroups` --- Navigation groups
217
218  Adds a navigation group field to each page which can be used to distinguish
219  between the header and footer (or meta) navigation. Filtering is achieved
220  by passing the ``group`` argument to ``feincms_nav``.
221
222
223* :mod:`feincms.module.page.extensions.relatedpages` --- Links related content
224
225  Add a many-to-many relationship field to relate this page to other pages.
226
227
228* :mod:`feincms.extensions.seo` --- Search engine optimization
229
230  Adds fields to the page relevant for search engine optimization (SEO),
231  currently only meta keywords and description.
232
233
234* :mod:`feincms.module.page.extensions.sites` --- Limit pages to sites
235
236  Allows to limit a page to a certain site and not display it on other sites.
237
238
239* :mod:`feincms.module.page.extensions.symlinks` --- Symlinked content extension
240
241  Sometimes you want to reuse all content from a page in another place. This
242  extension lets you do that.
243
244
245* :mod:`feincms.module.page.extensions.titles` --- Additional titles
246
247  Adds additional title fields to the page model. You may not only define a
248  single title for the page to be used in the navigation, the <title> tag and
249  inside the content area, you are not only allowed to define different titles
250  for the three uses but also enabled to define titles and subtitles for the
251  content area.
252
253
254* :mod:`feincms.extensions.translations` --- Page translations
255
256  Adds a language field and a recursive translations many to many field to the
257  page, so that you can define the language the page is in and assign
258  translations. I am currently very unhappy with state of things concerning
259  the definition of translations, so that extension might change somewhat too.
260  This extension also adds new instructions to the setup_request method where
261  the Django i18n tools are initialized with the language given on the page
262  object.
263
264  While it is not required by FeinCMS itself it's still recommended to add
265  :class:`django.middleware.locale.LocaleMiddleware` to the
266  ``MIDDLEWARE_CLASSES``; otherwise you will see strange language switching
267  behavior in non-FeinCMS managed views (such as third party apps not integrated
268  using :class:`feincms.content.application.models.ApplicationContent` or
269  Django's own administration tool).
270  You need to have defined ``settings.LANGUAGES`` as well.
271
272
273.. note::
274
275   These extension modules add new fields to the ``Page`` class. If you add or
276   remove page extensions you make and apply new migrations.
277
278
279Using page request processors
280=============================
281
282A request processor is a function that gets the currently selected page and the
283request as parameters and returns either None (or nothing) or a HttpResponse.
284All registered request processors are run before the page is actually rendered.
285If the request processor indeed returns a :class:`HttpResponse`, further rendering of
286the page is cut short and this response is returned immediately to the client.
287It is also possible to raise an exception which will be handled like all exceptions
288are handled in Django views.
289
290This allows for various actions dependent on page and request, for example a
291simple user access check can be implemented like this::
292
293    def authenticated_request_processor(page, request):
294        if not request.user.is_authenticated():
295            raise django.core.exceptions.PermissionDenied
296
297    Page.register_request_processor(authenticated_request_processor)
298
299``register_request_processor`` has an optional second argument named ``key``.
300If you register a request processor with the same key, the second processor
301replaces the first. This is especially handy to replace the standard request
302processors named ``path_active`` (which checks whether all ancestors of
303a given page are active too) and ``redirect`` (which issues HTTP-level redirects
304if the ``redirect_to`` page field is filled in).
305
306
307Using page response processors
308==============================
309
310Analogous to a request processor, a response processor runs after a page
311has been rendered. It needs to accept the page, the request and the response
312as parameters and may change the response (or throw an exception, but try
313not to).
314
315A response processor is the right place to tweak the returned http response
316for whatever purposes you have in mind.
317
318::
319
320    def set_random_header_response_processor(page, request, response):
321        response['X-Random-Number'] = 42
322
323    Page.register_response_processor(set_random_header_response_processor)
324
325``register_response_processor`` has an optional second argument named ``key``,
326exactly like ``register_request_processor`` above. It behaves in the same way.
327
328
329WYSIWYG Editors
330===============
331
332TinyMCE 3 is configured by default to only allow for minimal formatting. This has proven
333to be the best compromise between letting the client format text without destroying the
334page design concept. You can customize the TinyMCE settings by creating your own
335init_richtext.html that inherits from `admin/content/richtext/init_tinymce.html`.
336You can even set your own CSS and linklist files like so::
337
338	FEINCMS_RICHTEXT_INIT_CONTEXT = {
339		'TINYMCE_JS_URL': STATIC_URL + 'your_custom_path/tiny_mce.js',
340		'TINYMCE_CONTENT_CSS_URL': None,  # add your css path here
341		'TINYMCE_LINK_LIST_URL': None  # add your linklist.js path here
342	}
343
344FeinCMS is set up to use TinyMCE_ 3 but you can use CKEditor_ instead if you prefer
345that one. Change the following settings::
346
347	FEINCMS_RICHTEXT_INIT_TEMPLATE = 'admin/content/richtext/init_ckeditor.html'
348	FEINCMS_RICHTEXT_INIT_CONTEXT = {
349		'CKEDITOR_JS_URL': STATIC_URL + 'path_to_your/ckeditor.js',
350	}
351
352Alternatively, you can also use TinyMCE_ 4 by changing the following setting::
353
354    FEINCMS_RICHTEXT_INIT_TEMPLATE = 'admin/content/richtext/init_tinymce4.html'
355
356.. _TinyMCE: http://www.tinymce.com/
357.. _CKEditor: http://ckeditor.com/
358
359
360ETag handling
361=============
362
363An ETag is a string that is associated with a page -- it should change if
364(and only if) the page content itself has changed. Since a page's content
365may depend on more than just the raw page data in the database (e.g. it
366might list its children or a navigation tree or an excerpt from some other
367place in the CMS alltogether), you are required to write an etag producing
368method for the page.
369
370::
371
372    # Very stupid etag function, a page is supposed the unchanged as long
373    # as its id and slug do not change. You definitely want something more
374    # involved, like including last change dates or whatever.
375    def my_etag(page, request):
376        return 'PAGE-%d-%s' % ( page.id, page.slug )
377    Page.etag = my_etag
378
379    Page.register_request_processors(Page.etag_request_processor)
380    Page.register_response_processors(Page.etag_response_processor)
381
382
383Sitemaps
384========
385
386To create a sitemap that is automatically populated with all pages in your
387Feincms site, add the following to your top-level urls.py::
388
389    from feincms.module.page.sitemap import PageSitemap
390    sitemaps = {'pages' : PageSitemap}
391
392    urlpatterns += [
393        url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap',
394            {'sitemaps': sitemaps}),
395    ]
396
397This will produce a default sitemap at the /sitemap.xml url. A sitemap can be
398further customised by passing it appropriate parameters, like so::
399
400    sitemaps = {'pages': PageSitemap(max_depth=2)}
401
402
403The following parameters can be used to modify the behaviour of the sitemap:
404
405* ``navigation_only`` -- if set to True, only pages that are in_navigation will appear
406  in the site map.
407* ``max_depth`` -- if set to a non-negative integer, will limit the sitemap generated
408  to this page hierarchy depth.
409* ``changefreq`` -- should be a string or callable specifying the page update frequency,
410  according to the sitemap protocol.
411* ``queryset`` -- pass in a query set to restrict the Pages to include
412  in the site map.
413* ``filter`` -- pass in a callable that transforms a queryset to filter
414  out the pages you want to include in the site map.
415* ``extended_navigation`` -- if set to True, adds pages from any navigation
416  extensions. If using PagePretender, make sure to include title, url,
417  level, in_navigation and optionally modification_date.