Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More
publisher : render database content as styled html¶
The lino.modlib.publisher plugin adds the notion of content pages used to produce the pages of websites or books.
It doesn’t add any database model, but a choicelist, a model mixin and an
action. It also adds a printing build method
(lino.modlib.printing.BuildMethods).
This page contains code snippets (lines starting with >>>), which are
being tested during our development workflow. The following
snippet initializes the demo project used throughout this page.
>>> from lino_book.projects.noi2.startup import *
>>> from django.db.models import Q
Content pages¶
- content page¶
The basic building unit of a website or book, consisting of a title and a body.
Named pages¶
Named pages are pages with a user-given name in their Page name field. If this field is not empty, the page will have this name instead of its primary key in the url.
lino.core.actors.Actor.get_row_by_pk() uses a new class attribute
unique_keys, which defaults to ['id'], and on Pages it is
overridden to ['id', 'ref'].
Page overrides the Publishable.get_publisher_pk() method to
return self.page_name or str(self.pk).
Root pages¶
>>> ar = rt.login('robin')
>>> options = dict(column_names="id title", display_mode="grid")
>>> ar.show('publisher.RootPages', **options)
==== ===============
ID Title
---- ---------------
1 Home
22 (footer)
40 Laundry
91 Flying Circus
==== ===============
Site tenants¶
The noi2 site has lino.core.site.Site.with_tenants set to True and is a
showcase of a Lino site with multiple tenants. See
tenants : Multiple tenants Multi-tenant Lino sites.
>>> ar.show(tenants.SiteTenants)
==== =========
ID Name
---- ---------
1 Default
2 Circus
3 Laundry
==== =========
>>> ar.show(users.AllUsers)
========== ============= ===================== ============ ===========
Username Site tenant User type First name Last name
---------- ------------- --------------------- ------------ -----------
andy Circus 100 (Customer) Andreas Anderson
bert Laundry 100 (Customer) Albert Bernstein
chloe Default 100 (Customer) Chloe Cleoment
jean Default 400 (Developer) Jean
luc Circus 400 (Developer) Luc
marc Laundry 100 (Customer) Marc
mathieu Default 200 (Contributor) Mathieu
robin 900 (Administrator) Robin Rood
roby 900 (Administrator) Roby Raza
rolf 900 (Administrator) Rolf Rompen
========== ============= ===================== ============ ===========
>>> rt.models_by_base(tenants.TenantOnly)
[<class 'lino_noi.lib.groups.models.Group'>,
<class 'lino.modlib.publisher.models.Page'>,
<class 'lino.modlib.publisher.models.PageType'>]
This means for example that in a noi, contacts are always shared among all tenants. That’s a decision of the application developer.
>>> rt.login('andy').show('publisher.RootPages', **options)
==== ===============
ID Title
---- ---------------
91 Flying Circus
==== ===============
>>> rt.login('bert').show('publisher.RootPages', **options)
==== =========
ID Title
---- ---------
40 Laundry
==== =========
>>> rt.login('chloe').show('publisher.RootPages', **options)
==== ==========
ID Title
---- ----------
1 Home
22 (footer)
==== ==========
>>> publisher.Page.objects.filter(site_tenant_id=1).count()
45
>>> publisher.Page.objects.filter(site_tenant_id=2).count()
33
>>> publisher.Page.objects.filter(site_tenant_id=3).count()
51
>>> publisher.Page.objects.get(pk=1).site_tenant
SiteTenant #1 ('Default')
Translations¶
The English version of the home page has two translations, i.e. two other
Page: rows whose Page.translated_from points to the English
original.
>>> home = publisher.Page.objects.get(pk=1)
>>> home.language
'en'
>>> ar.show('publisher.TranslationsByPage', master_instance=home)
`ⓘ <…>`__ `⏏ <…>`__ | (bn) `হোম <…>`__, (de) `Startseite <…>`__
>>> ar.show('publisher.TranslationsByPage', master_instance=home, nosummary=True)
============ ========== ====
Title Language ID
------------ ---------- ----
হোম bn 2
Startseite de 3
============ ========== ====
>>> publisher.Page.objects.get(pk=1).language
'en'
>>> publisher.Page.objects.get(pk=2).language
'bn'
>>> publisher.Page.objects.get(pk=3).language
'de'
When a request asks for a page that is in another language than the requested language, then Lino redirects me to the corresponding page in the requested language.
>>> test_client.force_login(rt.login('chloe').user)
>>> test_client.get("/p/1?ul=de")
<HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/p/home?ul=de">
>>> test_client.get("/p/2?ul=de")
<HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/p/home?ul=de">
There is no redirect when asking with ul=de for a page that is already German:
>>> test_client.get("/p/3?ul=de")
<HttpResponse status_code=200, "text/html;charset="utf-8"">
>>> def test(url):
... res = test_client.get(url)
... if res.status_code == 302:
... print(f"redirect to {res.url}")
... res = test_client.get(res.url)
... soup = beautiful_soup(res.content)
... print(soup.title.text)
... for a in soup.find_all('a', {"class": "l-lngsel"}):
... print(f"{a.text} -> {a['href']}")
>>> test("/p/1?ul=de")
redirect to /p/home?ul=de
Startseite – noi2
en -> /p/home?ul=en
bn -> /p/home?ul=bn
>>> test("/p/2?ul=de")
redirect to /p/home?ul=de
Startseite – noi2
en -> /p/home?ul=en
bn -> /p/home?ul=bn
When using the Page.page_name instead of the primary key of a page,
there is no redirect because Lino will look up the page directly in the
requested language:
>>> test("/p/home?ul=en")
Home – noi2
bn -> /p/home?ul=bn
de -> /p/home?ul=de
>>> test("/p/home?ul=de")
Startseite – noi2
en -> /p/home?ul=en
bn -> /p/home?ul=bn
When the body of a named page in language 1 contains a memo command [ref page:2], then the end user wants to link to the specified page without redirecting back to the current language.
>>> sar = rt.login(renderer=dd.plugins.publisher.renderer, show_urls=True)
>>> home.memo2html(sar, None)
'<a href="/p/home" style="text-decoration:none">Home</a>'
>>> publisher.Page.objects.get(pk=2).memo2html(sar, None)
'<a href="/p/home?ul=bn" style="text-decoration:none">হোম</a>'
TODO 20260309 In the following snippet, shouldn’t the href specify “ul=en”? I’m not sure whether this is a problem:
>>> with translation.override('de'):
... home.memo2html(sar, None)
'<a href="/p/home" style="text-decoration:none">Home</a>'
Previous and next page¶
The previous_page fields have been updated:
>>> rt.login('robin').show('publisher.Pages', display_mode="grid", language="en",
... column_names="id title language root_page previous_page")
...
===== ================================= ========== =============== =================================
ID Title Language Root page Previous page
----- --------------------------------- ---------- --------------- ---------------------------------
1 Home en
2 হোম bn
3 Startseite de
4 About us en Home Home
5 আমাদের সম্পর্কে bn হোম হোম
6 Über uns de Startseite Startseite
7 Terms and conditions en Home About us
8 শর্তাবলী bn হোম আমাদের সম্পর্কে
9 Allgemeine Geschäftsbedingungen de Startseite Über uns
10 Privacy policy en Home Terms and conditions
11 গোপনীয়তা নীতি bn হোম শর্তাবলী
12 Datenschutz de Startseite Allgemeine Geschäftsbedingungen
13 Cookie settings en Home Privacy policy
14 কুকি সেটিংস bn হোম গোপনীয়তা নীতি
15 Cookie settings de Startseite Datenschutz
16 Copyright en Home Cookie settings
17 কপিরাইট bn হোম কুকি সেটিংস
18 Autorenrecht de Startseite Cookie settings
19 Root pages en Home Copyright
20 মূল পৃষ্ঠাসমূহ bn হোম কপিরাইট
21 Root pages de Startseite Autorenrecht
22 (footer) en
23 (footer) bn
24 (footer) de
25 Subscribe en Home Root pages
26 Subscribe bn হোম মূল পৃষ্ঠাসমূহ
27 Subscribe de Startseite Root pages
28 Blog en Home Subscribe
29 Blog bn হোম Subscribe
30 Blog de Startseite Subscribe
31 Calendar en Home Blog
32 ক্যালেন্ডার bn হোম Blog
33 Kalender de Startseite Blog
34 Contact en Home Synodality
35 যোগাযোগ bn হোম ক্যালেন্ডার
36 Kontakt de Startseite Kalender
37 Register en Home Contact
38 নিবন্ধন করুন bn হোম যোগাযোগ
39 Registrieren de Startseite Kontakt
40 Laundry en
41 Services en Laundry Laundry
42 Washing en Laundry Services
43 Drying en Laundry Washing
44 Air drying en Laundry Drying
45 Machine drying en Laundry Air drying
46 Drying foos en Laundry Machine drying
47 Drying bars en Laundry Drying foos
48 Drying bazes en Laundry Drying bars
49 Ironing en Laundry Drying bazes
50 Prices en Laundry Ironing
51 Photos en Laundry Prices
52 About us en Laundry Photos
53 Team en Laundry About us
54 History en Laundry Team
55 Contact en Laundry History
56 Terms & conditions en Laundry Contact
57 Laundry bn
58 পরিষেবা bn Laundry Laundry
59 Washing bn Laundry পরিষেবা
60 Drying bn Laundry Washing
61 Air drying bn Laundry Drying
62 Machine drying bn Laundry Air drying
63 Drying foos bn Laundry Machine drying
64 Drying bars bn Laundry Drying foos
65 Drying bazes bn Laundry Drying bars
66 Ironing bn Laundry Drying bazes
67 Prices bn Laundry Ironing
68 Photos bn Laundry Prices
69 আমাদের সম্পর্কে bn Laundry Photos
70 Team bn Laundry আমাদের সম্পর্কে
71 History bn Laundry Team
72 যোগাযোগ bn Laundry History
73 Terms & conditions bn Laundry যোগাযোগ
74 Laundry de
75 Dienstleistungen de Laundry Laundry
76 Washing de Laundry Dienstleistungen
77 Drying de Laundry Washing
78 Air drying de Laundry Drying
79 Machine drying de Laundry Air drying
80 Drying foos de Laundry Machine drying
81 Drying bars de Laundry Drying foos
82 Drying bazes de Laundry Drying bars
83 Ironing de Laundry Drying bazes
84 Prices de Laundry Ironing
85 Photos de Laundry Prices
86 Über uns de Laundry Photos
87 Team de Laundry Über uns
88 History de Laundry Team
89 Kontakt de Laundry History
90 Nutzungsbestimmungen de Laundry Kontakt
91 Flying Circus en
92 Places en Flying Circus Flying Circus
93 Programme en Flying Circus Places
94 Mission en Flying Circus Programme
95 Blog en Flying Circus Mission
96 Comments en Flying Circus Blog
97 About us en Flying Circus Comments
98 Team en Flying Circus About us
99 History en Flying Circus Team
100 Contact en Flying Circus History
101 Terms & conditions en Flying Circus Contact
102 Flying Circus bn
103 স্থান bn Flying Circus Flying Circus
104 Programme bn Flying Circus স্থান
105 Mission bn Flying Circus Programme
106 Blog bn Flying Circus Mission
107 মন্তব্য bn Flying Circus Blog
108 আমাদের সম্পর্কে bn Flying Circus মন্তব্য
109 Team bn Flying Circus আমাদের সম্পর্কে
110 History bn Flying Circus Team
111 যোগাযোগ bn Flying Circus History
112 Terms & conditions bn Flying Circus যোগাযোগ
113 Flying Circus de
114 Orte de Flying Circus Flying Circus
115 Programme de Flying Circus Orte
116 Mission de Flying Circus Programme
117 Blog de Flying Circus Mission
118 Kommentare de Flying Circus Blog
119 Über uns de Flying Circus Kommentare
120 Team de Flying Circus Über uns
121 History de Flying Circus Team
122 Kontakt de Flying Circus History
123 Nutzungsbestimmungen de Flying Circus Kontakt
124 FlyingCon en Home Calendar
125 Cascaded Continuous Voting en Home FlyingCon
126 Liquid democracy en Home Cascaded Continuous Voting
127 Digital vs analog en Home Liquid democracy
128 Software should be free en Home Digital vs analog
129 Synodality en Home Software should be free
===== ================================= ========== =============== =================================
Classes reference¶
- class lino.modlib.publisher.Page¶
The Django model that represents a content page.
Inherits from
PublishableContent,TranslatableContent.- title¶
- body¶
- language¶
- parent¶
- seqno¶
- root_page¶
The page that defines the top of the menu tree containing this page.
- special_page¶
If this page implements a “special page”, then this field points to a choice in
SpecialPages.
- pub_date¶
- pub_time¶
- publishing_state¶
See
PublishingStates
- class lino.modlib.publisher.Publishable¶
Model mixin to add to models that are potentially publishable.
- publisher_template¶
The name of the template to use when rendering a database row via the publisher interface.
“publisher/page.pub.html”
- preview_publication¶
Show this database row via the publisher interface.
Icon: 🌐
- class lino.modlib.publisher.PublishableContent¶
Model mixin to add to models that are potentially publishable.
Inherits from
Publishable.- language¶
The language of this content.
- publishing_state¶
Default value is ‘draft’
Pointer to
PublishingStates
- filler¶
Pointer to
PageFillers
- class lino.modlib.publisher.PublisherBuildMethod¶
This deserves better documentation. Maybe useless.
- class lino.modlib.publisher.PublishingStates¶
A choicelist with the possible states of a publisher page.
>>> rt.show(publisher.PublishingStates, language="en") ======= =========== ========= ============= ======== value name text Button text public ------- ----------- --------- ------------- -------- 10 draft Draft No 20 ready Ready No 30 published Public Yes 40 removed Removed No ======= =========== ========= ============= ========
- class lino.modlib.publisher.SpecialPages¶
A choicelist with the special pages available on this site.
>>> rt.show(publisher.SpecialPages, language="en") =========== ====================== ====================================== name text Pages ----------- ---------------------- -------------------------------------- home Home `en <…>`__ | `bn <…>`__ | `de <…>`__ about About us `en <…>`__ | `bn <…>`__ | `de <…>`__ terms Terms and conditions `en <…>`__ | `bn <…>`__ | `de <…>`__ privacy Privacy policy `en <…>`__ | `bn <…>`__ | `de <…>`__ cookies Cookie settings `en <…>`__ | `bn <…>`__ | `de <…>`__ copyright Copyright `en <…>`__ | `bn <…>`__ | `de <…>`__ roots roots `en <…>`__ | `bn <…>`__ | `de <…>`__ footer footer `en <…>`__ | `bn <…>`__ | `de <…>`__ subscribe Subscribe `en <…>`__ | `bn <…>`__ | `de <…>`__ blog blog `en <…>`__ | `bn <…>`__ | `de <…>`__ events events `en <…>`__ | `bn <…>`__ | `de <…>`__ contact Contact `en <…>`__ | `bn <…>`__ | `de <…>`__ register Register `en <…>`__ | `bn <…>`__ | `de <…>`__ =========== ====================== ======================================
Configuration¶
- lino.modlib.publisher.with_trees¶
Whether this site supports multiple publisher trees.
- lino.modlib.publisher.locations¶
A tuple of 2-tuples (loc, cls) where loc is a location string and cls a data table.
>>> pprint(dd.plugins.publisher.locations) (('b', lino_xl.lib.blogs.models.LatestEntries), ('p', lino.modlib.publisher.ui.Pages), ('r', lino.modlib.publisher.ui.RootPages), ('f', lino.modlib.uploads.ui.Uploads), ('books', lino_xl.lib.books.ui.Books), ('src', lino_xl.lib.sources.models.Sources), ('sng', lino_xl.lib.songs.ui.LatestSongs), ('e', lino_xl.lib.cal.ui.UpcomingEvents), ('t', lino_xl.lib.tickets.ui.Tickets), ('tp', lino_xl.lib.topics.models.Topics))
When setting this setting (usually in a
get_plugin_configs()method), the application developer should specify the data tables using their names. The above locations have been set inlino_cms.lib.cms.settingsas follows:yield ('publisher', 'locations', ( ('b', 'blogs.LatestEntries'), ('p', 'publisher.Pages'), ('t', 'topics.Topics'), ('u', 'users.Users')))
- lino.modlib.publisher.skin¶
Which skin to use. Default value is ‘boots’.
Currently the only alternative to ‘boots’ is ‘silly’, which is there just to show an alternative. You can see it by activating a line in the
noi2demo projectsettings.pyfile.
- lino.modlib.publisher.use_markup¶
(Deprecated)
Whether to use markup (instead of wysiwyg) for editing content.
When this is False, the body of pages gets edited using a wysiwyg editor and stored as (bleached) html.