lino 25.6.1__py3-none-any.whl → 25.7.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 (90) hide show
  1. lino/__init__.py +1 -1
  2. lino/api/doctest.py +21 -0
  3. lino/core/actions.py +59 -25
  4. lino/core/actors.py +38 -16
  5. lino/core/boundaction.py +16 -0
  6. lino/core/choicelists.py +7 -7
  7. lino/core/constants.py +3 -0
  8. lino/core/dashboard.py +1 -0
  9. lino/core/dbtables.py +1 -1
  10. lino/core/elems.py +38 -13
  11. lino/core/fields.py +20 -11
  12. lino/core/kernel.py +8 -0
  13. lino/core/layouts.py +6 -2
  14. lino/core/menus.py +3 -6
  15. lino/core/model.py +5 -4
  16. lino/core/renderer.py +14 -5
  17. lino/core/requests.py +8 -7
  18. lino/core/signals.py +1 -0
  19. lino/core/site.py +48 -28
  20. lino/core/store.py +4 -2
  21. lino/core/tables.py +23 -10
  22. lino/core/utils.py +4 -1
  23. lino/core/workflows.py +2 -1
  24. lino/help_texts.py +1 -2
  25. lino/management/commands/prep.py +2 -2
  26. lino/management/commands/show.py +8 -10
  27. lino/mixins/__init__.py +14 -13
  28. lino/mixins/periods.py +2 -0
  29. lino/mixins/sequenced.py +1 -1
  30. lino/modlib/about/models.py +4 -3
  31. lino/modlib/checkdata/__init__.py +42 -36
  32. lino/modlib/checkdata/choicelists.py +9 -1
  33. lino/modlib/checkdata/fixtures/checkdata.py +4 -2
  34. lino/modlib/checkdata/models.py +9 -2
  35. lino/modlib/comments/models.py +4 -3
  36. lino/modlib/extjs/ext_renderer.py +4 -4
  37. lino/modlib/extjs/views.py +8 -2
  38. lino/modlib/gfks/fields.py +1 -1
  39. lino/modlib/help/__init__.py +3 -3
  40. lino/modlib/help/config/makehelp/conf.tpl.py +2 -2
  41. lino/modlib/help/fixtures/demo2.py +6 -1
  42. lino/modlib/help/management/commands/makehelp.py +4 -1
  43. lino/modlib/help/models.py +2 -1
  44. lino/modlib/help/utils.py +12 -6
  45. lino/modlib/linod/choicelists.py +57 -4
  46. lino/modlib/linod/fixtures/{linod.py → checkdata.py} +3 -13
  47. lino/modlib/linod/management/commands/linod.py +0 -13
  48. lino/modlib/linod/mixins.py +8 -0
  49. lino/modlib/linod/models.py +29 -30
  50. lino/modlib/memo/__init__.py +7 -7
  51. lino/modlib/memo/management/__init__,py +0 -0
  52. lino/modlib/memo/management/commands/__init__.py +0 -0
  53. lino/modlib/memo/management/commands/removeurls.py +67 -0
  54. lino/modlib/memo/mixins.py +1 -9
  55. lino/modlib/memo/parser.py +1 -1
  56. lino/modlib/notify/config/notify/summary.eml +5 -2
  57. lino/modlib/notify/fixtures/demo2.py +5 -6
  58. lino/modlib/notify/models.py +9 -10
  59. lino/modlib/periods/__init__.py +11 -8
  60. lino/modlib/periods/choicelists.py +16 -10
  61. lino/modlib/periods/models.py +45 -45
  62. lino/modlib/summaries/fixtures/checksummaries.py +4 -2
  63. lino/modlib/system/models.py +17 -18
  64. lino/modlib/uploads/fixtures/demo.py +9 -3
  65. lino/modlib/uploads/mixins.py +5 -2
  66. lino/modlib/uploads/models.py +15 -9
  67. lino/modlib/uploads/utils.py +4 -1
  68. lino/modlib/users/__init__.py +59 -18
  69. lino/modlib/users/actions.py +24 -20
  70. lino/modlib/users/fixtures/demo_users.py +2 -35
  71. lino/modlib/users/mixins.py +3 -4
  72. lino/modlib/users/models.py +53 -13
  73. lino/modlib/users/ui.py +30 -16
  74. lino/modlib/users/utils.py +5 -6
  75. lino/projects/std/settings.py +1 -1
  76. lino/sphinxcontrib/logo/templates/footer.html +1 -0
  77. lino/utils/ajax.py +1 -1
  78. lino/utils/cycler.py +5 -0
  79. lino/utils/dbhash.py +4 -9
  80. lino/utils/dpy.py +2 -2
  81. lino/utils/format_date.py +4 -3
  82. lino/utils/html.py +13 -5
  83. lino/utils/jsgen.py +1 -1
  84. lino/utils/quantities.py +8 -0
  85. lino/utils/soup.py +75 -106
  86. {lino-25.6.1.dist-info → lino-25.7.0.dist-info}/METADATA +1 -1
  87. {lino-25.6.1.dist-info → lino-25.7.0.dist-info}/RECORD +90 -87
  88. {lino-25.6.1.dist-info → lino-25.7.0.dist-info}/WHEEL +0 -0
  89. {lino-25.6.1.dist-info → lino-25.7.0.dist-info}/licenses/AUTHORS.rst +0 -0
  90. {lino-25.6.1.dist-info → lino-25.7.0.dist-info}/licenses/COPYING +0 -0
@@ -5,7 +5,7 @@
5
5
  from django.core.management import call_command
6
6
  from django.core.management.base import BaseCommand, CommandError
7
7
  from django.conf import settings
8
- from lino.utils import dbhash
8
+ from lino.core.signals import database_prepared
9
9
 
10
10
 
11
11
  class Command(BaseCommand):
@@ -62,4 +62,4 @@ class Command(BaseCommand):
62
62
  kwargs["removemedia"] = True
63
63
  call_command("initdb", *args, **kwargs)
64
64
 
65
- dbhash.mark_virgin()
65
+ database_prepared.send(self)
@@ -2,25 +2,23 @@
2
2
  # Copyright 2013-2021 Rumma & Ko Ltd
3
3
  # License: GNU Affero General Public License v3 (see file COPYING for details)
4
4
 
5
- import argparse
6
5
  from lino.api import rt
7
6
  from django.core.management.base import BaseCommand, CommandError
8
7
 
8
+
9
9
  class Command(BaseCommand):
10
10
  help = "Show the content of a specified table to standard output."
11
11
 
12
12
  # args = "action_spec [options] [args ...]"
13
13
 
14
14
  def add_arguments(self, parser):
15
- (
16
- parser.add_argument(
17
- "-u",
18
- "--username",
19
- action="store",
20
- dest="username",
21
- default=None,
22
- help="The username to act as. Default is `None`.",
23
- ),
15
+ parser.add_argument(
16
+ "-u",
17
+ "--username",
18
+ action="store",
19
+ dest="username",
20
+ default=None,
21
+ help="The username to act as. Default is `None`.",
24
22
  )
25
23
  parser.add_argument(
26
24
  "-l",
lino/mixins/__init__.py CHANGED
@@ -61,7 +61,7 @@ class Contactable(model.Model):
61
61
  email.
62
62
  """
63
63
 
64
- class Meta(object):
64
+ class Meta:
65
65
  abstract = True
66
66
 
67
67
  email = models.EmailField(_("e-mail address"), blank=True)
@@ -78,7 +78,7 @@ class Phonable(model.Model):
78
78
  phone.
79
79
  """
80
80
 
81
- class Meta(object):
81
+ class Meta:
82
82
  abstract = True
83
83
 
84
84
  url = models.URLField(_("URL"), blank=True)
@@ -105,7 +105,7 @@ class Modified(model.Model):
105
105
  you explicitly call :meth:`touch`.
106
106
  """
107
107
 
108
- class Meta(object):
108
+ class Meta:
109
109
  abstract = True
110
110
 
111
111
  modified = models.DateTimeField(_("Modified"), editable=False, null=True)
@@ -132,7 +132,7 @@ class Created(model.Model):
132
132
  because their deserialization would be problematic.
133
133
  """
134
134
 
135
- class Meta(object):
135
+ class Meta:
136
136
  abstract = True
137
137
 
138
138
  created = models.DateTimeField(_("Created"), editable=False)
@@ -153,7 +153,7 @@ class CreatedModified(Created, Modified):
153
153
 
154
154
  """
155
155
 
156
- class Meta(object):
156
+ class Meta:
157
157
  abstract = True
158
158
 
159
159
 
@@ -175,7 +175,7 @@ class ProjectRelated(model.Model):
175
175
  <lino.core.fields.DummyField>`.
176
176
  """
177
177
 
178
- class Meta(object):
178
+ class Meta:
179
179
  abstract = True
180
180
 
181
181
  project = fields.ForeignKey(
@@ -186,11 +186,12 @@ class ProjectRelated(model.Model):
186
186
  )
187
187
 
188
188
  def get_related_project(self):
189
- if settings.SITE.project_model:
190
- return self.project
189
+ # if settings.SITE.project_model:
190
+ # When project_model is None, project is a dummy field which always returns None
191
+ return self.project
191
192
 
192
193
  # def on_create(self, ar):
193
- # super(ProjectRelated, self).on_create(ar)
194
+ # super().on_create(ar)
194
195
  # print(20200327, ar.actor.master_key, ar.master_instance)
195
196
  # if ar.actor.master_key and ar.actor.master_key == "project":
196
197
  # self.project = ar.master_instance
@@ -213,24 +214,24 @@ class ProjectRelated(model.Model):
213
214
  """
214
215
  if isinstance(controllable, ProjectRelated):
215
216
  controllable.project = self.project
216
- super(ProjectRelated, self).update_owned_instance(controllable)
217
+ super().update_owned_instance(controllable)
217
218
 
218
219
  def get_mailable_recipients(self):
219
220
  if isinstance(self.project, settings.SITE.models.contacts.Partner):
220
221
  if self.project.email:
221
222
  yield ("to", self.project)
222
- for r in super(ProjectRelated, self).get_mailable_recipients():
223
+ for r in super().get_mailable_recipients():
223
224
  yield r
224
225
 
225
226
  def get_postable_recipients(self):
226
227
  if isinstance(self.project, settings.SITE.models.contacts.Partner):
227
228
  yield self.project
228
- for p in super(ProjectRelated, self).get_postable_recipients():
229
+ for p in super().get_postable_recipients():
229
230
  yield p
230
231
 
231
232
  @classmethod
232
233
  def get_simple_parameters(cls):
233
- for p in super(ProjectRelated, cls).get_simple_parameters():
234
+ for p in super().get_simple_parameters():
234
235
  yield p
235
236
  # if settings.SITE.project_model:
236
237
  yield "project"
lino/mixins/periods.py CHANGED
@@ -18,6 +18,7 @@ from django.utils.translation import gettext_lazy as _
18
18
  from django.utils.translation import pgettext_lazy as pgettext
19
19
  from django.core.exceptions import ValidationError
20
20
  from django.utils.timezone import is_aware
21
+ from asgiref.sync import sync_to_async
21
22
 
22
23
  from lino.utils import last_day_of_month
23
24
  from lino.api import dd
@@ -276,6 +277,7 @@ class DateRange(DateRangeObservable):
276
277
  return False
277
278
  return True
278
279
 
280
+
279
281
  DateRange.set_widget_options("start_date", width=10)
280
282
  DateRange.set_widget_options("end_date", width=10)
281
283
 
lino/mixins/sequenced.py CHANGED
@@ -325,7 +325,7 @@ class Sequenced(Duplicable):
325
325
  message=_("Renumbered {} of {} siblings.").format(n, qs.count()))
326
326
  ar.set_response(refresh_all=True)
327
327
 
328
- @fields.displayfield("⇵")
328
+ @fields.displayfield("⇵", wildcard_data_elem=True)
329
329
  def dndreorder(self, ar):
330
330
  """A place holder column for drag and drop row reorder on :term:`React front end`
331
331
 
@@ -8,7 +8,7 @@ import datetime
8
8
  # from django.contrib.humanize.templatetags.humanize import naturaltime
9
9
  from django.utils.translation import gettext_lazy as _
10
10
  from django.utils.translation import gettext
11
- from django.utils.html import mark_safe
11
+ from django.utils.html import mark_safe, format_html
12
12
  from django.conf import settings
13
13
 
14
14
  from lino.utils.report import EmptyTable
@@ -64,7 +64,7 @@ class About(EmptyTable):
64
64
  body += "".join([tostring(e) for e in site.welcome_html()])
65
65
 
66
66
  for p in site.sorted_plugins:
67
- for i in p.get_site_info():
67
+ for i in p.get_site_info(ar):
68
68
  body += i
69
69
 
70
70
  if site.languages:
@@ -142,7 +142,8 @@ class About(EmptyTable):
142
142
  )
143
143
  )
144
144
  )
145
- body = "<div>{}</div>".format(body)
145
+ body = mark_safe(body)
146
+ body = format_html("<div>{}</div>", body)
146
147
  # return js_code(rt.html_text(body))
147
148
  # return rt.html_text(body)
148
149
  # return mark_safe(body)
@@ -14,6 +14,7 @@ See :doc:`/plugins/checkdata`.
14
14
  """
15
15
 
16
16
  from lino.api import ad, _
17
+ from lino.core.exceptions import ChangedAPI
17
18
 
18
19
 
19
20
  class Plugin(ad.Plugin):
@@ -21,42 +22,44 @@ class Plugin(ad.Plugin):
21
22
 
22
23
  verbose_name = _("Checkdata")
23
24
  needs_plugins = ["lino.modlib.users", "lino.modlib.gfks",
24
- "lino.modlib.office", "lino.modlib.linod"]
25
+ "lino.modlib.office", "lino.modlib.linod"]
26
+
27
+ fix_in_background = True
25
28
 
26
29
  # plugin settings
27
- responsible_user = None # the username (a string)
28
- """
29
-
30
- The :attr:`username <lino.modlib.users.User.username>`
31
- of the **main checkdata responsible**, i.e. a designated
32
- user who will be attributed to checkdata messages for which
33
- no *specific responible* could be designated (returned by the
34
- checker's :meth:`get_responsible_user
35
- <lino.modlib.checkdata.Checker.get_responsible_user>`
36
- method).
37
-
38
- The default value for this is `None`, except on a demo site
39
- (i.e. which has :attr:`is_demo_site
40
- <lino.core.site.Site.is_demo_site>` set to `True`) where it is
41
- ``"'robin'``.
42
-
43
- """
44
-
45
- _responsible_user = None # the cached User object
46
-
47
- def get_responsible_user(self, checker, obj):
48
- if self.responsible_user is None:
49
- return None
50
- if self._responsible_user is None:
51
- User = self.site.models.users.User
52
- try:
53
- self._responsible_user = User.objects.get(
54
- username=self.responsible_user)
55
- except User.DoesNotExist:
56
- msg = "Invalid username '{0}' in `responsible_user` "
57
- msg = msg.format(self.responsible_user)
58
- raise Exception(msg)
59
- return self._responsible_user
30
+ # responsible_user = None # the username (a string)
31
+ # """
32
+ #
33
+ # The :attr:`username <lino.modlib.users.User.username>`
34
+ # of the **main checkdata responsible**, i.e. a designated
35
+ # user who will be attributed to checkdata messages for which
36
+ # no *specific responible* could be designated (returned by the
37
+ # checker's :meth:`get_responsible_user
38
+ # <lino.modlib.checkdata.Checker.get_responsible_user>`
39
+ # method).
40
+ #
41
+ # The default value for this is `None`, except on a demo site
42
+ # (i.e. which has :attr:`is_demo_site
43
+ # <lino.core.site.Site.is_demo_site>` set to `True`) where it is
44
+ # ``"'robin'``.
45
+ #
46
+ # """
47
+
48
+ # _responsible_user = None # the cached User object
49
+ #
50
+ # def get_responsible_user(self, checker, obj):
51
+ # if self.responsible_user is None:
52
+ # return None
53
+ # if self._responsible_user is None:
54
+ # User = self.site.models.users.User
55
+ # try:
56
+ # self._responsible_user = User.objects.get(
57
+ # username=self.responsible_user)
58
+ # except User.DoesNotExist:
59
+ # msg = "Invalid username '{0}' in `responsible_user` "
60
+ # msg = msg.format(self.responsible_user)
61
+ # raise Exception(msg)
62
+ # return self._responsible_user
60
63
 
61
64
  def on_plugins_loaded(self, site):
62
65
  """Set :attr:`responsible_user` to ``"'robin'`` if this is a demo site
@@ -64,8 +67,11 @@ class Plugin(ad.Plugin):
64
67
 
65
68
  """
66
69
  super().on_plugins_loaded(site)
67
- if site.is_demo_site and self.responsible_user is None:
68
- self.configure(responsible_user="robin")
70
+ if hasattr(self, 'responsible_user'):
71
+ raise ChangedAPI(
72
+ "20250703 checkdata.responsible_user is replaced by users.demo_username")
73
+ # if site.is_demo_site and self.responsible_user is None:
74
+ # self.configure(responsible_user="robin")
69
75
 
70
76
  def post_site_startup(self, site):
71
77
  super().post_site_startup(site)
@@ -49,6 +49,9 @@ class Checker(dd.Choice):
49
49
  text = self.verbose_name
50
50
  super().__init__(value, text, None, **kwargs)
51
51
 
52
+ def __str__(self):
53
+ return self.value
54
+
52
55
  @classmethod
53
56
  def activate(cls, **kwargs):
54
57
  if cls.self is not None:
@@ -56,6 +59,11 @@ class Checker(dd.Choice):
56
59
  cls.self = cls(**kwargs)
57
60
  Checkers.add_item_instance(cls.self)
58
61
 
62
+ # @classmethod
63
+ # def fix_problems_silently(cls, ar, obj=None):
64
+ # for fixable, msg in cls.self.get_checkdata_problems(ar, obj, True):
65
+ # pass
66
+
59
67
  @classmethod
60
68
  def update_unbound_problems(cls, ar, **kwargs):
61
69
  assert cls.self.model is None
@@ -130,7 +138,7 @@ class Checker(dd.Choice):
130
138
  return []
131
139
 
132
140
  def get_responsible_user(self, obj):
133
- return dd.plugins.checkdata.get_responsible_user(self, obj)
141
+ return dd.plugins.users.get_demo_user(self, obj)
134
142
 
135
143
 
136
144
  class Checkers(dd.ChoiceList):
@@ -1,5 +1,5 @@
1
1
  # -*- coding: UTF-8 -*-
2
- # Copyright 2015 Rumma & Ko Ltd
2
+ # Copyright 2015-2025 Rumma & Ko Ltd
3
3
  # License: GNU Affero General Public License v3 (see file COPYING for details)
4
4
  """Runs the :manage:`checkdata` management command with `--fix`
5
5
  option.
@@ -7,8 +7,10 @@ option.
7
7
  """
8
8
 
9
9
  from django.core.management import call_command
10
+ from lino.api import dd
10
11
 
11
12
 
12
13
  def objects():
13
- call_command("checkdata", fix=True)
14
+ if not dd.is_installed("linod"):
15
+ call_command("checkdata", fix=True)
14
16
  return []
@@ -317,6 +317,13 @@ def check_instance(ar, obj, **kwargs):
317
317
  print(msg)
318
318
 
319
319
 
320
+ def fix_instance(ar, obj, **kwargs):
321
+ kwargs['fix'] = True
322
+ for chk in get_checkers_for(obj.__class__):
323
+ for fixable, msg in chk.check_instance(ar, obj, **kwargs):
324
+ pass
325
+
326
+
320
327
  def get_checkable_models(*args, only_auto=False):
321
328
  checkable_models = OrderedDict()
322
329
  for chk in Checkers.get_list_items():
@@ -407,8 +414,8 @@ def check_data(ar, args=[], fix=True, prune=False):
407
414
  ar.logger.info(msg, done, what, found, fixed)
408
415
 
409
416
 
410
- @background_task(every_unit="daily", every=1)
417
+ @background_task(every_unit="daily", every=1, run_after='generate_calendar_entries')
411
418
  def checkdata(ar):
412
419
  """Run all data checkers."""
413
- check_data(ar, fix=False)
420
+ check_data(ar, fix=dd.plugins.checkdata.fix_in_background)
414
421
  # rt.login().run(settings.SITE.site_config.run_checkdata)
@@ -3,7 +3,6 @@
3
3
  # License: GNU Affero General Public License v3 (see file COPYING for details)
4
4
 
5
5
  # from html import escape
6
- from .ui import *
7
6
  from lino.modlib.checkdata.choicelists import Checker
8
7
  from django.contrib.humanize.templatetags.humanize import naturaltime
9
8
  from django.db import models
@@ -14,7 +13,7 @@ from django.contrib.contenttypes.models import ContentType
14
13
  from django.conf import settings
15
14
  # from lino.utils.html import E, tostring, fromstring
16
15
 
17
- from lino.api import dd, rt, _
16
+ from lino.api import dd, rt, gettext, _
18
17
 
19
18
  from lino.core.requests import BaseRequest
20
19
  from lino.mixins import CreatedModified, BabelNamed
@@ -30,6 +29,8 @@ from .mixins import Commentable, MyEmotionField
30
29
  from .roles import CommentsReader, CommentsStaff
31
30
  # from .choicelists import PublishAllComments, PublishComment
32
31
 
32
+ from .ui import *
33
+
33
34
 
34
35
  class CommentType(BabelNamed):
35
36
  class Meta(object):
@@ -287,7 +288,7 @@ class Comment(
287
288
 
288
289
  def after_ui_save(self, ar, cw):
289
290
  super().after_ui_save(ar, cw)
290
- if self.owner_id:
291
+ if self.owner is not None:
291
292
  self.owner.on_commented(self, ar, cw)
292
293
 
293
294
  def full_clean(self):
@@ -210,7 +210,7 @@ class ExtRenderer(JsCacheRenderer):
210
210
  label = str(label or ba.get_button_label())
211
211
  uri = self.js2url(self.action_call(ar, ba, status or {}))
212
212
  return self.href_button_action(
213
- ba, uri, label, title or ba.action.help_text, **kw
213
+ ba, uri, label, title or ba.get_help_text(), **kw
214
214
  )
215
215
 
216
216
  def quick_manage_toolbar(self, ar, obj):
@@ -866,7 +866,7 @@ class ExtRenderer(JsCacheRenderer):
866
866
  # ~ text=unicode(a.label),
867
867
  )
868
868
 
869
- if a.help_text:
869
+ if (help_text := ba.get_help_text()) is not None:
870
870
  # if a.__class__.__name__ in ('ChangePassword', 'SubmitDetail'):
871
871
  # logger.info("20160829 a2btn() %r %r", a, str(a.help_text))
872
872
 
@@ -874,7 +874,7 @@ class ExtRenderer(JsCacheRenderer):
874
874
  # iconCls. On a button which has only text we must use
875
875
  # Lino.quicktip_renderer. But I didn't find out why this
876
876
  # doesn't seem to work.
877
- kw.update(tooltip=a.help_text, tooltipType="title")
877
+ kw.update(tooltip=help_text, tooltipType="title")
878
878
  # if not a.icon_name:
879
879
  # kw.update(tooltipType='title')
880
880
  # kw.update(listen ers=dict(render=js_code(
@@ -1496,7 +1496,7 @@ class ExtRenderer(JsCacheRenderer):
1496
1496
  either `data_record` or a `record_id`.
1497
1497
 
1498
1498
  Usually `data_record`, except if it is a `file upload
1499
- <https://docs.djangoproject.com/en/5.0/topics/http/file-uploads/>`_
1499
+ <https://docs.djangoproject.com/en/5.2/topics/http/file-uploads/>`_
1500
1500
  where some mysterious decoding problems (:blogref:`20120209`)
1501
1501
  force us to return a `record_id` which has the same visible
1502
1502
  result but using an additional GET.
@@ -330,6 +330,7 @@ class ApiElement(View):
330
330
  vm = test_version_mismatch(request)
331
331
  if vm and settings.SITE.kernel.editing_front_end.app_label == "react":
332
332
  return json_response(vm)
333
+
333
334
  # this is also used by the react front end
334
335
  rpt = requested_actor(app_label, actor)
335
336
  # if not rpt.get_view_permission(request.user.user_type):
@@ -341,12 +342,17 @@ class ApiElement(View):
341
342
  if action_name:
342
343
  ba = rpt.get_url_action(action_name)
343
344
  if ba is None:
344
- raise http.Http404("%s has no action %r" % (rpt, action_name))
345
+ msg = f"{rpt} has no action {action_name}"
346
+ print(msg)
347
+ raise http.Http404(msg)
345
348
  else:
346
349
  ba = rpt.detail_action
347
350
  if ba is None:
348
- raise http.Http404("%s has no detail_action" % rpt)
351
+ msg = f"{rpt} has no detail_action"
352
+ print(msg)
353
+ raise http.Http404(msg)
349
354
 
355
+ # print(f"20250608 {rpt} {action_name}")
350
356
  fmt = request.GET.get(constants.URL_PARAM_FORMAT,
351
357
  ba.action.default_format)
352
358
 
@@ -75,7 +75,7 @@ if settings.SITE.is_installed("contenttypes"):
75
75
 
76
76
  def deconstruct(self):
77
77
  # needed for Django 1.7
78
- # https://docs.djangoproject.com/en/5.0/howto/custom-model-fields/#custom-field-deconstruct-method
78
+ # https://docs.djangoproject.com/en/5.2/howto/custom-model-fields/#custom-field-deconstruct-method
79
79
 
80
80
  name, path, args, kwargs = super().deconstruct()
81
81
  args = [self.type_field]
@@ -66,10 +66,10 @@ class Plugin(ad.Plugin):
66
66
  # site.install_help_text(ba.action, ba.action.__class__)
67
67
  if a.model is not None:
68
68
  self.htl.install_help_text(
69
- ba.action, a.model, ba.action.action_name
69
+ ba, a.model, ba.action.action_name
70
70
  )
71
- self.htl.install_help_text(ba.action, a, ba.action.action_name)
72
- self.htl.install_help_text(ba.action.__class__)
71
+ self.htl.install_help_text(ba, a, ba.action.action_name)
72
+ self.htl.install_help_text(ba.__class__)
73
73
  # htl.install_help_text(
74
74
  # ba.action, ba.action.__class__,
75
75
  # attrname=ba.action.action_name)
@@ -18,9 +18,9 @@ from lino.sphinxcontrib import configure ; configure(globals())
18
18
  project = "{{settings.SITE.title}}"
19
19
  html_title = "{{settings.SITE.title}}"
20
20
 
21
- {% if settings.SITE.site_config.site_company %}
21
+ {% if settings.SITE.plugins.contacts.site_owner %}
22
22
  import datetime
23
- copyright = "{} {{settings.SITE.site_config.site_company}}".format(
23
+ copyright = "{} {{settings.SITE.plugins.contacts.site_owner}}".format(
24
24
  datetime.date.today())
25
25
  {% endif %}
26
26
 
@@ -14,10 +14,15 @@ if dd.get_plugin_setting("help", "use_contacts"):
14
14
  return help.SiteContact(site_contact_type=type, company=company, **kwargs)
15
15
 
16
16
  def objects():
17
- yield site_contact("owner", settings.SITE.site_config.site_company)
17
+ yield site_contact("owner", settings.SITE.plugins.contacts.site_owner)
18
18
  yield site_contact("serveradmin", contacts.Company.objects.get(pk=PS+6))
19
19
  yield site_contact(
20
20
  "hotline",
21
21
  contact_person=contacts.Person.objects.get(pk=PS+13),
22
22
  **dd.babelkw("remark", _("Mon and Fri from 11:30 to 12:00")),
23
23
  )
24
+
25
+ else:
26
+
27
+ def objects():
28
+ return []
@@ -410,7 +410,10 @@ class Command(GeneratingCommand):
410
410
 
411
411
  def action2par(self, a):
412
412
  if isinstance(a, BoundAction):
413
- a = a.action
413
+ # a = a.action
414
+ return shortpar(a.action.action_name, a.action.label,
415
+ a.get_help_text() or _("See {}.").format(
416
+ self.refto(a.action)))
414
417
  return shortpar(a.action_name, a.label, self.get_help_text_from_field(a))
415
418
 
416
419
  def collect_refs(self, cls, role):
@@ -20,7 +20,8 @@ class OpenHelpWindow(dd.Action):
20
20
  # this doesn't look nice. But adding spaces breaks a series of doctests, so
21
21
  # I undid that change:
22
22
  # button_text = " ? "
23
- button_text = "?"
23
+ # button_text = "?"
24
+ button_text = "🛈"
24
25
  select_rows = False
25
26
  help_text = _("Open Help Window")
26
27
  show_in_plain = True
lino/modlib/help/utils.py CHANGED
@@ -79,8 +79,10 @@ class HelpTextsLoader:
79
79
  # if m in self.unhelpful_classes:
80
80
  # continue
81
81
  k, txt = self.get_help_text_for_class(m, attrname)
82
- # if attrname == "update_missing_rates":
83
- # print("20181004 {} {} {}".format(cls, k, txt))
82
+ # if attrname == "update_guests":
83
+ # # if str(cls) == "cal.Events":
84
+ # # if k == "lino_xl.lib.cal.Events.update_guests":
85
+ # print(f"20250622 {hash(fld)} {cls} {k} {fld.help_text} {txt}")
84
86
  if txt is None:
85
87
  if debug:
86
88
  print(
@@ -102,11 +104,15 @@ class HelpTextsLoader:
102
104
  cls, attrname, txt, k, fld
103
105
  )
104
106
  )
105
- try:
106
- fld.help_text = txt
107
- except AttributeError as e:
108
- raise AttributeError("20240329 {} {}".format(fld, e))
107
+ fld.help_text = txt
108
+ # fld._found = True
109
+ # try:
110
+ # fld.help_text = txt
111
+ # except AttributeError as e:
112
+ # raise AttributeError("20240329 {} {}".format(fld, e))
109
113
  fld._lino_help_ref = k # for makehelp
114
+ # if attrname == "update_guests":
115
+ # print(f"20250622 {fld} {cls} {k} {txt}")
110
116
  return
111
117
  if debug:
112
118
  print("20170824 {}.{} : no help_text".format(cls, attrname))