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