lino 25.6.1__py3-none-any.whl → 25.7.1__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.
- lino/__init__.py +1 -1
- lino/api/dd.py +1 -0
- lino/api/doctest.py +21 -0
- lino/core/actions.py +80 -25
- lino/core/actors.py +54 -27
- lino/core/boundaction.py +16 -0
- lino/core/choicelists.py +7 -7
- lino/core/constants.py +3 -0
- lino/core/dashboard.py +4 -2
- lino/core/dbtables.py +2 -2
- lino/core/elems.py +38 -13
- lino/core/fields.py +20 -11
- lino/core/kernel.py +8 -0
- lino/core/layouts.py +6 -2
- lino/core/menus.py +3 -6
- lino/core/model.py +5 -4
- lino/core/renderer.py +20 -9
- lino/core/requests.py +8 -7
- lino/core/signals.py +1 -0
- lino/core/site.py +48 -28
- lino/core/store.py +4 -2
- lino/core/tables.py +23 -10
- lino/core/utils.py +4 -1
- lino/core/workflows.py +2 -1
- lino/help_texts.py +1 -2
- lino/management/commands/prep.py +2 -2
- lino/management/commands/show.py +8 -10
- lino/mixins/__init__.py +14 -13
- lino/mixins/periods.py +2 -0
- lino/mixins/sequenced.py +1 -1
- lino/modlib/about/models.py +4 -3
- lino/modlib/checkdata/__init__.py +42 -36
- lino/modlib/checkdata/choicelists.py +9 -1
- lino/modlib/checkdata/fixtures/checkdata.py +4 -2
- lino/modlib/checkdata/management/commands/checkdata.py +3 -3
- lino/modlib/checkdata/models.py +9 -2
- lino/modlib/comments/models.py +4 -3
- lino/modlib/extjs/ext_renderer.py +4 -4
- lino/modlib/extjs/views.py +8 -2
- lino/modlib/gfks/fields.py +1 -1
- lino/modlib/help/__init__.py +3 -3
- lino/modlib/help/config/makehelp/conf.tpl.py +2 -2
- lino/modlib/help/fixtures/demo2.py +6 -1
- lino/modlib/help/management/commands/makehelp.py +4 -1
- lino/modlib/help/models.py +4 -1
- lino/modlib/help/utils.py +12 -6
- lino/modlib/linod/choicelists.py +57 -4
- lino/modlib/linod/fixtures/{linod.py → checkdata.py} +3 -13
- lino/modlib/linod/management/commands/linod.py +0 -13
- lino/modlib/linod/mixins.py +8 -0
- lino/modlib/linod/models.py +29 -30
- lino/modlib/memo/__init__.py +7 -7
- lino/modlib/memo/management/__init__,py +0 -0
- lino/modlib/memo/management/commands/__init__.py +0 -0
- lino/modlib/memo/management/commands/removeurls.py +67 -0
- lino/modlib/memo/mixins.py +1 -9
- lino/modlib/memo/parser.py +1 -1
- lino/modlib/notify/config/notify/summary.eml +5 -2
- lino/modlib/notify/fixtures/demo2.py +5 -6
- lino/modlib/notify/models.py +9 -10
- lino/modlib/periods/__init__.py +11 -8
- lino/modlib/periods/choicelists.py +16 -10
- lino/modlib/periods/models.py +45 -45
- lino/modlib/publisher/renderer.py +2 -5
- lino/modlib/summaries/fixtures/checksummaries.py +4 -2
- lino/modlib/system/models.py +17 -18
- lino/modlib/uploads/fixtures/demo.py +9 -3
- lino/modlib/uploads/mixins.py +5 -2
- lino/modlib/uploads/models.py +15 -9
- lino/modlib/uploads/utils.py +4 -1
- lino/modlib/users/__init__.py +59 -18
- lino/modlib/users/actions.py +24 -20
- lino/modlib/users/fixtures/demo_users.py +2 -35
- lino/modlib/users/mixins.py +3 -4
- lino/modlib/users/models.py +53 -13
- lino/modlib/users/ui.py +30 -16
- lino/modlib/users/utils.py +5 -6
- lino/projects/std/settings.py +1 -1
- lino/sphinxcontrib/logo/templates/footer.html +1 -0
- lino/utils/ajax.py +1 -1
- lino/utils/cycler.py +5 -0
- lino/utils/dbhash.py +4 -9
- lino/utils/dpy.py +2 -2
- lino/utils/format_date.py +4 -3
- lino/utils/html.py +13 -5
- lino/utils/jsgen.py +3 -2
- lino/utils/quantities.py +8 -0
- lino/utils/soup.py +75 -106
- {lino-25.6.1.dist-info → lino-25.7.1.dist-info}/METADATA +1 -1
- {lino-25.6.1.dist-info → lino-25.7.1.dist-info}/RECORD +93 -90
- {lino-25.6.1.dist-info → lino-25.7.1.dist-info}/WHEEL +0 -0
- {lino-25.6.1.dist-info → lino-25.7.1.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.6.1.dist-info → lino-25.7.1.dist-info}/licenses/COPYING +0 -0
lino/core/tables.py
CHANGED
@@ -403,7 +403,7 @@ class AbstractTable(actors.Actor):
|
|
403
403
|
order_by = None
|
404
404
|
"""If specified, this must be a tuple or list of field names that
|
405
405
|
will be passed to Django's `order_by
|
406
|
-
<https://docs.djangoproject.com/en/5.
|
406
|
+
<https://docs.djangoproject.com/en/5.2/ref/models/querysets/#order-by>`__
|
407
407
|
method in order to sort the rows of the queryset.
|
408
408
|
|
409
409
|
"""
|
@@ -412,7 +412,7 @@ method in order to sort the rows of the queryset.
|
|
412
412
|
"""
|
413
413
|
If specified, this must be a :class:`django.db.models.Q` object that will be
|
414
414
|
passed to Django's `filter
|
415
|
-
<https://docs.djangoproject.com/en/5.
|
415
|
+
<https://docs.djangoproject.com/en/5.2/ref/models/querysets/#filter>`__
|
416
416
|
method.
|
417
417
|
|
418
418
|
If you allow a user to insert rows into a filtered table, you should make
|
@@ -427,7 +427,7 @@ method in order to sort the rows of the queryset.
|
|
427
427
|
One advantage of :attr:`filter` over
|
428
428
|
:attr:`known_values <lino.core.actors.Actor.known_values>`
|
429
429
|
is that this can use the full range of Django's `field lookup methods
|
430
|
-
<https://docs.djangoproject.com/en/5.
|
430
|
+
<https://docs.djangoproject.com/en/5.2/topics/db/queries/#field-lookups>`_
|
431
431
|
|
432
432
|
"""
|
433
433
|
|
@@ -435,7 +435,7 @@ method in order to sort the rows of the queryset.
|
|
435
435
|
"""
|
436
436
|
If specified, this must be a :class:`django.db.models.Q` object that will be
|
437
437
|
passed to Django's `exclude
|
438
|
-
<https://docs.djangoproject.com/en/5.
|
438
|
+
<https://docs.djangoproject.com/en/5.2/ref/models/querysets/#exclude>`__
|
439
439
|
method.
|
440
440
|
|
441
441
|
This is the logical opposite of :attr:`filter`.
|
@@ -485,6 +485,8 @@ method in order to sort the rows of the queryset.
|
|
485
485
|
resolve_fields_list(cls, "mobile_columns", set, {})
|
486
486
|
resolve_fields_list(cls, "popin_columns", set, {})
|
487
487
|
if cls.model is not None:
|
488
|
+
if not isinstance(cls.model, type):
|
489
|
+
raise Exception(f"{cls}.model is {repr(cls.model)}")
|
488
490
|
if not issubclass(cls.model, models.Model):
|
489
491
|
if cls.model._lino_default_table is None:
|
490
492
|
cls.model._lino_default_table = cls
|
@@ -526,13 +528,24 @@ method in order to sort the rows of the queryset.
|
|
526
528
|
@classmethod
|
527
529
|
def get_default_action(cls):
|
528
530
|
if cls.default_record_id is not None:
|
531
|
+
# return cls._detail_action_class(
|
532
|
+
# label=cls.label, help_text=cls.help_text,
|
533
|
+
# default_record_id = cls.default_record_id)
|
529
534
|
# assert cls.display_mode == ((None, constants.DISPLAY_MODE_DETAIL))
|
530
535
|
assert cls.detail_action is not None
|
531
|
-
cls.detail_action.action.
|
532
|
-
|
533
|
-
cls.detail_action.action.
|
534
|
-
|
535
|
-
|
536
|
+
# if cls.detail_action.action.defining_actor is not cls:
|
537
|
+
# raise Exception(
|
538
|
+
# f"{cls.detail_action.action.defining_actor} is not {cls}")
|
539
|
+
a = cls.detail_action.action
|
540
|
+
return a.__class__(
|
541
|
+
a.owner,
|
542
|
+
label=cls.label, help_text=cls.help_text,
|
543
|
+
default_record_id=cls.default_record_id)
|
544
|
+
# cls.detail_action.action.label = cls.label
|
545
|
+
# cls.detail_action.help_text = cls.help_text
|
546
|
+
# cls.detail_action.action.default_record_id = cls.default_record_id
|
547
|
+
# return cls.detail_action
|
548
|
+
return actions.SHOW_TABLE
|
536
549
|
|
537
550
|
@classmethod
|
538
551
|
def get_actor_editable(self):
|
@@ -611,7 +624,7 @@ method in order to sort the rows of the queryset.
|
|
611
624
|
if not isinstance(master_instance, self.master):
|
612
625
|
# e.g. a ByUser table descendant called by AnonymousUser
|
613
626
|
msg = "20240731 %r is not a %s (%s.master_key = '%s')" % (
|
614
|
-
master_instance
|
627
|
+
master_instance,
|
615
628
|
self.master,
|
616
629
|
self,
|
617
630
|
self.master_key,
|
lino/core/utils.py
CHANGED
@@ -13,6 +13,7 @@ import datetime
|
|
13
13
|
# import yaml
|
14
14
|
from importlib import import_module
|
15
15
|
from django.utils.html import format_html, mark_safe, SafeString
|
16
|
+
from django.utils.functional import Promise
|
16
17
|
from django.db import models
|
17
18
|
from django.db.models import Q
|
18
19
|
from django.core.exceptions import FieldDoesNotExist
|
@@ -90,7 +91,7 @@ def getrqdata(request):
|
|
90
91
|
"""Return the request data.
|
91
92
|
|
92
93
|
Unlike the now defunct `REQUEST
|
93
|
-
<https://docs.djangoproject.com/en/5.
|
94
|
+
<https://docs.djangoproject.com/en/5.2/ref/request-response/#django.http.HttpRequest.REQUEST>`_
|
94
95
|
attribute, this inspects the request's `method` in order to decide
|
95
96
|
what to return.
|
96
97
|
|
@@ -1163,6 +1164,8 @@ class Panel:
|
|
1163
1164
|
|
1164
1165
|
def resolve_layout(cls, k, spec, layout_class, **options):
|
1165
1166
|
# k: just for naming the culprit in error messages
|
1167
|
+
if isinstance(spec, Promise):
|
1168
|
+
spec = str(spec)
|
1166
1169
|
if isinstance(spec, str):
|
1167
1170
|
if "\n" in spec or "." not in spec:
|
1168
1171
|
return layout_class(spec, cls, **options)
|
lino/core/workflows.py
CHANGED
@@ -255,7 +255,8 @@ class ChangeStateAction(actions.Action):
|
|
255
255
|
self.button_text = target_state.button_text
|
256
256
|
|
257
257
|
if self.icon_name:
|
258
|
-
self.help_text = format_lazy(
|
258
|
+
self.help_text = format_lazy(
|
259
|
+
"{}. {}", self.label, self.help_text)
|
259
260
|
|
260
261
|
# def get_action_permission(self, ar, obj, state):
|
261
262
|
# if not super(ChangeStateAction, self).get_action_permission(ar, obj, state):
|
lino/help_texts.py
CHANGED
@@ -110,7 +110,6 @@ help_texts = {
|
|
110
110
|
'lino.modlib.bootstrap3.views.Element' : _("""Render a single record."""),
|
111
111
|
'lino.modlib.bootstrap3.views.Index' : _("""Render the main page."""),
|
112
112
|
'lino.modlib.checkdata.Plugin' : _("""The config descriptor for this plugin."""),
|
113
|
-
'lino.modlib.checkdata.Plugin.responsible_user' : _("""The username of the main checkdata responsible, i.e. a designated user who will be attributed to checkdata messages for which no specific responible could be designated (returned by the checker’s get_responsible_user method)."""),
|
114
113
|
'lino.modlib.checkdata.Plugin.on_plugins_loaded' : _("""Set responsible_user to "'robin' if this is a demo site (is_demo_site)."""),
|
115
114
|
'lino.modlib.checkdata.roles.CheckdataUser' : _("""Can see checkdata messages."""),
|
116
115
|
'lino.modlib.comments.Plugin' : _("""See /dev/plugins."""),
|
@@ -560,7 +559,7 @@ help_texts = {
|
|
560
559
|
'lino.modlib.jinja.XMLMaker.xml_validator_file' : _("""The name of a “validator” to use for validating the XML content."""),
|
561
560
|
'lino.modlib.jinja.XMLMaker.get_xml_file' : _("""Get the name of the XML file to be generated for this database row."""),
|
562
561
|
'lino.modlib.jinja.XMLMaker.make_xml_file' : _("""Make the XML file for this database row."""),
|
563
|
-
'lino.modlib.memo.Previewable' : _("""
|
562
|
+
'lino.modlib.memo.Previewable' : _("""See dg.memo.Previewable."""),
|
564
563
|
'lino.modlib.memo.Previewable.body' : _("""An editable text body."""),
|
565
564
|
'lino.modlib.memo.Previewable.body_short_preview' : _("""A read-only preview of the first paragraph of body."""),
|
566
565
|
'lino.modlib.memo.Previewable.body_full_preview' : _("""A read-only full preview of body."""),
|
lino/management/commands/prep.py
CHANGED
@@ -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.
|
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
|
-
|
65
|
+
database_prepared.send(self)
|
lino/management/commands/show.py
CHANGED
@@ -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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
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(
|
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(
|
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(
|
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(
|
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(
|
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
|
|
lino/modlib/about/models.py
CHANGED
@@ -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 =
|
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
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
68
|
-
|
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.
|
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
|
-
|
14
|
+
if not dd.is_installed("linod"):
|
15
|
+
call_command("checkdata", fix=True)
|
14
16
|
return []
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
|
-
# Copyright 2015-
|
2
|
+
# Copyright 2015-2025 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
5
|
from django.core.management.base import BaseCommand, CommandError
|
@@ -7,7 +7,7 @@ from django.core.management.base import BaseCommand, CommandError
|
|
7
7
|
from lino.modlib.checkdata.choicelists import Checkers
|
8
8
|
from lino.modlib.checkdata.models import check_data
|
9
9
|
|
10
|
-
from lino.api import rt
|
10
|
+
from lino.api import dd, rt
|
11
11
|
|
12
12
|
|
13
13
|
class Command(BaseCommand):
|
@@ -56,7 +56,7 @@ class Command(BaseCommand):
|
|
56
56
|
app = options.get("checkers", args)
|
57
57
|
if app:
|
58
58
|
args += tuple(app)
|
59
|
-
ar = rt.login()
|
59
|
+
ar = rt.login(dd.plugins.users.demo_username)
|
60
60
|
if options["list"]:
|
61
61
|
ar.show(Checkers, column_names="value text")
|
62
62
|
else:
|
lino/modlib/checkdata/models.py
CHANGED
@@ -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=
|
420
|
+
check_data(ar, fix=dd.plugins.checkdata.fix_in_background)
|
414
421
|
# rt.login().run(settings.SITE.site_config.run_checkdata)
|
lino/modlib/comments/models.py
CHANGED
@@ -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.
|
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.
|
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
|
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=
|
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.
|
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.
|
lino/modlib/extjs/views.py
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
|
lino/modlib/gfks/fields.py
CHANGED
@@ -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.
|
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]
|