lino 25.8.2__py3-none-any.whl → 25.9.0__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 (143) hide show
  1. lino/__init__.py +1 -1
  2. lino/api/dd.py +0 -1
  3. lino/config/unused/403.html +1 -1
  4. lino/config/unused/404.html +1 -1
  5. lino/config/unused/500.html +1 -1
  6. lino/core/__init__.py +0 -1
  7. lino/core/actions.py +2 -2
  8. lino/core/actors.py +10 -2
  9. lino/core/elems.py +1 -1
  10. lino/core/fields.py +4 -1
  11. lino/core/kernel.py +5 -1
  12. lino/core/model.py +2 -11
  13. lino/core/renderer.py +2 -2
  14. lino/core/requests.py +12 -12
  15. lino/core/site.py +5 -82
  16. lino/core/store.py +3 -1
  17. lino/core/urls.py +1 -1
  18. lino/core/user_types.py +1 -10
  19. lino/help_texts.py +6 -6
  20. lino/management/commands/initdb.py +0 -3
  21. lino/modlib/__init__.py +0 -1
  22. lino/modlib/bootstrap5/README.txt +2 -0
  23. lino/modlib/bootstrap5/__init__.py +69 -0
  24. lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/base.html +9 -4
  25. lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/detail.html +1 -1
  26. lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/index.html +1 -1
  27. lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/table.html +1 -1
  28. lino/modlib/bootstrap5/models.py +30 -0
  29. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.css +4085 -0
  30. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.css.map +1 -0
  31. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.min.css +6 -0
  32. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.min.css.map +1 -0
  33. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.css +4084 -0
  34. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.css.map +1 -0
  35. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.min.css +6 -0
  36. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.min.css.map +1 -0
  37. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.css +597 -0
  38. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.css.map +1 -0
  39. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.min.css +6 -0
  40. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.min.css.map +1 -0
  41. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.css +594 -0
  42. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.css.map +1 -0
  43. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.min.css +6 -0
  44. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.min.css.map +1 -0
  45. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.css +5406 -0
  46. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.css.map +1 -0
  47. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.min.css +6 -0
  48. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.min.css.map +1 -0
  49. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.css +5397 -0
  50. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.css.map +1 -0
  51. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.min.css +6 -0
  52. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.min.css.map +1 -0
  53. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.css +12043 -0
  54. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.css.map +1 -0
  55. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.min.css +6 -0
  56. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.min.css.map +1 -0
  57. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.css +12016 -0
  58. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.css.map +1 -0
  59. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.min.css +6 -0
  60. lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.min.css.map +1 -0
  61. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.js +6315 -0
  62. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.js.map +1 -0
  63. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.min.js +7 -0
  64. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.min.js.map +1 -0
  65. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.js +4450 -0
  66. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.js.map +1 -0
  67. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.min.js +7 -0
  68. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.min.js.map +1 -0
  69. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.js +4497 -0
  70. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.js.map +1 -0
  71. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.min.js +7 -0
  72. lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.min.js.map +1 -0
  73. lino/modlib/{bootstrap3 → bootstrap5}/views.py +12 -117
  74. lino/modlib/checkdata/choicelists.py +1 -1
  75. lino/modlib/comments/fixtures/demo2.py +1 -0
  76. lino/modlib/comments/mixins.py +1 -8
  77. lino/modlib/comments/models.py +2 -0
  78. lino/modlib/comments/ui.py +7 -7
  79. lino/modlib/extjs/__init__.py +2 -4
  80. lino/modlib/extjs/config/extjs/index.html +1 -1
  81. lino/modlib/extjs/ext_renderer.py +1 -7
  82. lino/modlib/extjs/views.py +2 -0
  83. lino/modlib/help/models.py +1 -12
  84. lino/modlib/jinja/renderer.py +1 -1
  85. lino/modlib/linod/mixins.py +3 -2
  86. lino/modlib/memo/__init__.py +11 -11
  87. lino/modlib/memo/mixins.py +38 -21
  88. lino/modlib/memo/models.py +10 -7
  89. lino/modlib/memo/parser.py +3 -1
  90. lino/modlib/notify/models.py +6 -9
  91. lino/modlib/odata/views.py +7 -7
  92. lino/modlib/publisher/__init__.py +15 -3
  93. lino/modlib/publisher/choicelists.py +8 -94
  94. lino/modlib/publisher/config/publisher/page.pub.html +82 -19
  95. lino/modlib/publisher/fixtures/std.py +14 -1
  96. lino/modlib/publisher/fixtures/synodalworld.py +3 -1
  97. lino/modlib/publisher/mixins.py +59 -77
  98. lino/modlib/publisher/models.py +109 -204
  99. lino/modlib/publisher/renderer.py +31 -11
  100. lino/modlib/publisher/ui.py +46 -98
  101. lino/modlib/publisher/views.py +61 -11
  102. lino/modlib/system/models.py +3 -2
  103. lino/modlib/uploads/__init__.py +1 -0
  104. lino/modlib/uploads/mixins.py +2 -2
  105. lino/modlib/uploads/models.py +55 -21
  106. lino/modlib/uploads/ui.py +1 -0
  107. lino/modlib/uploads/utils.py +2 -2
  108. lino/modlib/users/__init__.py +2 -3
  109. lino/modlib/users/actions.py +12 -17
  110. lino/modlib/users/fixtures/abc.py +20 -0
  111. lino/modlib/users/mixins.py +6 -6
  112. lino/modlib/users/models.py +37 -36
  113. lino/modlib/weasyprint/__init__.py +25 -14
  114. lino/modlib/weasyprint/choicelists.py +6 -0
  115. lino/modlib/weasyprint/config/weasyprint/base.weasy.html +43 -27
  116. lino/utils/diag.py +5 -3
  117. lino/utils/html.py +103 -0
  118. lino/utils/mldbc/mixins.py +2 -2
  119. lino/utils/soup.py +16 -8
  120. {lino-25.8.2.dist-info → lino-25.9.0.dist-info}/METADATA +1 -1
  121. {lino-25.8.2.dist-info → lino-25.9.0.dist-info}/RECORD +135 -95
  122. lino/modlib/bootstrap3/README.txt +0 -2
  123. lino/modlib/bootstrap3/__init__.py +0 -73
  124. lino/modlib/bootstrap3/models.py +0 -30
  125. lino/modlib/bootstrap3/static/bootstrap-3.3.4/css/bootstrap.css +0 -6584
  126. lino/modlib/bootstrap3/static/bootstrap-3.3.4/css/bootstrap.css.map +0 -1
  127. lino/modlib/bootstrap3/static/bootstrap-3.3.4/css/bootstrap.min.css +0 -5
  128. lino/modlib/bootstrap3/static/bootstrap-3.3.4/js/bootstrap.js +0 -2317
  129. lino/modlib/bootstrap3/static/bootstrap-3.3.4/js/bootstrap.min.js +0 -7
  130. /lino/modlib/{bootstrap3 → bootstrap5}/renderer.py +0 -0
  131. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/css/bootstrap-theme.css +0 -0
  132. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/css/bootstrap-theme.css.map +0 -0
  133. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/css/bootstrap-theme.min.css +0 -0
  134. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.eot +0 -0
  135. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.svg +0 -0
  136. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.ttf +0 -0
  137. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.woff +0 -0
  138. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.woff2 +0 -0
  139. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/js/bootstrap_lino.js +0 -0
  140. /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/js/npm.js +0 -0
  141. {lino-25.8.2.dist-info → lino-25.9.0.dist-info}/WHEEL +0 -0
  142. {lino-25.8.2.dist-info → lino-25.9.0.dist-info}/licenses/AUTHORS.rst +0 -0
  143. {lino-25.8.2.dist-info → lino-25.9.0.dist-info}/licenses/COPYING +0 -0
lino/__init__.py CHANGED
@@ -31,7 +31,7 @@ from django import VERSION
31
31
  from django.apps import AppConfig
32
32
  from django.conf import settings
33
33
  import warnings
34
- __version__ = '25.8.2'
34
+ __version__ = '25.9.0'
35
35
 
36
36
  # import setuptools # avoid UserWarning "Distutils was imported before Setuptools"?
37
37
 
lino/api/dd.py CHANGED
@@ -189,7 +189,6 @@ resolve_plugin = settings.SITE.resolve_plugin
189
189
  get_plugin_setting = settings.SITE.get_plugin_setting
190
190
  add_welcome_handler = settings.SITE.add_welcome_handler
191
191
  build_media_url = settings.SITE.build_media_url
192
- build_site_cache_url = settings.SITE.build_site_cache_url
193
192
  build_static_url = settings.SITE.build_static_url
194
193
  get_default_language = settings.SITE.get_default_language
195
194
  get_language_info = settings.SITE.get_language_info
@@ -1,4 +1,4 @@
1
- {% extends "bootstrap3/base.html" %}
1
+ {% extends "bootstrap5/base.html" %}
2
2
 
3
3
  {% block title %}{{_('Permission denied')}}{% endblock %}
4
4
 
@@ -1,4 +1,4 @@
1
- {% extends "bootstrap3/base.html" %}
1
+ {% extends "bootstrap5/base.html" %}
2
2
 
3
3
  {% block title %}{{_('Page not found')}}{% endblock %}
4
4
 
@@ -1,4 +1,4 @@
1
- {% extends "bootstrap3/base.html" %}
1
+ {% extends "bootstrap5/base.html" %}
2
2
 
3
3
  {% block title %}{{_('Server error (500)')}}{% endblock %}
4
4
 
lino/core/__init__.py CHANGED
@@ -39,7 +39,6 @@ For some modules the documentation has already been migrated to prosa:
39
39
  tables
40
40
  urls
41
41
  userprefs
42
- user_types
43
42
  utils
44
43
  views
45
44
  workflows
lino/core/actions.py CHANGED
@@ -595,7 +595,7 @@ class SubmitDetail(SaveGridCell):
595
595
  ar.goto_instance(elem)
596
596
  else:
597
597
  if len(ar.selected_rows) == 1:
598
- ar.set_response(data_record=ar.elem2rec_detailed(elem))
598
+ ar.success(data_record=ar.elem2rec_detailed(elem))
599
599
 
600
600
 
601
601
  class CreateRow(Action):
@@ -987,7 +987,7 @@ class ShowEditor(Action):
987
987
  ar.master_instance.__class__).pk
988
988
  })
989
989
  ar.set_response(goto_url=ar.renderer.front_end.build_plain_url(
990
- "api", *ar.actor.actor_id.split("."), str(ar.selected_rows[0].pk), self.buddy_name, **kw))
990
+ "#", "api", *ar.actor.actor_id.split("."), str(ar.selected_rows[0].pk), self.buddy_name, **kw))
991
991
 
992
992
 
993
993
  # Some actions are described by a single action instance used by most actors:
lino/core/actors.py CHANGED
@@ -265,12 +265,12 @@ class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
265
265
 
266
266
  hidden_elements = frozenset()
267
267
 
268
- detail_html_template = "bootstrap3/detail.html"
268
+ detail_html_template = "bootstrap5/detail.html"
269
269
  """The template to be used for rendering a row of this actor as a
270
270
  detail html page.
271
271
 
272
272
  """
273
- list_html_template = "bootstrap3/table.html"
273
+ list_html_template = "bootstrap5/table.html"
274
274
  """The template to be used for rendering a collection of rows of this
275
275
  actor as a table html page.
276
276
 
@@ -1715,6 +1715,14 @@ class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
1715
1715
  kw = self.model.param_defaults(ar, **kw)
1716
1716
  return kw
1717
1717
 
1718
+ # @classmethod
1719
+ # def get_parent_links(cls, ar):
1720
+ # if cls.model is not None:
1721
+ # for pl in cls.model.get_parent_links(ar):
1722
+ # yield pl
1723
+ # # if (mi := ar.master_instance) is not None:
1724
+ # # yield ar.obj2htmls(mi, str(mi))
1725
+
1718
1726
  @classmethod
1719
1727
  def request(cls, *args, **kwargs):
1720
1728
  """
lino/core/elems.py CHANGED
@@ -37,7 +37,7 @@ from lino.core import actions
37
37
  from lino.core.utils import resolve_model
38
38
  from lino.core.gfks import GenericRelation, GenericRel
39
39
  from lino.core.permissions import Permittable
40
- from lino.modlib.bootstrap3.views import table2html
40
+ from lino.utils.html import table2html
41
41
 
42
42
  from lino.utils.jsgen import VisibleComponent
43
43
  from lino.utils.html import E, tostring, forcetext, html2text
lino/core/fields.py CHANGED
@@ -1265,7 +1265,7 @@ class ImportedFields(object):
1265
1265
  # ~ cls,cls._imported_fields))
1266
1266
 
1267
1267
 
1268
- class TableRow(object):
1268
+ class TableRow:
1269
1269
  """Base class for everything that can be used as a table row."""
1270
1270
 
1271
1271
  _lino_default_table = None
@@ -1420,6 +1420,9 @@ class TableRow(object):
1420
1420
  # raise Exception("20230425 {}".format(ar.actor))
1421
1421
  return a
1422
1422
 
1423
+ def get_parent_links(self, ar):
1424
+ return []
1425
+
1423
1426
  def get_choices_text(self, ar, actor, field):
1424
1427
  return self.as_str(ar)
1425
1428
  # return str(self)
lino/core/kernel.py CHANGED
@@ -601,13 +601,17 @@ class Kernel(object):
601
601
  # for p in self.web_front_ends]))
602
602
 
603
603
  editing_wf = None
604
+ primary_wf = self.web_front_ends[0]
604
605
  for p in self.web_front_ends:
606
+ # if primary_wf is None and not p.url_prefix:
607
+ # primary_wf = p
605
608
  if p.ui_handle_attr_name is not None:
606
609
  editing_wf = p
607
610
  break
608
611
 
609
612
  self.editing_front_end = editing_wf
610
- self.html_renderer = HtmlRenderer(self.web_front_ends[0])
613
+ self.primary_front_end = primary_wf
614
+ self.html_renderer = HtmlRenderer(primary_wf)
611
615
  self.text_renderer = TextRenderer(editing_wf)
612
616
 
613
617
  for p in site.installed_plugins:
lino/core/model.py CHANGED
@@ -44,7 +44,7 @@ from .tables import AbstractTable
44
44
 
45
45
  class Model(models.Model, fields.TableRow):
46
46
 
47
- class Meta(object):
47
+ class Meta:
48
48
  abstract = True
49
49
 
50
50
  allow_cascaded_delete = frozenset()
@@ -436,16 +436,6 @@ class Model(models.Model, fields.TableRow):
436
436
  new = old
437
437
  else:
438
438
  new = sanitize(old, **kwargs)
439
- # try:
440
- # new = bleach.clean(
441
- # new,
442
- # tags=settings.SITE.bleach_allowed_tags,
443
- # attributes=settings.SITE.bleach_allowed_attributes,
444
- # strip=True,
445
- # )
446
- # except TypeError as e:
447
- # logger.warning("Could not bleach %r : %s (%s)", old, e, self)
448
- # continue
449
439
  if old != new:
450
440
  logger.debug("Bleaching %s from %r to %r", f.name, old, new)
451
441
  yield f, old, new
@@ -1003,6 +993,7 @@ LINO_MODEL_ATTRIBS = (
1003
993
  "show_in_site_search",
1004
994
  "allow_merge_action",
1005
995
  "get_overview_elems",
996
+ "get_parent_links",
1006
997
  )
1007
998
 
1008
999
 
lino/core/renderer.py CHANGED
@@ -89,7 +89,7 @@ def add_user_language(kw, ar):
89
89
  # ~ request.LANGUAGE_CODE = translation.get_language()
90
90
 
91
91
 
92
- class Renderer(object):
92
+ class Renderer:
93
93
  """
94
94
  Base class for all Lino renderers.
95
95
 
@@ -709,7 +709,7 @@ class HtmlRenderer(Renderer):
709
709
  """
710
710
  Render the given menu as an HTML etree element.
711
711
 
712
- Used by bootstrap3 front end.
712
+ Used by bootstrap5 front end.
713
713
  """
714
714
  if not isinstance(mnu, Menu):
715
715
  assert isinstance(mnu, MenuItem)
lino/core/requests.py CHANGED
@@ -478,6 +478,7 @@ class BaseRequest:
478
478
  filter=None,
479
479
  gridfilters=None,
480
480
  extra=None,
481
+ selected_rows=None,
481
482
  ):
482
483
  if logger is not None:
483
484
  self.logger = logger
@@ -503,6 +504,9 @@ class BaseRequest:
503
504
  if xcallback_answers is not None:
504
505
  self.xcallback_answers = xcallback_answers
505
506
 
507
+ if selected_rows is not None:
508
+ self.selected_rows = selected_rows
509
+
506
510
  if self.actor is not None:
507
511
  if master is None:
508
512
  master = self.actor.master
@@ -1672,17 +1676,18 @@ class BaseRequest:
1672
1676
  return rec
1673
1677
 
1674
1678
  def get_breadcrumbs(self, elem=None):
1679
+ # print("20250910 get_breadcrumbs", self)
1675
1680
  list_title = self.get_title()
1676
- # TODO: make it clickable so that we can return from detail to list view
1677
- if elem is None or self.actor.default_record_id is not None:
1678
- return list_title
1679
- else:
1680
- # print("20190703", self.actor, self.actor.default_action)
1681
+ if self.actor.default_record_id is None:
1681
1682
  sar = self.spawn_request(actor=self.actor)
1682
1683
  list_title = tostring(sar.href_to_request(
1683
1684
  sar, list_title, icon_name=None))
1684
- # return list_title + " » " + self.get_detail_title(elem)
1685
- return format_html("{} » {}", list_title, self.get_detail_title(elem))
1685
+ if elem is not None:
1686
+ list_title = format_html(
1687
+ "{} » {}", list_title, self.get_detail_title(elem))
1688
+ for pl in elem.get_parent_links(self):
1689
+ list_title = format_html("{} » {}", pl, list_title)
1690
+ return list_title
1686
1691
 
1687
1692
  def form2obj_and_save(ar, data, elem, is_new):
1688
1693
  """
@@ -1813,7 +1818,6 @@ class ActionRequest(BaseRequest):
1813
1818
  title=None,
1814
1819
  exclude=None,
1815
1820
  selected_pks=None,
1816
- selected_rows=None,
1817
1821
  **kw
1818
1822
  ):
1819
1823
  if exclude is not None:
@@ -1825,10 +1829,6 @@ class ActionRequest(BaseRequest):
1825
1829
  if limit is not None:
1826
1830
  self.limit = limit
1827
1831
 
1828
- if selected_rows is not None:
1829
- assert selected_pks is None
1830
- self.selected_rows = selected_rows
1831
-
1832
1832
  super().setup(**kw)
1833
1833
 
1834
1834
  if self.bound_action is None:
lino/core/site.py CHANGED
@@ -262,7 +262,6 @@ class Site(object):
262
262
  use_elasticsearch = False
263
263
  use_solr = False
264
264
  developer_site_cache = None
265
- never_build_site_cache = False
266
265
  keep_erroneous_cache_files = False
267
266
  use_java = True
268
267
  use_systemd = False
@@ -1131,48 +1130,8 @@ class Site(object):
1131
1130
  # sfd = tuple([x for x in sfd if x != root])
1132
1131
  # self.update_settings(STATICFILES_DIRS=sfd)
1133
1132
 
1134
- # if self.build_js_cache_on_startup or self.never_build_site_cache:
1135
- # sfd = list(self.django_settings.get('STATICFILES_DIRS', []))
1136
- # sfd.append(self.media_root)
1137
- # self.update_settings(STATICFILES_DIRS=sfd)
1138
-
1139
1133
  # print(20150331, self.django_settings['FIXTURE_DIRS'])
1140
1134
 
1141
- # def setup_cache_directory(self):
1142
- # stamp = self.site_dir / "lino_cache.txt"
1143
- # this = class2str(self.__class__)
1144
- # if stamp.exists():
1145
- # other = stamp.read_file()
1146
- # if other == this:
1147
- # ok = True
1148
- # else:
1149
- # ok = False
1150
- # for parent in self.__class__.__mro__:
1151
- # if other == class2str(parent):
1152
- # ok = True
1153
- # break
1154
- # if not ok:
1155
- # # Can happen e.g. when `python -m lino.hello` is
1156
- # # called. in certain conditions.
1157
- # msg = (
1158
- # "Cannot use {site_dir} for {this} "
1159
- # "because it is used for {other}. (Settings {settings})"
1160
- # )
1161
- # msg = msg.format(
1162
- # site_dir=self.site_dir,
1163
- # this=this,
1164
- # settings=self.django_settings.get("SETTINGS_MODULE"),
1165
- # other=other,
1166
- # )
1167
- # if True:
1168
- # raise Exception(msg)
1169
- # else:
1170
- # # print(msg)
1171
- # self.site_dir = None
1172
- # else:
1173
- # self.makedirs_if_missing(self.site_dir)
1174
- # stamp.write_file(this)
1175
-
1176
1135
  def set_user_model(self, spec):
1177
1136
  # if self.user_model is not None:
1178
1137
  # msg = "Site.user_model was already set!"
@@ -2022,44 +1981,12 @@ class Site(object):
2022
1981
  def build_site_cache(self, force=False, later=False, verbosity=1):
2023
1982
  from lino.modlib.users.utils import with_user_profile
2024
1983
  from lino.modlib.users.choicelists import UserTypes
2025
- # from django.utils import translation
2026
- # if not self.is_prepared:
2027
- # self.prepare_layouts()
2028
- # self.is_prepared = True
2029
- # settings_file = self.django_settings.get("__file__")
2030
- # Path(settings_file).touch()
2031
- # p = self.site_dir / "lino_version.txt"
2032
- # p.touch()
2033
1984
  self.kernel.touch_lino_version()
2034
1985
 
2035
1986
  if later:
2036
1987
  # print("20230823 later")
2037
1988
  return
2038
1989
 
2039
- if self.never_build_site_cache:
2040
- self.logger.debug(
2041
- "Not building site cache because `settings.SITE.never_build_site_cache` is True"
2042
- )
2043
- # print("20230823 never")
2044
- return
2045
-
2046
- # logger.info("20140401 build_site_cache started")
2047
- if False:
2048
- # 20240907 until now the Site class creates the media directory if
2049
- # it doesn't exist, but the initdb command removes it again. So the
2050
- # following code seems really useless:
2051
- if not self.media_root.is_dir():
2052
- self.media_root.mkdir()
2053
- # try:
2054
- # settings.SITE.media_root.mkdir()
2055
- # except Exception as e:
2056
- # logger.debug(
2057
- # "Not building site cache because 'mkdir %s' says %s.",
2058
- # settings.SITE.media_root, e)
2059
- # return
2060
-
2061
- self.makedirs_if_missing(self.media_root / "webdav")
2062
-
2063
1990
  if verbosity > 0:
2064
1991
  self.logger.info("Build site cache in %s.", self.media_root)
2065
1992
 
@@ -2151,9 +2078,9 @@ class Site(object):
2151
2078
 
2152
2079
  # if self.default_ui == "extjs":
2153
2080
  # yield 'lino.modlib.extjs'
2154
- # yield 'lino.modlib.bootstrap3'
2155
- # elif self.default_ui == "bootstrap3":
2156
- # yield 'lino.modlib.bootstrap3'
2081
+ # yield 'lino.modlib.bootstrap5'
2082
+ # elif self.default_ui == "bootstrap5":
2083
+ # yield 'lino.modlib.bootstrap5'
2157
2084
 
2158
2085
  # yield "lino.modlib.lino_startup"
2159
2086
 
@@ -2177,6 +2104,8 @@ class Site(object):
2177
2104
  This must *start and end* with a *slash*. Default value is
2178
2105
  ``'/'``.
2179
2106
 
2107
+ Don't change this. Other values than the default value are not tested.
2108
+
2180
2109
  This must be set if your project is not being served at the "root"
2181
2110
  URL of your server.
2182
2111
 
@@ -2218,12 +2147,6 @@ class Site(object):
2218
2147
  def build_static_url(self, *args, **kw):
2219
2148
  return buildurl(settings.STATIC_URL, *args, **kw)
2220
2149
 
2221
- def build_site_cache_url(self, *args, **kw):
2222
- assert str(self.media_root) == str(settings.MEDIA_ROOT)
2223
- # if str(self.media_root) != str(settings.MEDIA_ROOT):
2224
- # return self.build_static_url(*args, **kw)
2225
- return self.build_media_url(*args, **kw)
2226
-
2227
2150
  def welcome_html(self, ui=None):
2228
2151
  from django.utils.translation import gettext as _
2229
2152
 
lino/core/store.py CHANGED
@@ -92,7 +92,9 @@ class StoreField(object):
92
92
  self.name = str(name) # TypeError 20160425
93
93
  self.options = options
94
94
  # if settings.SITE.kernel.default_ui.support_async:
95
- if settings.SITE.default_ui == "lino_react.react":
95
+ # if settings.SITE.default_ui == "lino_react.react":
96
+ editing_wf = settings.SITE.kernel.editing_front_end
97
+ if editing_wf and editing_wf.support_async:
96
98
  if isinstance(field, fields.FakeField) and field.delayed_value:
97
99
  self.delayed_value = True
98
100
  # print("20210619 StoreField.delayedValue is True", name)
lino/core/urls.py CHANGED
@@ -83,4 +83,4 @@ if is_devserver():
83
83
  # pat = r'^{0}(?P<path>.*)$'.format(settings.STATIC_URL[1:])
84
84
  # urlpatterns.append(url(pat, serve))
85
85
 
86
- # print("20210114", urlpatterns)
86
+ # print("20250814", '\n'.join(map(str, urlpatterns)))
lino/core/user_types.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2015-2018 Rumma & Ko Ltd
1
+ # Copyright 2015-2025 Rumma & Ko Ltd
2
2
  # License: GNU Affero General Public License v3 (see file COPYING for details)
3
3
  """
4
4
  Defines a set of user types "Anonymous", "User" and
@@ -10,17 +10,8 @@ This can be used directly as :attr:`user_types_module
10
10
 
11
11
  from django.utils.translation import gettext_lazy as _
12
12
  from lino.core.roles import UserRole, SiteAdmin, SiteUser
13
-
14
- # from lino.modlib.office.roles import OfficeUser, OfficeStaff
15
13
  from lino.modlib.users.choicelists import UserTypes
16
14
 
17
- # class SiteAdmin(SiteAdmin, OfficeStaff):
18
- # pass
19
- #
20
- # class SiteUser(SiteUser, OfficeUser):
21
- # pass
22
-
23
- UserTypes.clear()
24
15
  add = UserTypes.add_item
25
16
  add("000", _("Anonymous"), UserRole, name="anonymous", readonly=True)
26
17
  add("100", _("User"), SiteUser, name="user")
lino/help_texts.py CHANGED
@@ -103,16 +103,11 @@ help_texts = {
103
103
  'lino.mixins.sequenced.Hierarchical.whole_clan' : _("""Return a set of this instance and all children and grandchildren."""),
104
104
  'lino.mixins.sequenced.Hierarchical.whole_tree' : _("""Returns a tuple with two items (obj, children) representing the whole tree."""),
105
105
  'lino.modlib.about.Plugin' : _("""See /dev/plugins."""),
106
- 'lino.modlib.bootstrap3.renderer.Renderer' : _("""A HTML render that uses Bootstrap3."""),
107
- 'lino.modlib.bootstrap3.views.List' : _("""Render a list of records."""),
108
- 'lino.modlib.bootstrap3.views.Element' : _("""Render a single record."""),
109
- 'lino.modlib.bootstrap3.views.Index' : _("""Render the main page."""),
110
106
  'lino.modlib.checkdata.Plugin' : _("""The config descriptor for this plugin."""),
111
107
  'lino.modlib.checkdata.Plugin.on_plugins_loaded' : _("""Set responsible_user to "'robin' if this is a demo site (is_demo_site)."""),
112
108
  'lino.modlib.checkdata.roles.CheckdataUser' : _("""Can see checkdata messages."""),
113
109
  'lino.modlib.comments.Plugin' : _("""See /dev/plugins."""),
114
110
  'lino.modlib.comments.Plugin.emotion_range' : _("""Which range of emotion icons to provide. Either “business” or “social”."""),
115
- 'lino.modlib.comments.Plugin.private_default' : _("""Whether comments are private by default."""),
116
111
  'lino.modlib.dashboard.Plugin' : _("""See /dev/plugins."""),
117
112
  'lino.modlib.dashboard.UpdateWidgets' : _("""Create or update the dashboard widgets for this user."""),
118
113
  'lino.modlib.export_excel.Plugin' : _("""See /dev/plugins."""),
@@ -217,6 +212,7 @@ help_texts = {
217
212
  'lino.utils.diag.Analyzer.show_window_fields' : _("""List all window actions and the form fields they contain."""),
218
213
  'lino.utils.diag.Analyzer.show_window_permissions' : _("""List all window actions and the user types that can see them."""),
219
214
  'lino.utils.diag.Analyzer.show_memo_commands' : _("""List the memo commands defined in this application."""),
215
+ 'lino.utils.diag.Analyzer.show_db_structure' : _("""Show a bullet list of all models and their fields."""),
220
216
  'lino.utils.diag.Analyzer.show_database_structure' : _("""Show a bullet list of all models and their fields."""),
221
217
  'lino.utils.diag.Analyzer.show_db_overview' : _("""Print a reStructredText-formatted “database overview” report. Used by test cases in tested documents."""),
222
218
  'lino.utils.diag.Analyzer.show_foreign_keys' : _("""Return a list that shows how database objects are being referred to by some other database object. This information is important (1) before deleting objects and (2) when merging them."""),
@@ -400,7 +396,6 @@ help_texts = {
400
396
  'lino.modlib.publisher.PublishableContent.publishing_state' : _("""Default value is ‘draft’"""),
401
397
  'lino.modlib.publisher.PublishableContent.filler' : _("""Pointer to PageFillers"""),
402
398
  'lino.modlib.publisher.PublishingStates' : _("""A choicelist with the possible states of a publisher page."""),
403
- 'lino.modlib.publisher.PageFillers' : _("""A choicelist with the page fillers that are available for this application."""),
404
399
  'lino.modlib.publisher.SpecialPages' : _("""A choicelist with the special pages available on this site."""),
405
400
  'lino.modlib.search.SiteSearch' : _("""A virtual table that searches in all database tables."""),
406
401
  'lino.modlib.search.ElasticSiteSearch' : _("""A virtual table used to search on this Lino site using ElasticSearch."""),
@@ -487,6 +482,9 @@ help_texts = {
487
482
  'lino.modlib.users.UserPlan.create_user_plan' : _("""Return the database object for this plan and user. or create"""),
488
483
  'lino.modlib.users.UserPlan.update_plan' : _("""Implementing models should provide this method."""),
489
484
  'lino.modlib.users.UpdatePlan' : _("""Build a new list of suggestions. This will remove all current suggestions."""),
485
+ 'lino.modlib.users.PrivacyRelevant' : _("""Model mixin to mark a database model as privacy-relevant data, i.e. something that is to be shown only to members of a given user group."""),
486
+ 'lino.modlib.users.PrivacyRelevant.private' : _("""Whether this row is confidential."""),
487
+ 'lino.modlib.users.PrivacyRelevant.group' : _("""The user group this row belongs to."""),
490
488
  'lino.modlib.about.About.sign_in' : _("""Ask for your username and password in order to authenticate."""),
491
489
  'lino.modlib.about.About.reset_password' : _("""Ask for your email address and send a verification code."""),
492
490
  'lino.modlib.about.About.verify_user' : _("""Ask for the verification code you have received by email and mark your email address as verified."""),
@@ -496,6 +494,7 @@ help_texts = {
496
494
  'lino.modlib.users.User.coaching_supervisor' : _("""Notify me when a coach has been assigned."""),
497
495
  'lino.modlib.comments.Comment' : _("""The database model to represent a comment."""),
498
496
  'lino.modlib.comments.Comment.user' : _("""The author of the comment."""),
497
+ 'lino.modlib.comments.Comment.group' : _("""The user group this comment belongs to."""),
499
498
  'lino.modlib.comments.Comment.owner' : _("""The discussion topic this comment is about."""),
500
499
  'lino.modlib.comments.Comment.body' : _("""The full body text of your comment."""),
501
500
  'lino.modlib.comments.Comment.short_preview' : _("""The first paragraph of your body."""),
@@ -516,6 +515,7 @@ help_texts = {
516
515
  'lino.modlib.comments.Commentable' : _("""Mixin for models that are commentable, i.e. the rows of which can become discussion topic of comments."""),
517
516
  'lino.modlib.comments.Commentable.add_comments_filter' : _("""Add filters to the given queryset of comments, requested by the given user."""),
518
517
  'lino.modlib.comments.Commentable.get_rfc_description' : _("""Return a HTML formatted string with the description of this Commentable as it should be displayed by the slave summary of CommentsByRFC."""),
518
+ 'lino.modlib.comments.Commentable.on_create_comment' : _("""Called when a comment about this is being created."""),
519
519
  'lino.modlib.comments.Commentable.on_commented' : _("""This is automatically called when a comment has been created or modified."""),
520
520
  'lino.modlib.files.Volume' : _("""The Django model representing a file volume."""),
521
521
  'lino.modlib.files.Volume.id' : _("""The primary key used to point to this volume from a database object."""),
@@ -301,9 +301,6 @@ class Command(BaseCommand):
301
301
  # if engine == 'django.db.backends.postgresql':
302
302
  # foralltables(using, "ALTER TABLE {} ENABLE TRIGGER ALL;")
303
303
 
304
- # if buildcache: # why did we add this at all?
305
- # settings.SITE.build_site_cache(verbosity=verbosity)
306
-
307
304
  settings.SITE.clear_site_config()
308
305
 
309
306
  # dblogger.info("Lino initdb %s done on database %s.", args, dbname)
lino/modlib/__init__.py CHANGED
@@ -30,7 +30,6 @@ Front ends
30
30
  .. autosummary::
31
31
  :toctree:
32
32
 
33
- bootstrap3
34
33
  extjs
35
34
 
36
35
  Optional features
@@ -0,0 +1,2 @@
1
+ The content of directory `static/bootstrap-5.3.7` comes from
2
+ https://getbootstrap.com
@@ -0,0 +1,69 @@
1
+ # -*- coding: UTF-8 -*-
2
+ # Copyright 2009-2018 Rumma & Ko Ltd
3
+ # License: GNU Affero General Public License v3 (see file COPYING for details)
4
+ """
5
+ This started as a copy of :mod:`lino.modlib.plain` and moved to the
6
+ version 3 of `Bootstrap <https://getbootstrap.com/>`_ CSS toolkit.
7
+
8
+ .. autosummary::
9
+ :toctree:
10
+
11
+ views
12
+ renderer
13
+ models
14
+ """
15
+
16
+ from lino.api.ad import Plugin
17
+
18
+
19
+ class Plugin(Plugin):
20
+ # ui_label = _("Bootstrap")
21
+ # site_js_snippets = ['snippets/plain.js']
22
+ needs_plugins = ["lino.modlib.jinja"]
23
+ media_name = "bootstrap-5.3.7"
24
+ # media_base_url = "http://maxcdn.bootstrapcdn.com/bootstrap/5.3.7/"
25
+
26
+ # ui_handle_attr_name = "bootstrap5_handle"
27
+ # url_prefix = "bs5"
28
+ #
29
+ # def on_ui_init(self, kernel):
30
+ # from .renderer import Renderer
31
+ #
32
+ # self.renderer = Renderer(self)
33
+ # # ui.bs5_renderer = self.renderer
34
+ #
35
+ # def get_patterns(self):
36
+ # # from django.conf.urls import url
37
+ # from django.urls import re_path as url
38
+ # from . import views
39
+ #
40
+ # rx = "^"
41
+ #
42
+ # urls = [
43
+ # # url(rx + r'/?$', views.Index.as_view()),
44
+ # url(rx + r"$", views.Index.as_view()),
45
+ # url(rx + r"auth", views.Authenticate.as_view()),
46
+ # # NB app_label must be at least 3 chars long to avoid clash with
47
+ # # publisher patterns
48
+ # url(rx + r"(?P<app_label>\w\w\w+)/(?P<actor>\w+)$", views.List.as_view()),
49
+ # url(
50
+ # rx + r"(?P<app_label>\w\w\w+)/(?P<actor>\w+)/(?P<pk>.+)$",
51
+ # views.Element.as_view(),
52
+ # ),
53
+ # ]
54
+ # return urls
55
+
56
+ def get_detail_url(self, ar, actor, pk, *args, **kw):
57
+ return self.build_plain_url(
58
+ actor.app_label, actor.__name__, str(pk), *args, **kw
59
+ )
60
+
61
+ def get_used_libs(self, html=False):
62
+ if html is not None:
63
+ yield ("Bootstrap", "5.3.7", "https://getbootstrap.com")
64
+ # yield ("jQuery", '?', "http://...")
65
+
66
+ # def get_index_view(self):
67
+ # from . import views
68
+ #
69
+ # return views.Index.as_view()
@@ -1,11 +1,16 @@
1
1
  <!DOCTYPE html>
2
2
  <html language="{{requested_language}}"><head>
3
3
  <meta charset="utf-8"/>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" />
4
5
  <title>{% block title %}{{site.title or site.verbose_name}}{% endblock %}</title>
5
- <link rel="stylesheet" href="{{site.plugins.bootstrap3.build_lib_url('css','bootstrap.css')}}" type="text/css">
6
+ <link rel="stylesheet" href="{{site.plugins.bootstrap5.build_lib_url('css','bootstrap.css')}}" type="text/css">
6
7
  <link rel="stylesheet" href="{{site.build_static_url('bootstrap.css')}}" type="text/css">
7
8
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/timepicker@1.11.12/jquery.timepicker.min.css" type="text/css">
9
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@enzedonline/quill-blot-formatter2/dist/css/quill-blot-formatter2.css">
10
+ <link rel="stylesheet" href="https://unpkg.com/primeicons/primeicons.css"/>
11
+ {# Do we need jquery? Isn't this an invalid URL?
8
12
  <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
13
+ #}
9
14
  <link rel="shortcut icon" type="image/png" href="{{ site.build_static_url('favicon2.ico') }}" />
10
15
 
11
16
  </head><body>
@@ -21,7 +26,7 @@
21
26
  <form class="form-inline" method="POST"
22
27
  action="{{ar.renderer.front_end.build_plain_url('auth')}}">
23
28
  <input name="username" type="username" class="input-small" placeholder="Username">
24
- <input name="password" type"password" class="input-small" placeholder="Password">
29
+ <input name="password" type="password" class="input-small" placeholder="Password">
25
30
  <button type="submit" class="btn">{{_("Sign in")}}</button>
26
31
  &mdash;
27
32
  {% endif -%}
@@ -101,7 +106,7 @@
101
106
  <!-- Placed at the end of the document so the pages load faster -->
102
107
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
103
108
  <script src="https://code.jquery.com/ui/1.11.1/jquery-ui.js"></script>
104
- <script src="{{site.plugins.bootstrap3.build_lib_url('js','bootstrap.min.js')}}"></script>
109
+ <script src="{{site.plugins.bootstrap5.build_lib_url('js','bootstrap.min.js')}}"></script>
105
110
  <script src="https://cdn.jsdelivr.net/npm/timepicker@1.11.12/jquery.timepicker.min.js"></script>
106
- <script src="{{site.plugins.bootstrap3.build_lib_url('js','bootstrap_lino.js')}}"></script>
111
+ <script src="{{site.plugins.bootstrap5.build_lib_url('js','bootstrap_lino.js')}}"></script>
107
112
  </body></html>
@@ -1,4 +1,4 @@
1
- {% extends "bootstrap3/base.html" %}
1
+ {% extends "bootstrap5/base.html" %}
2
2
  {% set active_page = "" %}
3
3
  {% block title %}{{title}}{% endblock %}
4
4
  {% block main %}
@@ -1,4 +1,4 @@
1
- {% extends "bootstrap3/base.html" %}
1
+ {% extends "bootstrap5/base.html" %}
2
2
  {% block main %}
3
3
  <div class="row-fluid">
4
4
  {{main}}