lino 25.7.1__py3-none-any.whl → 25.7.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lino/__init__.py +1 -1
- lino/api/doctest.py +41 -12
- lino/core/__init__.py +0 -2
- lino/core/actions.py +15 -7
- lino/core/actors.py +2 -162
- lino/core/atomizer.py +9 -8
- lino/core/auth/utils.py +9 -1
- lino/core/callbacks.py +2 -2
- lino/core/elems.py +1 -1
- lino/core/fields.py +3 -1
- lino/core/kernel.py +14 -18
- lino/core/layouts.py +5 -7
- lino/core/model.py +12 -3
- lino/core/plugin.py +1 -1
- lino/core/renderer.py +1 -1
- lino/core/requests.py +3 -4
- lino/core/site.py +1 -1
- lino/core/store.py +3 -3
- lino/core/utils.py +20 -17
- lino/help_texts.py +10 -7
- lino/mixins/__init__.py +3 -2
- lino/mixins/{duplicable.py → clonable.py} +45 -50
- lino/mixins/dupable.py +2 -2
- lino/mixins/registrable.py +7 -5
- lino/mixins/sequenced.py +12 -14
- lino/modlib/dupable/models.py +2 -2
- lino/modlib/extjs/views.py +7 -0
- lino/modlib/linod/__init__.py +1 -1
- lino/modlib/memo/__init__.py +1 -2
- lino/modlib/notify/api.py +5 -0
- lino/modlib/office/roles.py +0 -1
- lino/modlib/printing/actions.py +2 -6
- lino/modlib/printing/choicelists.py +6 -6
- lino/modlib/printing/mixins.py +4 -4
- lino/modlib/publisher/__init__.py +21 -30
- lino/modlib/publisher/models.py +3 -1
- lino/modlib/publisher/views.py +4 -11
- lino/modlib/summaries/mixins.py +6 -4
- lino/modlib/users/actions.py +5 -0
- lino/modlib/weasyprint/__init__.py +9 -0
- lino/modlib/weasyprint/choicelists.py +14 -9
- lino/modlib/weasyprint/config/weasyprint/base.weasy.html +15 -13
- lino/sphinxcontrib/__init__.py +1 -1
- lino/sphinxcontrib/actordoc.py +1 -1
- lino/utils/diag.py +2 -2
- lino/utils/instantiator.py +21 -1
- {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/METADATA +1 -1
- {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/RECORD +51 -55
- lino/modlib/forms/__init__.py +0 -51
- lino/modlib/forms/models.py +0 -0
- lino/modlib/forms/renderer.py +0 -74
- lino/modlib/forms/views.py +0 -311
- {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/WHEEL +0 -0
- {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.7.1.dist-info → lino-25.7.3.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.7.
|
34
|
+
__version__ = '25.7.3'
|
35
35
|
|
36
36
|
# import setuptools # avoid UserWarning "Distutils was imported before Setuptools"?
|
37
37
|
|
lino/api/doctest.py
CHANGED
@@ -14,6 +14,7 @@ tested document. It includes
|
|
14
14
|
|
15
15
|
"""
|
16
16
|
|
17
|
+
from lino.mixins.clonable import Clonable
|
17
18
|
from lino.utils.fieldutils import get_fields, fields_help
|
18
19
|
from lino.core.boundaction import BoundAction
|
19
20
|
from lino.core.tables import AbstractTable
|
@@ -23,7 +24,9 @@ from lino.core.actions import ShowTable
|
|
23
24
|
from lino.core.menus import Menu
|
24
25
|
from lino.utils.html import html2text
|
25
26
|
from lino.utils import dbhash
|
26
|
-
from lino.core.utils import
|
27
|
+
from lino.core.utils import get_models
|
28
|
+
from lino.core.utils import full_model_name
|
29
|
+
from lino.core.utils import full_model_name as fmn
|
27
30
|
from lino.utils.diag import visible_for
|
28
31
|
from lino.sphinxcontrib.actordoc import menuselection_text
|
29
32
|
from lino import logger
|
@@ -83,6 +86,8 @@ HttpQuery = collections.namedtuple(
|
|
83
86
|
|
84
87
|
# settings.SITE.is_testing = True
|
85
88
|
|
89
|
+
ADMIN_FRONT_END = settings.SITE.kernel.editing_front_end
|
90
|
+
|
86
91
|
|
87
92
|
def get_json_dict(username, uri, an="detail", **kwargs):
|
88
93
|
url = "/api/{0}?fmt=json&an={1}".format(uri, an)
|
@@ -381,27 +386,28 @@ def walk_menu_items(username=None, severe=True):
|
|
381
386
|
mnu = settings.SITE.get_site_menu(user_type)
|
382
387
|
items = []
|
383
388
|
for mi in mnu.walk_items():
|
384
|
-
if mi.bound_action:
|
389
|
+
if ba := mi.bound_action:
|
385
390
|
item = menuselection_text(mi)
|
386
391
|
# item += " ({})".format(mi)
|
387
392
|
item += " : "
|
388
|
-
if isinstance(
|
393
|
+
if isinstance(ba.action, ShowTable) and not mi.params:
|
389
394
|
# url = settings.SITE.kernel.default_ui.renderer.request_handler()
|
390
|
-
# sar =
|
395
|
+
# sar = ba.request(parent=ar)
|
391
396
|
if False:
|
392
|
-
url = ar.get_permalink(
|
397
|
+
url = ar.get_permalink(ba, mi.instance)
|
393
398
|
else:
|
394
|
-
mt =
|
399
|
+
mt = ba.actor
|
395
400
|
baseurl = "api/{}/{}".format(mt.app_label, mt.__name__)
|
396
401
|
kwargs = dict(fmt="json")
|
397
|
-
url = settings.SITE.buildurl(baseurl, **kwargs)
|
402
|
+
# url = settings.SITE.buildurl(baseurl, **kwargs)
|
403
|
+
url = ADMIN_FRONT_END.build_plain_url(baseurl, **kwargs)
|
404
|
+
|
398
405
|
try:
|
399
406
|
response = test_client.get(url)
|
400
407
|
# response = test_client.get(url,
|
401
408
|
# REMOTE_USER=str(username))
|
402
409
|
result = check_json_result(
|
403
|
-
response, None, "GET
|
404
|
-
)
|
410
|
+
response, None, f"GET {url} for user {username}")
|
405
411
|
item += str(result["count"])
|
406
412
|
# Also ask in display_mode "list" to cover assert_safe() bugs.
|
407
413
|
# But not e.g. UserRoles because it's not on a model and doesn't have a list mode
|
@@ -409,10 +415,10 @@ def walk_menu_items(username=None, severe=True):
|
|
409
415
|
kwargs[
|
410
416
|
constants.URL_PARAM_DISPLAY_MODE
|
411
417
|
] = constants.DISPLAY_MODE_LIST
|
412
|
-
url =
|
418
|
+
url = ADMIN_FRONT_END.build_plain_url(baseurl, **kwargs)
|
413
419
|
response = test_client.get(url)
|
414
420
|
result = check_json_result(
|
415
|
-
response, None, "GET
|
421
|
+
response, None, f"GET {url} for user {username}"
|
416
422
|
)
|
417
423
|
# if ar is not None:
|
418
424
|
# sar = mt.request(parent=ar)
|
@@ -743,7 +749,7 @@ def show_change_watchers():
|
|
743
749
|
ws = m.change_watcher_spec
|
744
750
|
if ws:
|
745
751
|
rows.append(
|
746
|
-
[
|
752
|
+
[fmn(m), ws.master_key, " ".join(sorted(ws.ignored_fields))]
|
747
753
|
)
|
748
754
|
print(rstgen.table(headers, rows, max_width=40))
|
749
755
|
|
@@ -798,3 +804,26 @@ def checkdb(m, num):
|
|
798
804
|
if m.objects.count() != num:
|
799
805
|
raise Exception(
|
800
806
|
f"Model {m} should have {num} rows but has {m.objects.count()}")
|
807
|
+
|
808
|
+
|
809
|
+
def show_clonables():
|
810
|
+
"""
|
811
|
+
Print a list of all :class:`Clonable <lino.mixins.clonable.Clonable>`
|
812
|
+
models, together with their related slaves, i.e. the data that will be
|
813
|
+
cloned in cascade with their master.
|
814
|
+
"""
|
815
|
+
items = []
|
816
|
+
for m in get_models():
|
817
|
+
if issubclass(m, Clonable):
|
818
|
+
rels = []
|
819
|
+
if (obj := m.objects.first()) is not None:
|
820
|
+
new, related = obj.duplication_plan()
|
821
|
+
for fk, qs in related:
|
822
|
+
rels.append(f"{fmn(qs.model)}.{fk.name}")
|
823
|
+
if len(rels):
|
824
|
+
x = ", ".join(rels)
|
825
|
+
items.append(f"{fmn(m)} : {x}")
|
826
|
+
else:
|
827
|
+
items.append(fmn(m))
|
828
|
+
items = sorted(items)
|
829
|
+
print(rstgen.ul(items).strip())
|
lino/core/__init__.py
CHANGED
@@ -10,7 +10,6 @@ For some modules the documentation has already been migrated to prosa:
|
|
10
10
|
.. autosummary::
|
11
11
|
:toctree:
|
12
12
|
|
13
|
-
actors
|
14
13
|
boundaction
|
15
14
|
callbacks
|
16
15
|
choicelists
|
@@ -31,7 +30,6 @@ For some modules the documentation has already been migrated to prosa:
|
|
31
30
|
layouts
|
32
31
|
menus
|
33
32
|
merge
|
34
|
-
model
|
35
33
|
permissions
|
36
34
|
renderer
|
37
35
|
requests
|
lino/core/actions.py
CHANGED
@@ -11,7 +11,7 @@ from django.utils.encoding import force_str
|
|
11
11
|
from django.utils.translation import gettext
|
12
12
|
from lino.core import constants
|
13
13
|
from lino.core import keyboard
|
14
|
-
from lino.core.exceptions import ChangedAPI
|
14
|
+
# from lino.core.exceptions import ChangedAPI
|
15
15
|
from .utils import traverse_ddh_fklist
|
16
16
|
from .utils import navinfo
|
17
17
|
from .utils import obj2unicode
|
@@ -37,20 +37,17 @@ class Action(Parametrizable, Permittable):
|
|
37
37
|
save_action_name = None
|
38
38
|
disable_primary_key = True
|
39
39
|
keep_user_values = False
|
40
|
-
icon_name = None
|
40
|
+
icon_name: str = None
|
41
41
|
ui5_icon_name = None
|
42
42
|
react_icon_name = None
|
43
43
|
hidden_elements = frozenset()
|
44
44
|
combo_group = None
|
45
45
|
parameters: dict[str, Any] | None = None
|
46
|
-
|
47
46
|
use_param_panel = False
|
48
47
|
no_params_window = False
|
49
48
|
sort_index = 90
|
50
49
|
help_text = None
|
51
|
-
|
52
50
|
auto_save = True
|
53
|
-
|
54
51
|
extjs_main_panel = None
|
55
52
|
js_handler = None
|
56
53
|
action_name = None
|
@@ -67,6 +64,7 @@ class Action(Parametrizable, Permittable):
|
|
67
64
|
show_in_plain = False
|
68
65
|
show_in_toolbar = True
|
69
66
|
show_in_workflow = False
|
67
|
+
buddy_name: str = None
|
70
68
|
custom_handler = False
|
71
69
|
select_rows = True
|
72
70
|
http_method = "GET"
|
@@ -100,6 +98,13 @@ class Action(Parametrizable, Permittable):
|
|
100
98
|
raise Exception(
|
101
99
|
"Unkonwn icon_name '{0}'".format(self.icon_name))
|
102
100
|
|
101
|
+
params = {}
|
102
|
+
if self.parameters is not None:
|
103
|
+
params.update(self.parameters)
|
104
|
+
self.setup_parameters(params)
|
105
|
+
if len(params):
|
106
|
+
self.parameters = params
|
107
|
+
|
103
108
|
register_params(self)
|
104
109
|
|
105
110
|
if self.callable_from is not None:
|
@@ -153,6 +158,9 @@ class Action(Parametrizable, Permittable):
|
|
153
158
|
|
154
159
|
return decorator
|
155
160
|
|
161
|
+
def setup_parameters(self, params):
|
162
|
+
pass
|
163
|
+
|
156
164
|
def get_help_text(self, ba):
|
157
165
|
if ba is ba.actor.default_action:
|
158
166
|
if self.default_record_id is not None:
|
@@ -966,7 +974,7 @@ class ShowEditor(Action):
|
|
966
974
|
show_in_toolbar = False
|
967
975
|
|
968
976
|
def __init__(self, fieldname, *args, **kwargs):
|
969
|
-
self.
|
977
|
+
self.buddy_name = fieldname
|
970
978
|
super().__init__(*args, **kwargs)
|
971
979
|
|
972
980
|
def run_from_ui(self, ar, **kwargs):
|
@@ -978,7 +986,7 @@ class ShowEditor(Action):
|
|
978
986
|
ar.master_instance.__class__).pk
|
979
987
|
})
|
980
988
|
ar.set_response(goto_url=ar.renderer.front_end.build_plain_url(
|
981
|
-
"api", *ar.actor.actor_id.split("."), str(ar.selected_rows[0].pk), self.
|
989
|
+
"api", *ar.actor.actor_id.split("."), str(ar.selected_rows[0].pk), self.buddy_name, **kw))
|
982
990
|
|
983
991
|
|
984
992
|
# Some actions are described by a single action instance used by most actors:
|
lino/core/actors.py
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
"""This defines :class:`Actor` and related classes.
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
Introduction see :doc:`/dev/actors`.
|
7
|
+
Class reference see :doc:`/src/lino/core/actors`.
|
8
8
|
|
9
9
|
"""
|
10
10
|
|
@@ -215,138 +215,21 @@ class ActorMetaClass(type):
|
|
215
215
|
# class Actor(metaclass=ActorMetaClass, type('NewBase', (actions.Parametrizable, Permittable), {}))):
|
216
216
|
class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
|
217
217
|
"""
|
218
|
-
The base class for all actors (:term:`data table`). Subclassed by
|
219
|
-
:class:`AbstractTable <lino.core.tables.AbstractTable>`, :class:`Table
|
220
|
-
<lino.core.dbtables.Table>`, :class:`ChoiceList
|
221
|
-
<lino.core.choicelists.ChoiceList>` and :class:`Frame
|
222
|
-
<lino.core.frames.Frame>`.
|
223
|
-
|
224
|
-
.. attribute:: known_values
|
225
|
-
|
226
|
-
A `dict` of `fieldname` -> `value` pairs that specify "known values".
|
227
|
-
|
228
|
-
Requests will automatically be filtered to show only existing
|
229
|
-
records with those values. This is like :attr:`filter`, but new
|
230
|
-
instances created in this Table will automatically have these
|
231
|
-
values set.
|
232
|
-
|
233
|
-
.. attribute:: welcome_message_when_count
|
234
|
-
|
235
|
-
Set this to an integer (e.g. 0) to tell Lino to make a generic
|
236
|
-
welcome message "You have X items in Y" when the number of rows
|
237
|
-
in this table is *greater than* the given integer.
|
238
|
-
|
239
|
-
The following class methods are `None` in the default
|
240
|
-
implementation. Subclass can define them.
|
241
|
-
|
242
|
-
.. classmethod:: get_handle_name(self, ar)
|
243
|
-
|
244
|
-
Most actors use the same UI handle for each request. But
|
245
|
-
e.g. :class:`lino_welfare.modlib.debts.models.PrintEntriesByBudget`
|
246
|
-
and :class:`lino_xl.lib.events.EventsByType` override this to
|
247
|
-
implement dynamic columns depending on it's master_instance.
|
248
|
-
|
249
|
-
|
250
|
-
.. classmethod:: get_welcome_messages(self, ar)
|
251
|
-
|
252
|
-
If a method of this name is defined on an actor, then it must
|
253
|
-
be a class method that takes an :class:`ar
|
254
|
-
<lino.core.requests.BaseRequest>` as single argument and
|
255
|
-
returns or yields a list of :term:`welcome messages <welcome
|
256
|
-
message>` (messages to be displayed in the welcome block of
|
257
|
-
:xfile:`admin_main.html`).
|
258
|
-
|
259
|
-
Note that this handler will be called independently of whether
|
260
|
-
the user has permission to view the actor or not.
|
261
218
|
"""
|
262
219
|
|
263
220
|
_detail_action_class = None
|
264
221
|
|
265
222
|
obvious_fields = set()
|
266
|
-
"""A set of names of fields that are considered :term:`obvious field`. """
|
267
|
-
|
268
223
|
required_roles = set([SiteUser])
|
269
|
-
"""See :doc:`/dev/perms`."""
|
270
|
-
|
271
224
|
model = None
|
272
|
-
"""The model on which this table iterates.
|
273
|
-
|
274
|
-
The :term:`application developer` can specify either the model class itself
|
275
|
-
or a string of type ``'app.Model'``.
|
276
|
-
|
277
|
-
If this is not `None`, it should be a subclass of
|
278
|
-
:class:`lino.core.fields.TableRow`.
|
279
|
-
|
280
|
-
"""
|
281
|
-
|
282
|
-
# actions = None
|
283
|
-
# """An :class:`AttrDict <atelier.utils.AttrDict>` containing the
|
284
|
-
# actions available on this actor.
|
285
|
-
#
|
286
|
-
# """
|
287
|
-
|
288
225
|
only_fields = None
|
289
|
-
|
290
226
|
default_display_modes = None
|
291
|
-
"""
|
292
|
-
Which :term:`display mode` to use in a :term:`slave panel`,
|
293
|
-
depending on available width.
|
294
|
-
|
295
|
-
See :ref:`dg.table.default_display_modes`.
|
296
|
-
"""
|
297
|
-
|
298
227
|
# extra_display_modes = set()
|
299
228
|
# extra_display_modes = {constants.DISPLAY_MODE_SUMMARY}
|
300
229
|
extra_display_modes = {constants.DISPLAY_MODE_HTML}
|
301
|
-
"""
|
302
|
-
A set of additional display modes to make available when rendering this table.
|
303
|
-
|
304
|
-
See :ref:`dg.dd.table.extra_display_modes`.
|
305
|
-
"""
|
306
|
-
|
307
230
|
app_label = None
|
308
|
-
"""
|
309
|
-
Specify this if you want to "override" an existing actor.
|
310
|
-
|
311
|
-
The default value is deduced from the module where the subclass is
|
312
|
-
defined.
|
313
|
-
|
314
|
-
Note that this attribute is not inherited from base classes.
|
315
|
-
|
316
|
-
:func:`lino.core.dbtables.table_factory` also uses this.
|
317
|
-
"""
|
318
|
-
|
319
231
|
master = None
|
320
|
-
"""The class of master instances for requests on this table.
|
321
|
-
|
322
|
-
Application code usually doesn't need to specify this because it
|
323
|
-
is automatically set on actors whose :attr:`master_key` is
|
324
|
-
specified.
|
325
|
-
|
326
|
-
Setting this to something else than `None` will turn the table
|
327
|
-
into a :term:`slave table`.
|
328
|
-
|
329
|
-
If the :attr:`master` is something else than a database model
|
330
|
-
(e.g. a ChoiceList), then the actor must also define a
|
331
|
-
:meth:`get_master_instance` method.
|
332
|
-
|
333
|
-
"""
|
334
|
-
|
335
232
|
master_key = None
|
336
|
-
"""The name of a field of this table's :attr:`model` that
|
337
|
-
points to its :attr:`master`.
|
338
|
-
|
339
|
-
The field named by :attr:`master_key` should usually be a
|
340
|
-
:class:`ForeignKey` field.
|
341
|
-
|
342
|
-
Special cases: :class:`lino_xl.lib.cal.EntriesByGuest` shows the entries
|
343
|
-
having a presence pointing to this guest.
|
344
|
-
|
345
|
-
The :attr:`master_key` is automatically added to :attr:`hidden_columns`.
|
346
|
-
|
347
|
-
|
348
|
-
"""
|
349
|
-
|
350
233
|
details_of_master_template = _("%(details)s of %(master)s")
|
351
234
|
|
352
235
|
parameters = None
|
@@ -458,39 +341,12 @@ class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
458
341
|
get_handle_name = None
|
459
342
|
|
460
343
|
abstract = True
|
461
|
-
"""
|
462
|
-
Set this to `True` to prevent Lino from generating useless
|
463
|
-
JavaScript if this is just an abstract base class to be inherited
|
464
|
-
by other actors.
|
465
|
-
|
466
|
-
Note that this class attribute is not inherited to subclasses.
|
467
|
-
|
468
|
-
"""
|
469
344
|
sum_text_column = 0
|
470
345
|
|
471
346
|
preview_limit = None
|
472
347
|
|
473
348
|
handle_uploaded_files = None
|
474
|
-
"""
|
475
|
-
Handler for uploaded files.
|
476
|
-
Same remarks as for :attr:`lino.core.actors.Actor.disabled_fields`.
|
477
|
-
"""
|
478
|
-
|
479
349
|
default_record_id = None
|
480
|
-
"""
|
481
|
-
Turn this table into a :doc:`single-row table </topics/singlerow>`.
|
482
|
-
|
483
|
-
When this is not `None`, you must also implement a custom version of
|
484
|
-
:meth:`get_row_by_pk` that returns the same :term:`database row` regardless
|
485
|
-
the given :term:`primary key`.
|
486
|
-
|
487
|
-
This must currently be either `None` or ``'row'`` (or ``'myself'``).
|
488
|
-
|
489
|
-
TODO: Rename this to `single_row` and make it a simple boolean so that the
|
490
|
-
application developer can say ``single_row = True`` instead of
|
491
|
-
``default_record_id = 'row'``.
|
492
|
-
|
493
|
-
"""
|
494
350
|
|
495
351
|
def __init__(self, *args, **kw):
|
496
352
|
raise Exception("Actors should never get instantiated")
|
@@ -1682,11 +1538,6 @@ class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
1682
1538
|
|
1683
1539
|
@classmethod
|
1684
1540
|
def get_toolbar_actions(self, parent, user_type):
|
1685
|
-
"""
|
1686
|
-
Return a list of actions for which a button should exist in the
|
1687
|
-
toolbar of the specified "parent" action.
|
1688
|
-
"""
|
1689
|
-
|
1690
1541
|
for ba in self.get_button_actions(parent):
|
1691
1542
|
if ba.action.show_in_toolbar and ba.get_view_permission(user_type):
|
1692
1543
|
if ba.action.readonly or not self.hide_editing(user_type):
|
@@ -1703,14 +1554,6 @@ class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
1703
1554
|
|
1704
1555
|
@classmethod
|
1705
1556
|
def get_button_actions(self, parent):
|
1706
|
-
"""
|
1707
|
-
Return a sorted list of actions that should be available as
|
1708
|
-
buttons in the specified `parent` (a window action).
|
1709
|
-
|
1710
|
-
This is used (1) by :meth:`get_toolbar_actions` and (2) to
|
1711
|
-
reduce the list of disabled actions in `disabled_fields` to
|
1712
|
-
those which make sense. `dbtables.make_disabled_fields`
|
1713
|
-
"""
|
1714
1557
|
if not parent.opens_a_window:
|
1715
1558
|
# return []
|
1716
1559
|
raise Exception(
|
@@ -1720,9 +1563,6 @@ class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
1720
1563
|
|
1721
1564
|
@classmethod
|
1722
1565
|
def get_actions(self):
|
1723
|
-
"""
|
1724
|
-
Return a sorted list of all bound actions offered by this actor.
|
1725
|
-
"""
|
1726
1566
|
return self._actions_list
|
1727
1567
|
|
1728
1568
|
@classmethod
|
lino/core/atomizer.py
CHANGED
@@ -309,10 +309,10 @@ def fields_list(model, field_names):
|
|
309
309
|
Return a set with the names of the specified fields, checking
|
310
310
|
whether each of them exists.
|
311
311
|
|
312
|
-
Arguments: `model` is any subclass of `django.db.models.Model`. It
|
313
|
-
|
314
|
-
|
315
|
-
|
312
|
+
Arguments: `model` is any subclass of `django.db.models.Model`. It may be a
|
313
|
+
string with the full name of a model (e.g. ``"myapp.MyModel"``).
|
314
|
+
`field_names` is an iterable of field names or a single string with a
|
315
|
+
space-separated list of field names.
|
316
316
|
|
317
317
|
If one of the names refers to a dummy field, this name will be ignored
|
318
318
|
silently.
|
@@ -326,16 +326,17 @@ def fields_list(model, field_names):
|
|
326
326
|
iterable on the fields.
|
327
327
|
"""
|
328
328
|
lst = set()
|
329
|
-
|
329
|
+
if isinstance(field_names, str):
|
330
|
+
field_names = field_names.split()
|
330
331
|
|
331
|
-
for name in
|
332
|
+
for name in field_names:
|
332
333
|
if name == "*":
|
333
334
|
explicit_names = set()
|
334
335
|
for name in names_list:
|
335
336
|
if name != "*":
|
336
337
|
explicit_names.add(name)
|
337
338
|
for de in fields.wildcard_data_elems(model):
|
338
|
-
if not isinstance(de, DummyField):
|
339
|
+
if not isinstance(de, fields.DummyField):
|
339
340
|
if de.name not in explicit_names:
|
340
341
|
if fields.use_as_wildcard(de):
|
341
342
|
lst.add(de.name)
|
@@ -343,7 +344,7 @@ def fields_list(model, field_names):
|
|
343
344
|
e = model.get_data_elem(name)
|
344
345
|
if e is None:
|
345
346
|
raise fields.FieldDoesNotExist(
|
346
|
-
"No data element
|
347
|
+
f"No data element '{name}' in {model}")
|
347
348
|
if not hasattr(e, "name"):
|
348
349
|
raise fields.FieldDoesNotExist(
|
349
350
|
"%s %r in %s has no name" % (e.__class__, name, model)
|
lino/core/auth/utils.py
CHANGED
@@ -73,7 +73,8 @@ class AnonymousUser:
|
|
73
73
|
|
74
74
|
|
75
75
|
def activate_social_auth_testing(
|
76
|
-
globals_dict, google=True, github=True, wikimedia=True, facebook=True
|
76
|
+
globals_dict, google=True, github=True, wikimedia=True, facebook=True,
|
77
|
+
smart_id=False
|
77
78
|
):
|
78
79
|
"""
|
79
80
|
Used for testing a development server.
|
@@ -97,6 +98,13 @@ def activate_social_auth_testing(
|
|
97
98
|
# 'social_core.backends.google.GoogleOAuth2',
|
98
99
|
# 'social_core.backends.google.GoogleOAuth',
|
99
100
|
# 'social_core.backends.facebook.FacebookOAuth2',
|
101
|
+
if smart_id:
|
102
|
+
Site.social_auth_backends.append("lino.utils.smart_id.SmartID")
|
103
|
+
# https://oauth.ee/docs
|
104
|
+
globals_dict.update(
|
105
|
+
SOCIAL_AUTH_SMART_ID_KEY="xxx",
|
106
|
+
SOCIAL_AUTH_SMART_ID_SECRET="yyy",
|
107
|
+
)
|
100
108
|
if wikimedia:
|
101
109
|
Site.social_auth_backends.append("social_core.backends.mediawiki.MediaWiki")
|
102
110
|
globals_dict.update(
|
lino/core/callbacks.py
CHANGED
@@ -72,9 +72,9 @@ class Callback(object):
|
|
72
72
|
- func: a callable to be executed when user selects this choice
|
73
73
|
- the label of the button
|
74
74
|
"""
|
75
|
-
assert not
|
75
|
+
assert name not in self.choices_dict
|
76
76
|
allowed_names = ("yes", "no", "ok", "cancel")
|
77
|
-
if not
|
77
|
+
if name not in allowed_names:
|
78
78
|
raise Exception("Sorry, name must be one of %s" % allowed_names)
|
79
79
|
cbc = CallbackChoice(name, func, label)
|
80
80
|
self.choices.append(cbc)
|
lino/core/elems.py
CHANGED
@@ -1103,7 +1103,7 @@ class ForeignKeyElement(ComplexRemoteComboFieldElement):
|
|
1103
1103
|
if actor is None:
|
1104
1104
|
return kw
|
1105
1105
|
|
1106
|
-
if settings.SITE.kernel.
|
1106
|
+
if settings.SITE.kernel.editing_front_end.app_label == "react":
|
1107
1107
|
options = dict(
|
1108
1108
|
related_actor_id=actor.actor_id, allowBlank=kw.get(
|
1109
1109
|
"allowBlank", False)
|
lino/core/fields.py
CHANGED
@@ -370,6 +370,9 @@ class FakeField(object):
|
|
370
370
|
# if self.verbose_name is None and self.name:
|
371
371
|
# self.verbose_name = self.name.replace('_', ' ')
|
372
372
|
|
373
|
+
def get(self, *args, **kwargs):
|
374
|
+
return None
|
375
|
+
|
373
376
|
|
374
377
|
class RemoteField(FakeField):
|
375
378
|
"""
|
@@ -1489,7 +1492,6 @@ class TableRow(object):
|
|
1489
1492
|
|
1490
1493
|
def save_existing_instance(self, ar):
|
1491
1494
|
watcher = ChangeWatcher(self)
|
1492
|
-
# print("20210213 save_existing_instance", ar.ah, ar.rqdata, self.disabled_fields)
|
1493
1495
|
ar.ah.store.form2obj(ar, ar.rqdata, self, False)
|
1494
1496
|
self.full_clean()
|
1495
1497
|
pre_ui_save.send(sender=self.__class__, instance=self, ar=ar)
|
lino/core/kernel.py
CHANGED
@@ -230,25 +230,15 @@ class Kernel(object):
|
|
230
230
|
|
231
231
|
Model.django2lino(model)
|
232
232
|
|
233
|
-
if
|
234
|
-
model.
|
235
|
-
atomizer.fields_list(model, model.hidden_columns)
|
236
|
-
)
|
237
|
-
|
238
|
-
if isinstance(model.active_fields, str):
|
239
|
-
model.active_fields = frozenset(
|
240
|
-
atomizer.fields_list(model, model.active_fields)
|
241
|
-
)
|
233
|
+
if model.allow_cascaded_copy is None:
|
234
|
+
model.allow_cascaded_copy = model.allow_cascaded_delete
|
242
235
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
)
|
247
|
-
|
248
|
-
|
249
|
-
model.allow_cascaded_copy = frozenset(
|
250
|
-
atomizer.fields_list(model, model.allow_cascaded_copy)
|
251
|
-
)
|
236
|
+
# 'suppress_cascaded_copy'
|
237
|
+
for k in ('hidden_columns', 'active_fields',
|
238
|
+
'allow_cascaded_delete', 'allow_cascaded_copy'):
|
239
|
+
# resolve_fields_list(model, k, frozenset, frozenset())
|
240
|
+
if (v := getattr(model, k, None)) is not None:
|
241
|
+
setattr(model, k, atomizer.fields_list(model, v))
|
252
242
|
|
253
243
|
# Note how to inherit this from from parent model.
|
254
244
|
if model.quick_search_fields is None:
|
@@ -610,6 +600,7 @@ class Kernel(object):
|
|
610
600
|
# print("\n".join(["- {0.url_prefix} --> {0.app_name} {1}".format(p, hash(p))
|
611
601
|
# for p in self.web_front_ends]))
|
612
602
|
|
603
|
+
editing_wf = None
|
613
604
|
for p in self.web_front_ends:
|
614
605
|
if p.ui_handle_attr_name is not None:
|
615
606
|
editing_wf = p
|
@@ -686,6 +677,7 @@ class Kernel(object):
|
|
686
677
|
# trigger creation of params_layout.params_store and
|
687
678
|
# virtual fields in LightWeightContainer
|
688
679
|
for res in actors.actors_list:
|
680
|
+
field_actions = dict()
|
689
681
|
for ba in res.get_actions():
|
690
682
|
# ba._started = True
|
691
683
|
if ba.help_text is None:
|
@@ -694,6 +686,10 @@ class Kernel(object):
|
|
694
686
|
ba.action.params_layout.get_layout_handle()
|
695
687
|
if ba.action.is_window_action():
|
696
688
|
ba.get_layout_handel()
|
689
|
+
if ba.action.buddy_name:
|
690
|
+
lst = field_actions.setdefault(ba.action.buddy_name, [])
|
691
|
+
lst.append(ba)
|
692
|
+
res._field_actions = field_actions
|
697
693
|
|
698
694
|
# logger.info("20161219 kernel_startup done")
|
699
695
|
|
lino/core/layouts.py
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
|
5
5
|
"""
|
6
6
|
|
7
|
-
from lino import logger
|
8
7
|
import re
|
9
8
|
|
10
9
|
from django.conf import settings
|
@@ -14,6 +13,8 @@ from django.db.models.fields import NOT_PROVIDED
|
|
14
13
|
from django.db.models.fields.related import ForeignObject
|
15
14
|
# from django.contrib.contenttypes.fields import GenericRelation
|
16
15
|
|
16
|
+
from lino import logger
|
17
|
+
from lino.utils import jsgen
|
17
18
|
from lino.core import constants
|
18
19
|
from lino.core import atomizer
|
19
20
|
from lino.core import fields
|
@@ -222,6 +223,7 @@ class LayoutHandle:
|
|
222
223
|
if len(elems) == 1 and elemname != "main":
|
223
224
|
elems[0].setup(**kwargs)
|
224
225
|
return elems[0]
|
226
|
+
|
225
227
|
from lino.core.elems import create_layout_panel
|
226
228
|
|
227
229
|
return create_layout_panel(self, elemname, vertical, elems, **kwargs)
|
@@ -813,16 +815,12 @@ class ActionParamsLayout(ParamsLayout):
|
|
813
815
|
url_param_name = constants.URL_PARAM_FIELD_VALUES
|
814
816
|
|
815
817
|
def setup_element(self, lh, e):
|
816
|
-
from lino.utils import jsgen
|
817
|
-
|
818
818
|
e.declare_type = jsgen.DECLARE_THIS
|
819
819
|
|
820
820
|
def get_choices_url(self, ui, field, **kw):
|
821
|
-
return settings.SITE.kernel.
|
821
|
+
return settings.SITE.kernel.editing_front_end.build_plain_url(
|
822
822
|
"apchoices",
|
823
823
|
self._datasource.defining_actor.app_label,
|
824
824
|
self._datasource.defining_actor.__name__,
|
825
825
|
self._datasource.action_name,
|
826
|
-
field.name,
|
827
|
-
**kw,
|
828
|
-
)
|
826
|
+
field.name, **kw)
|