From 3d8abd9df842c40901f965e768f547f3b1e2d649 Mon Sep 17 00:00:00 2001 From: bkfox Date: Wed, 29 Jun 2016 01:33:53 +0200 Subject: [PATCH] work on exposure to support inheritance, start to work on calendar with dynamic loading --- cms/README.md | 134 +++++++++++++++--- cms/decorators.py | 134 ++++++++---------- cms/qcombine.py | 14 +- cms/routes.py | 62 ++++---- cms/sections.py | 99 +++++++++++++ cms/static/aircox/cms/scripts.js | 101 ++++++++++--- cms/templates/aircox/cms/calendar.html | 25 ++++ cms/website.py | 7 +- notes.md | 14 +- website/sections.py | 2 +- website/templates/aircox/website/player.html | 2 +- .../templates/aircox/website/schedule.html | 36 +---- 12 files changed, 446 insertions(+), 184 deletions(-) create mode 100644 cms/templates/aircox/cms/calendar.html diff --git a/cms/README.md b/cms/README.md index 62e8955..f4a4ffd 100644 --- a/cms/README.md +++ b/cms/README.md @@ -2,7 +2,9 @@ Simple CMS generator used in Aircox. Main features includes: - website configuration and templating - articles and static pages -- sections: embeddable views used to display related elements of an object +- sections: + - embeddable views used to display related elements of an object + - load dynamic personnalized data - posts related to external models: - attributes binding, automatically updated - personalization of view rendering, using templates or sections @@ -53,6 +55,42 @@ function will be called using arguments **(post, related_object)**. At rendering, the property *info* can be retrieved from the Post. It is however not a field. + +### Combine multiple models +It is possible to render views that combine multiple models, with some +limitation. In order to make it possible, the `GenericModel` has been created +with its own query__set class `QCombine`. + +It can be used to register views that must render list of different elements, +such as for search. Once declared, you just need to register the class with +a view to the website. + + +```python +import aircox.cms.qcombine as qcombine + +class Article(RelatedPost): + pass + +class Program(RelatedPost): + pass + +class Diffusion(RelatedPost): + pass + + +class Publication(qcombine.GenericModel): + models = [ + Program, + Diffusion, + Article, + ] + +# website.register(... model = Publication ...) +# qs = Publication.objects.filter(title__icontain == "Hi") +``` + + ## Routes Routes are used to generate the URLs of the website. We provide some of the common routes: for the detail view of course, but also to select all posts or @@ -60,8 +98,6 @@ by thread, date, search, tags. It is of course possible to create your own routes. -Routes are registered to a router (FIXME: it might be possible that we remove -this later) ## Sections Sections are used to render part of a publication, for example to render a @@ -76,6 +112,60 @@ files (e.g. one for each type of publication), we prefer to declare these sections and configure them. This reduce the work, keep design coherent, and reduce the risk of bugs and so on. + +## Exposure +Section can expose one or more methods whose result are accessible from the +web. Such function is called an *Exposure*, and return a *string*(!). + +The routes to the exposures are generated and registered at the same time +a model is registered to the website, with the corresponding Section. + +To expose a method, there is to steps: +* decorate the parent class with `aircox.cms.decorators.expose` +* decorate the method with `aircox.cms.decorators.expose` + +An exposed method has an `_exposure` attribute, that is an instance of +Exposure, that holds exposure's informations and offer some tools to +work with exposure: + +```python +from aircox.cms.decorators import expose + +@expose +class MySection(sections.Section): + @expose + hi(self, request, *args, **kwargs): + return "hi!" + + @expose + hello(self, request, name, *args, **kwargs): + return "hello " + name + + hello._exposure.pattern = "(?P\w+)" +``` + +The urls will be generated once the class decorator is called, using for each +exposed item the following values: +- name: `'exps.' + class._exposure.name + '.' + method._exposure.name` +- url pattern: `'exps/' + class._exposure.pattern + '/' + method._exposure.name` +- kwargs: `{'cl': class }` + +Check also the javascript scripts where there are various utility to get the +result of exposure and map them easily. + + +### Using templates +It is possible to render using templates, by setting `_exposure.template_name`. +In this case, the function is wrapped using `aircox.cms.decorators.template`, +and have this signature: + + ```python + function(class, request) -> dict + ``` + +The returned dictionnary is used as context object to render the template. + + ## Website This class is used to create the website itself and regroup all the needed informations to make it beautiful. There are different steps to create the @@ -83,22 +173,33 @@ website, using instance of the Website class: 1. Create the Website instance with all basic information: name, tags, description, menus and so on. -2. For each type of publication, register it using a Post model, a list of - used sections, routes, and optional parameters. The given name is used - for routing. +2. Register the views: eventually linking with a model, a list of used + sections, routes, and optional parameters. The given name is used for + routing. 3. Register website's URLs to Django. 4. Change templates and css styles if needed. -It also offers various facilities, such as comments view registering. +It also offers various facilities, such as comments view registering, menu +initialization, url reversing. + + +### Default view and reverse +The Website class offers a `reverse` function that can be used to reverse +a url using a Route, a Post and kwargs arguments. + +It is possible to ask to reverse to a default route if the reversing process +failed with the given model. In this case, it uses the registered views +that have been registered as *default view* (register function with argument +`as_default=True`). # Rendering ## Views They are three kind of views, among which two are used to render related content (`PostListView`, `PostDetailView`), and one is used to set arbitrary content at given url pattern (`PageView`). -The `PostDetailView` and `PageView` use a list of sections to render their content. While `PostDetailView` is related to a model instance, `PageView` just render its sections. +The `PostDetailView` and `PageView` use a list of sections to render their content. If there is only one section in the view, it is merged as the main content instead of being a block in the content; this has for consequence that the template's context are merged and rendering at the same time than the page instead of before. -`PostListView` uses the route that have been matched in order to render the list. Internally, it uses `sections.List` to render the list, if no section is given by the user. The context used to render the page is initialized using the list's rendering context. +`PostListView` uses the route that have been matched in order to render the list. Internally, it uses `sections.List` to render the list, if no section is given by the user. The context used to render the page is initialized using the list's rendering context, then completed with its own context's stuff. ## Sections A Section behave similar to a view with few differences: @@ -118,20 +219,19 @@ It is also possible to specify a list of fields that are rendered in the list, a # Rendered content ## Templates -There are two base template that are extended by the others: -* **section.html**: used to render a single section; -* **website.html**: website page layout; +All sections and pages uses the **website.html** view to be rendered. If `context['embed']` is True, it only render the content without any menu. Previously there was two distinct templates, but in order to reduce the amount of code, keep coherence between the templates, they have been merged. -These both define the following blocks, with their related container (declared *inside* the block): +The following blocks are available, with their related container (declared *inside* the block): * *title*: the optional title in a `

` tag; * *header*: the optional header in a `
` tag; -* *content*: the content itself; for *section* there is not related container, for *website* container is declared *outside* as an element of class `.content`; +* *content*: the content itself; by default there is no related container. By convention however, if there is a container it has the class `.content`; * *footer*: the footer in a `