lino 25.8.2__py3-none-any.whl → 25.8.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. lino/__init__.py +1 -1
  2. lino/config/unused/403.html +1 -1
  3. lino/config/unused/404.html +1 -1
  4. lino/config/unused/500.html +1 -1
  5. lino/core/actions.py +1 -1
  6. lino/core/actors.py +2 -2
  7. lino/core/elems.py +1 -1
  8. lino/core/kernel.py +5 -1
  9. lino/core/renderer.py +2 -2
  10. lino/core/requests.py +4 -5
  11. lino/core/site.py +5 -3
  12. lino/core/store.py +3 -1
  13. lino/core/urls.py +1 -1
  14. lino/help_texts.py +9 -5
  15. lino/modlib/__init__.py +1 -1
  16. lino/modlib/bootstrap5/README.txt +2 -0
  17. lino/modlib/{bootstrap3 → bootstrap5}/__init__.py +6 -6
  18. lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/base.html +5 -4
  19. lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/detail.html +1 -1
  20. lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/index.html +1 -1
  21. lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/table.html +1 -1
  22. lino/modlib/{bootstrap3 → bootstrap5}/models.py +2 -2
  23. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.css +4085 -0
  24. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.css.map +1 -0
  25. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.min.css +6 -0
  26. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.min.css.map +1 -0
  27. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.css +4084 -0
  28. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.css.map +1 -0
  29. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.min.css +6 -0
  30. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.min.css.map +1 -0
  31. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.css +597 -0
  32. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.css.map +1 -0
  33. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.min.css +6 -0
  34. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.min.css.map +1 -0
  35. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.css +594 -0
  36. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.css.map +1 -0
  37. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.min.css +6 -0
  38. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.min.css.map +1 -0
  39. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.css +5406 -0
  40. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.css.map +1 -0
  41. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.min.css +6 -0
  42. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.min.css.map +1 -0
  43. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.css +5397 -0
  44. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.css.map +1 -0
  45. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.min.css +6 -0
  46. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.min.css.map +1 -0
  47. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.css +12043 -0
  48. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.css.map +1 -0
  49. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.min.css +6 -0
  50. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.min.css.map +1 -0
  51. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.css +12016 -0
  52. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.css.map +1 -0
  53. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.min.css +6 -0
  54. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.min.css.map +1 -0
  55. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.js +6315 -0
  56. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.js.map +1 -0
  57. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.min.js +7 -0
  58. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.min.js.map +1 -0
  59. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.js +4450 -0
  60. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.js.map +1 -0
  61. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.min.js +7 -0
  62. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.min.js.map +1 -0
  63. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.js +4497 -0
  64. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.js.map +1 -0
  65. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.min.js +7 -0
  66. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.min.js.map +1 -0
  67. lino/modlib/{bootstrap3 → bootstrap5}/views.py +10 -10
  68. lino/modlib/comments/mixins.py +1 -8
  69. lino/modlib/comments/models.py +2 -0
  70. lino/modlib/extjs/__init__.py +1 -1
  71. lino/modlib/jinja/renderer.py +1 -1
  72. lino/modlib/memo/__init__.py +1 -2
  73. lino/modlib/odata/views.py +7 -7
  74. lino/modlib/publisher/__init__.py +9 -2
  75. lino/modlib/publisher/choicelists.py +12 -43
  76. lino/modlib/publisher/config/publisher/page.pub.html +10 -13
  77. lino/modlib/publisher/fixtures/synodalworld.py +3 -1
  78. lino/modlib/publisher/mixins.py +28 -75
  79. lino/modlib/publisher/models.py +49 -143
  80. lino/modlib/publisher/renderer.py +6 -2
  81. lino/modlib/publisher/ui.py +23 -75
  82. lino/modlib/publisher/views.py +30 -15
  83. lino/modlib/users/fixtures/abc.py +20 -0
  84. lino/modlib/users/mixins.py +6 -6
  85. lino/modlib/weasyprint/__init__.py +25 -14
  86. lino/modlib/weasyprint/config/weasyprint/base.weasy.html +43 -27
  87. {lino-25.8.2.dist-info → lino-25.8.3.dist-info}/METADATA +1 -1
  88. {lino-25.8.2.dist-info → lino-25.8.3.dist-info}/RECORD +102 -62
  89. lino/modlib/bootstrap3/README.txt +0 -2
  90. lino/modlib/bootstrap3/static/bootstrap-3.3.4/css/bootstrap.css +0 -6584
  91. lino/modlib/bootstrap3/static/bootstrap-3.3.4/css/bootstrap.css.map +0 -1
  92. lino/modlib/bootstrap3/static/bootstrap-3.3.4/css/bootstrap.min.css +0 -5
  93. lino/modlib/bootstrap3/static/bootstrap-3.3.4/js/bootstrap.js +0 -2317
  94. lino/modlib/bootstrap3/static/bootstrap-3.3.4/js/bootstrap.min.js +0 -7
  95. /lino/modlib/{bootstrap3 → bootstrap5}/renderer.py +0 -0
  96. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/css/bootstrap-theme.css +0 -0
  97. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/css/bootstrap-theme.css.map +0 -0
  98. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/css/bootstrap-theme.min.css +0 -0
  99. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.eot +0 -0
  100. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.svg +0 -0
  101. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.ttf +0 -0
  102. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.woff +0 -0
  103. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.woff2 +0 -0
  104. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/js/bootstrap_lino.js +0 -0
  105. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/js/npm.js +0 -0
  106. {lino-25.8.2.dist-info → lino-25.8.3.dist-info}/WHEEL +0 -0
  107. {lino-25.8.2.dist-info → lino-25.8.3.dist-info}/licenses/AUTHORS.rst +0 -0
  108. {lino-25.8.2.dist-info → lino-25.8.3.dist-info}/licenses/COPYING +0 -0
@@ -1,58 +1,35 @@
1
1
  # -*- coding: UTF-8 -*-
2
- # Copyright 2012-2024 Rumma & Ko Ltd
2
+ # Copyright 2012-2025 Rumma & Ko Ltd
3
3
  # License: GNU Affero General Public License v3 (see file COPYING for details)
4
4
 
5
5
  from html import escape
6
+ from lorem import get_paragraph
6
7
  from django.db import models
7
8
  from django.http import HttpResponseRedirect
8
9
  from django.conf import settings
9
10
  from django.utils import translation
10
- from django.utils.translation import pgettext_lazy
11
-
12
- from django.utils import translation
13
- from django.conf import settings
14
-
15
- try:
16
- from lorem import get_paragraph
17
- except ImportError:
18
- lorem = None
19
-
20
11
  # from django.utils.translation import get_language
21
- from django.utils.html import mark_safe
22
-
23
12
  from lino.api import dd, rt, _
24
- from lino.utils import mti
13
+ # from lino.utils import mti
25
14
  from lino.utils.html import E, tostring
26
- from lino.core import constants
27
15
  # from lino.core.renderer import add_user_language
28
-
29
- from lino.utils.mldbc.fields import LanguageField
30
- from lino import mixins
31
- from lino.mixins import Hierarchical, Sequenced, Referrable
32
-
16
+ # from lino.utils.mldbc.fields import LanguageField
17
+ from lino.mixins import Hierarchical, Sequenced
33
18
  # from lino.modlib.summaries.mixins import Summarized
34
- from lino.modlib.office.roles import OfficeUser
35
19
  from lino.modlib.publisher.mixins import Publishable, PublishableContent
36
20
  from lino.modlib.comments.mixins import Commentable
37
21
  from lino.modlib.linod.choicelists import schedule_daily
38
22
  from lino.modlib.memo.mixins import Previewable
39
- from lino.mixins.polymorphic import Polymorphic
40
23
  from lino_xl.lib.topics.mixins import Taggable
41
- from lino.modlib.users.mixins import PrivacyRelevant
42
- # from .utils import render_node
43
24
 
44
- from lino.api import rt, dd
45
25
  from .choicelists import PublishingStates, PageFillers, SpecialPages
46
26
  from .mixins import Publishable
47
27
  from .ui import *
48
28
 
49
- # class Node(Referrable, Hierarchical, Sequenced, Previewable, Publishable, Commentable):
50
- # Polymorphic,
51
-
52
29
 
53
30
  class Page(
54
31
  Hierarchical, Sequenced, Previewable, Commentable, PublishableContent,
55
- Taggable # PrivacyRelevant
32
+ Taggable
56
33
  ):
57
34
  class Meta:
58
35
  verbose_name = _("Page")
@@ -65,6 +42,7 @@ class Page(
65
42
  unique_together = ["ref", "language"]
66
43
 
67
44
  memo_command = "page"
45
+ allow_cascaded_delete = ['parent']
68
46
 
69
47
  ref = dd.CharField(_("Reference"), max_length=200, blank=True, null=True)
70
48
  title = dd.CharField(_("Title"), max_length=250, blank=True)
@@ -81,8 +59,8 @@ class Page(
81
59
  )
82
60
 
83
61
  previous_page = dd.ForeignKey(
84
- "self", null=True, blank=True, verbose_name=_("Previous page")
85
- )
62
+ "self", null=True, blank=True, editable=False,
63
+ verbose_name=_("Previous page"), related_name='+')
86
64
 
87
65
  def __str__(self):
88
66
  return self.title or self.ref or super().__str__()
@@ -102,6 +80,9 @@ class Page(
102
80
  def get_node_info(self, ar):
103
81
  return ""
104
82
 
83
+ def is_public(self):
84
+ return not self.private
85
+
105
86
  # def full_clean(self):
106
87
  # self.page_type = self.mti_child().get_page_type()
107
88
  # super().full_clean()
@@ -111,6 +92,12 @@ class Page(
111
92
  # return mti.get_child(self, self.page_type.nodes_table.model) or self
112
93
  return self
113
94
 
95
+ def walk(self):
96
+ yield self
97
+ for c in self.children.all():
98
+ for i in c.walk():
99
+ yield i
100
+
114
101
  # def as_summary_row(self, ar, **kwargs):
115
102
  # return ar.obj2htmls(self, **kwargs)
116
103
 
@@ -246,77 +233,20 @@ class Page(
246
233
  return
247
234
  return "".join(ar.row_as_page(self))
248
235
 
249
- # @classmethod
250
- # def get_publisher_pages(cls, pv):
251
- # root = cls.objects.get(parent__isnull=True)
252
-
253
- # def compute_summary_values(self):
254
- # def walk(node, prev=None):
255
- # if node.page_type is not None:
256
- # node.set_prev(prev)
257
- # prev = node
258
- # for c in node.children.all():
259
- # prev = walk(c, prev)
260
- # return prev
261
- # root = self.get_ancestor()
262
- # last = walk(root)
263
- # root.set_prev(last)
264
-
265
- # from pprint import pprint
266
- # pprint(self.whole_tree())
267
-
268
- # def get_sidebar_caption(self):
269
- # if self.title:
270
- # return self.title
271
- # if self.ref:
272
- # return self.ref
273
- # return str(self.id)
274
- #
275
- # #~ if self.ref or self.parent:
276
- # #~ return self.ref
277
- # #~ return unicode(_('Home'))
278
-
279
- # def get_sidebar_item(self, request, other):
280
- # kw = dict()
281
- # add_user_language(kw, request)
282
- # url = self.get_absolute_url(**kw)
283
- # a = E.a(self.get_sidebar_caption(), href=url)
284
- # if self == other:
285
- # return E.li(a, **{'class':'active'})
286
- # return E.li(a)
287
- #
288
- # def get_sidebar_html(self, request):
289
- # items = []
290
- # #~ loop over top-level nodes
291
- # for n in self.__class__.objects.filter(parent__isnull=True).order_by('seqno'):
292
- # #~ items += [li for li in n.get_sidebar_items(request,self)]
293
- # items.append(n.get_sidebar_item(request, self))
294
- # if self.is_parented(n):
295
- # children = []
296
- # for ch in n.children.order_by('seqno'):
297
- # children.append(ch.get_sidebar_item(request, self))
298
- # if len(children):
299
- # items.append(E.ul(*children, **{'class':'nav nav-list'}))
300
- #
301
- # e = E.ul(*items, **{'class':'nav nav-list'})
302
- # return tostring_pretty(e)
303
- #
304
- # def get_sidebar_menu(self, request):
305
- # qs = self.__class__.objects.filter(parent__isnull=True, language=get_language())
306
- # #~ qs = self.children.all()
307
- # yield ('/', 'index', str(_('Home')))
308
- # #~ yield ('/downloads/', 'downloads', 'Downloads')
309
- # #~ yield ('/about', 'about', 'About')
310
- # #~ if qs is not None:
311
- # for obj in qs.order_by("seqno"):
312
- # if obj.ref and obj.title:
313
- # yield ('/' + obj.ref, obj.ref, obj.title)
314
- # #~ else:
315
- # #~ yield ('/','index',obj.title)
316
-
317
- def set_previous_page(self, prev):
236
+ def full_clean(self):
237
+ if self.root_page is None and self.parent is not None:
238
+ self.root_page = self.parent.root_page
239
+ super().full_clean()
240
+
241
+ def update_page(self, prev, root):
242
+ save = False
318
243
  if self.previous_page != prev:
319
244
  self.previous_page = prev
245
+ save = True
246
+ if self.root_page != root:
247
+ self.root_page = root
248
+ save = True
249
+ if save:
320
250
  self.save()
321
251
 
322
252
  def get_prev_link(self, ar, text="◄"): # "◄" 0x25c4
@@ -339,18 +269,6 @@ class Page(
339
269
  # url = ar.obj2url(next_node.mti_child())
340
270
  # return """<a href="{}">{}</a>""".format(url, text)
341
271
 
342
- @classmethod
343
- def get_publisher_pages(cls):
344
- def walk(page, prev=None):
345
- yield page
346
- for c in page.children.all():
347
- for i in walk(c):
348
- yield i
349
-
350
- for root in cls.objects.filter(parent__isnull=True):
351
- for i in walk(root):
352
- yield i
353
-
354
272
  @classmethod
355
273
  def get_dashboard_objects(cls, user):
356
274
  # print("20210114 get_dashboard_objects()", get_language())
@@ -405,42 +323,27 @@ class Page(
405
323
  if dd.plugins.memo.use_markup:
406
324
  dd.update_field(Page, "body", format="plain")
407
325
 
408
- # class Translation(dd.Model):
409
- # class Meta:
410
- # verbose_name = _("Page translation")
411
- # verbose_name_plural = _("Page translations")
412
- #
413
- # parent = dd.ForeignKey(
414
- # 'publisher.Page',
415
- # verbose_name=_("Translated from..."),
416
- # related_name='translated_from')
417
- # child = dd.ForeignKey(
418
- # 'publisher.Page',
419
- # blank=True, null=True,
420
- # verbose_name=_("Translated to..."),
421
- # related_name='translated_to')
422
- # language = dd.LanguageField()
423
-
424
326
 
425
327
  @schedule_daily()
426
328
  def update_publisher_pages(ar):
427
329
  # BaseRequest(parent=ar).run(settings.SITE.site_config.check_all_summaries)
428
330
  # rt.login().run(settings.SITE.site_config.check_all_summaries)
429
- Page = settings.SITE.models.publisher.Page
331
+ Page = rt.models.publisher.Page
430
332
  # for pv in PublisherViews.get_list_items():
431
333
  # for m in rt.models_by_base(Published, toplevel_only=True):
432
- prev = None
433
334
  count = 0
434
335
  ar.logger.info("Update publisher pages...")
435
336
 
436
- for obj in Page.get_publisher_pages():
437
- obj.set_previous_page(prev)
438
- prev = obj
439
- count += 1
337
+ for root in Page.objects.filter(parent__isnull=True):
338
+ prev = None
339
+ for obj in root.walk():
340
+ obj.update_page(prev, root)
341
+ prev = obj
342
+ count += 1
440
343
  ar.logger.info("%d pages have been updated.", count)
441
344
 
442
345
 
443
- def make_demo_pages(pages_desc):
346
+ def make_demo_pages(pages_desc, root_ref, group=None):
444
347
  # Translation = rt.models.pages.Translation
445
348
  # for lc in settings.SITE.LANGUAGE_CHOICES:
446
349
  # language = lc[0]
@@ -451,31 +354,33 @@ def make_demo_pages(pages_desc):
451
354
  for lng in settings.SITE.languages:
452
355
  counter = {None: 0}
453
356
  # count = 0
454
- home_page = Page.objects.get(
455
- special_page=SpecialPages.home, language=lng.django_code)
357
+ # home_page = Page.objects.get(
358
+ # special_page=SpecialPages.home, language=lng.django_code)
456
359
 
457
360
  with translation.override(lng.django_code):
458
361
 
459
- def make_pages(pages, parent=None):
362
+ # def make_pages(pages, parent=None):
363
+ def make_pages(pages, parent=None, root_ref=None):
460
364
  for page in pages:
461
365
  if len(page) != 3:
462
366
  raise Exception(f"Oops {page}")
463
367
  title, body, children = page
464
- kwargs = dict(title=title)
368
+ kwargs = dict(title=title, group=group)
465
369
  if body is None:
466
370
  kwargs.update(body=get_paragraph())
467
371
  else:
468
372
  kwargs.update(body=body)
469
373
  if parent is None:
470
- # kwargs.update(ref='index')
471
- continue # home page is created by SpecialPages
374
+ kwargs.update(ref=root_ref)
375
+ else:
376
+ kwargs.update(parent=parent)
472
377
  if lng.suffix:
473
378
  kwargs.update(
474
379
  translated_from=parent_nodes[counter[None]])
475
380
  kwargs.update(language=lng.django_code)
476
381
  if dd.is_installed("publisher"):
477
382
  kwargs.update(publishing_state='published')
478
- obj = Page(parent=parent, **kwargs)
383
+ obj = Page(**kwargs)
479
384
  yield obj
480
385
  if not lng.suffix:
481
386
  parent_nodes.append(obj)
@@ -483,4 +388,5 @@ def make_demo_pages(pages_desc):
483
388
  # print("20230324", title, kwargs)
484
389
  yield make_pages(children, obj)
485
390
 
486
- yield make_pages(pages_desc, parent=home_page)
391
+ # yield make_pages(pages_desc, parent=home_page)
392
+ yield make_pages(pages_desc, None, root_ref)
@@ -4,7 +4,7 @@
4
4
 
5
5
  from lino import logger
6
6
  from lino.core.renderer import add_user_language
7
- from lino.modlib.bootstrap3.renderer import Renderer
7
+ from lino.modlib.bootstrap5.renderer import Renderer
8
8
 
9
9
 
10
10
  class Renderer(Renderer):
@@ -21,11 +21,15 @@ class Renderer(Renderer):
21
21
 
22
22
  def obj2url(self, ar, obj, **kwargs):
23
23
  # if ar.actor is None or not isinstance(obj, ar.actor.model):
24
+ add_user_language(kwargs, ar)
25
+ if isinstance(obj, self.front_end.site.models.publisher.Page) and obj.ref == 'index':
26
+ return self.front_end.buildurl(**kwargs)
27
+ # if obj.ref:
28
+ # return self.front_end.buildurl(obj.ref, **kwargs)
24
29
  loc = obj.__class__._lino_publisher_location
25
30
  if loc is None:
26
31
  # logger.warning("No location for %s", obj.__class__)
27
32
  return None
28
- add_user_language(kwargs, ar)
29
33
  return self.front_end.buildurl(loc, str(obj.pk), **kwargs)
30
34
  # for i in PublisherViews.get_list_items():
31
35
  # if isinstance(obj, i.table_class.model):
@@ -1,86 +1,19 @@
1
1
  # -*- coding: UTF-8 -*-
2
- # Copyright 2012-2024 Rumma & Ko Ltd
2
+ # Copyright 2012-2025 Rumma & Ko Ltd
3
3
  # License: GNU Affero General Public License v3 (see file COPYING for details)
4
4
 
5
- from html import escape
6
5
  from django.db import models
7
- from django.http import HttpResponseRedirect
8
- from django.conf import settings
9
- from django.utils import translation
10
- from django.utils.translation import pgettext_lazy
11
6
 
12
7
  # from django.utils.translation import get_language
13
- from django.utils.html import mark_safe
14
- from django.utils.html import format_html
8
+ # from django.utils.html import format_html
15
9
 
16
10
  from lino.api import dd, rt, _
17
11
  from lino.utils.html import E
18
12
  from lino.core import constants
19
13
  # from lino.core.renderer import add_user_language
20
-
21
- from lino.utils.mldbc.fields import LanguageField
22
- from lino import mixins
23
- from lino.mixins import Hierarchical, Sequenced, Referrable
24
14
  from lino.modlib.office.roles import OfficeUser
25
- from lino.modlib.publisher.mixins import Publishable
26
-
27
- # from lino.modlib.publisher.choicelists import PublisherViews
28
- from lino.modlib.memo.mixins import Previewable
29
- # from .utils import render_node
30
-
31
- # class NodeDetail(dd.DetailLayout):
32
- # main = "first_panel general more"
33
- #
34
- # first_panel = dd.Panel("""
35
- # treeview_panel:20 preview:60
36
- # """, label=_("Preview"))
37
- #
38
- # general = dd.Panel("""
39
- # content_panel:60 right_panel:20
40
- # """, label=_("General"), required_roles=dd.login_required(OfficeUser))
41
- #
42
- # more = dd.Panel("""
43
- # # topics.TagsByOwner:20 add_interest
44
- # comments.CommentsByRFC:20
45
- # """, label=_("More"), required_roles=dd.login_required(OfficeUser))
46
- #
47
- # content_panel = """
48
- # title id
49
- # body
50
- # publisher.PagesByParent
51
- # """
52
- #
53
- # right_panel = """
54
- # parent seqno
55
- # child_node_depth
56
- # page_type
57
- # filler
58
- # """
59
- #
60
- #
61
- # class Nodes(dd.Table):
62
- # model = 'pages.Node'
63
- # column_names = "title page_type id *"
64
- # order_by = ["id"]
65
- # detail_layout = 'pages.NodeDetail'
66
- # insert_layout = """
67
- # title
68
- # page_type filler
69
- # """
70
- # display_mode = ((None, constants.DISPLAY_MODE_STORY),)
71
- #
72
- #
73
-
74
- # class Translations(dd.Table):
75
- # model = 'pages.Translation'
76
- #
77
- # class TranslationsByParent(Translations):
78
- # master_key = 'parent'
79
- # label = _("Translated to...")
80
- #
81
- # class TranslationsByChild(Translations):
82
- # master_key = 'child'
83
- # label = _("Translated from...")
15
+
16
+ from .choicelists import PageFillers, SpecialPages
84
17
 
85
18
 
86
19
  if dd.is_installed("comments") and dd.is_installed("topics"):
@@ -129,11 +62,11 @@ class PageDetail(dd.DetailLayout):
129
62
  # """
130
63
 
131
64
  right_panel = """
132
- ref language
133
- parent seqno
65
+ group language ref
66
+ parent seqno root_page
134
67
  child_node_depth main_image
135
- #page_type filler
136
- publishing_state special_page
68
+ special_page filler
69
+ publishing_state private
137
70
  publisher.TranslationsByPage
138
71
  """
139
72
 
@@ -175,3 +108,18 @@ class TranslationsByPage(Pages):
175
108
  def row_as_summary(cls, ar, obj, text=None, **kwargs):
176
109
  # return format_html("({}) {}", obj.language, obj.as_summary_row(ar, **kwargs))
177
110
  return E.span("({}) ".format(obj.language), obj.as_summary_item(ar, text, **kwargs))
111
+
112
+
113
+ class RootPages(Pages):
114
+ label = _("Root pages")
115
+ filter = models.Q(parent__isnull=True, special_page='')
116
+ column_names = "ref title language"
117
+ # default_display_modes = {None: constants.DISPLAY_MODE_LIST}
118
+
119
+
120
+ filler = PageFillers.add_item(RootPages)
121
+
122
+ SpecialPages.add_item(
123
+ "pages", filler=filler, body=_("List of root pages."),
124
+ title=_("Root pages"),
125
+ parent='home')
@@ -7,6 +7,7 @@ from django.conf import settings
7
7
  from django.core.exceptions import ObjectDoesNotExist
8
8
  from django.utils import translation
9
9
  from django.views.generic import View
10
+ from lino.core.requests import BaseRequest
10
11
 
11
12
 
12
13
  class Element(View):
@@ -23,36 +24,50 @@ class Element(View):
23
24
 
24
25
  # kw = dict(actor=self.publisher_model.get_default_table(),
25
26
  # request=request, renderer=rnd, permalink_uris=True)
26
- kw = dict(renderer=rnd)
27
+ # kw = dict(renderer=rnd)
27
28
  # kw = dict(renderer=rnd, permalink_uris=True)
28
29
  # if rnd.front_end.media_name == 'react':
29
30
  # kw.update(hash_router=True)
30
31
 
31
- kw.update(selected_pks=[pk])
32
+ # kw.update(selected_pks=[pk])
33
+ #
34
+ # try:
35
+ # ar = self.table_class.create_request(request=request, **kw)
36
+ # except ObjectDoesNotExist as e:
37
+ # # print("20240911", e)
38
+ # return http.HttpResponseNotFound(f"No row #{pk} in {self.table_class} ({e})")
39
+ # if len(ar.selected_rows) == 0:
40
+ # # print(f"20241003 Oops {ar} has no rows")
41
+ # return http.HttpResponseNotFound(f"20241003 No row #{pk} in {self.table_class}")
42
+ # obj = ar.selected_rows[0]
32
43
 
44
+ m = self.table_class.model
33
45
  try:
34
- ar = self.table_class.create_request(request=request, **kw)
35
- except ObjectDoesNotExist as e:
36
- # print("20240911", e)
37
- return http.HttpResponseNotFound(f"No row #{pk} in {self.table_class} ({e})")
38
- if len(ar.selected_rows) == 0:
39
- # print(f"20241003 Oops {ar} has no rows")
40
- return http.HttpResponseNotFound(f"20241003 No row #{pk} in {self.table_class}")
41
- obj = ar.selected_rows[0]
46
+ obj = m.objects.get(pk=pk)
47
+ except m.DoesNotExist as e:
48
+ return http.HttpResponseNotFound(f"No row #{pk} in {m} ({e})")
49
+ ar = BaseRequest(renderer=rnd, request=request, selected_rows=[obj])
50
+ # ar = BaseRequest(renderer=rnd, request=request)
42
51
  return obj.get_publisher_response(ar)
43
52
 
44
53
 
45
54
  class Index(View):
46
- def get(self, request, pk=1):
47
- rnd = settings.SITE.plugins.publisher.renderer
55
+ def get(self, request, ref=''):
48
56
  dv = settings.SITE.models.publisher.Pages
49
57
  if len(settings.SITE.languages) == 1:
50
58
  # language = settings.SITE.languages[0].django_code
51
59
  language = translation.get_language()
52
60
  else:
53
61
  language = request.LANGUAGE_CODE
54
- index_node = dv.model.objects.get(ref="index", language=language)
62
+ if ref == '':
63
+ ref = 'index'
64
+ try:
65
+ obj = dv.model.objects.get(ref=ref, language=language)
66
+ except dv.model.DoesNotExist:
67
+ return http.HttpResponseNotFound(f"No row {ref} in {dv.model}")
68
+
55
69
  # print("20231025", index_node)
70
+ rnd = settings.SITE.plugins.publisher.renderer
56
71
  ar = dv.create_request(request=request, renderer=rnd,
57
- selected_rows=[index_node])
58
- return index_node.get_publisher_response(ar)
72
+ selected_rows=[obj])
73
+ return obj.get_publisher_response(ar)
@@ -0,0 +1,20 @@
1
+ # -*- coding: UTF-8 -*-
2
+ # Copyright 2017-2025 Rumma & Ko Ltd
3
+ # License: GNU Affero General Public License v3 (see file COPYING for details)
4
+
5
+ from lino.api import dd, rt, _
6
+
7
+
8
+ def objects():
9
+ User = rt.models.users.User
10
+ UserTypes = rt.models.users.UserTypes
11
+
12
+ def user(username, **kwargs):
13
+ kwargs.update(user_type=UserTypes.user, username=username)
14
+ if not dd.plugins.users.with_nickname:
15
+ kwargs.pop('nickname', None)
16
+ return User(**kwargs)
17
+
18
+ yield user("andy", first_name="Andreas", last_name="Anderson", nickname="Andy", email="andy@example.com")
19
+ yield user("bert", first_name="Albert", last_name="Bernstein", nickname="Bert", email="bert@example.com")
20
+ yield user("chloe", first_name="Chloe", last_name="Cleoment", email="chloe@example.com")
@@ -490,18 +490,16 @@ class PrivacyRelevant(dd.Model):
490
490
  qs = super().get_request_queryset(ar, **filter)
491
491
  user = ar.get_user()
492
492
  if user.is_anonymous:
493
- if dd.is_installed('groups'):
494
- qs = qs.filter(group__private=False)
493
+ # if dd.is_installed('groups'):
494
+ # qs = qs.filter(group__private=False)
495
495
  return qs.filter(private=False)
496
- # if user.current_group is not None:
497
- # qs = qs.filter(group=user.current_group)
498
496
  if user.user_type.has_required_roles([SiteAdmin]):
499
497
  return qs
500
498
  flt = Q(private=False)
501
499
  if issubclass(cls, UserAuthored):
502
500
  flt |= Q(user=user)
503
501
  if dd.is_installed('groups'):
504
- flt |= Q(group__private=False)
502
+ # flt |= Q(group__private=False)
505
503
  flt |= Q(group__members__user=user)
506
504
  qs = qs.filter(flt).distinct()
507
505
  return qs
@@ -518,9 +516,11 @@ class PrivacyRelevant(dd.Model):
518
516
  self.private = self.group.private
519
517
 
520
518
  def get_default_group(self):
521
- return None
519
+ return None # dd.plugins.groups.get_default_group()
522
520
 
523
521
  def full_clean(self):
524
522
  if not self.group_id:
525
523
  self.group = self.get_default_group()
524
+ if self.group and self.group.private:
525
+ self.private = True
526
526
  super().full_clean()
@@ -34,6 +34,8 @@ class Plugin(ad.Plugin):
34
34
  top_right_width = None
35
35
  page_background_image = None
36
36
  top_right_image = None
37
+ bottom_left_image = None
38
+ bottom_left_width = None
37
39
  header_image = None
38
40
  margin = 10
39
41
  margin_left = 17
@@ -53,25 +55,34 @@ class Plugin(ad.Plugin):
53
55
  yield 'django-bulma'
54
56
 
55
57
  def pre_site_startup(self, site):
58
+ fcf = site.confdirs.find_config_file
56
59
  for ext in ("jpg", "png"):
57
- if self.header_height:
58
- fn = site.confdirs.find_config_file("top-right." + ext, "weasyprint")
59
- if fn:
60
+ if self.bottom_left_image is None:
61
+ if fn := fcf("bottom-left." + ext, "weasyprint"):
62
+ self.bottom_left_image = fn
63
+ if self.top_right_image is None:
64
+ if fn := fcf("top-right." + ext, "weasyprint"):
60
65
  self.top_right_image = fn
61
- if self.top_right_width is None:
62
- if imagesize is None:
63
- site.logger.warning("imagesize is not installed")
64
- continue
65
- w, h = imagesize.get(fn)
66
- self.top_right_width = self.header_height * w / h
67
- fn = site.confdirs.find_config_file("header." + ext, "weasyprint")
68
- if fn:
66
+ if self.header_image is None:
67
+ if fn := fcf("header." + ext, "weasyprint"):
69
68
  # site.logger.info("Found header_image %s", fn)
70
69
  self.header_image = fn
71
70
  if self.page_background_image is None:
72
- fn = site.confdirs.find_config_file(
73
- "page-background." + ext, "weasyprint")
74
- if fn:
71
+ if fn := fcf("page-background." + ext, "weasyprint"):
75
72
  # site.logger.info("Found page_background_image %s", fn)
76
73
  self.page_background_image = fn
74
+ if self.header_height:
75
+ if self.top_right_image and not self.top_right_width:
76
+ # if imagesize is None:
77
+ # site.logger.warning("imagesize is not installed")
78
+ # continue
79
+ w, h = imagesize.get(self.top_right_image)
80
+ self.top_right_width = self.header_height * w / h
81
+ if self.footer_height:
82
+ if self.bottom_left_image and not self.bottom_left_width:
83
+ # if imagesize is None:
84
+ # site.logger.warning("imagesize is not installed")
85
+ # continue
86
+ w, h = imagesize.get(self.bottom_left_image)
87
+ self.bottom_left_width = self.footer_height * w / h
77
88
  super().pre_site_startup(site)