lino 25.5.1__py3-none-any.whl → 25.5.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/dd.py +5 -3
- lino/api/doctest.py +2 -2
- lino/core/__init__.py +3 -3
- lino/core/actions.py +60 -582
- lino/core/actors.py +66 -32
- lino/core/atomizer.py +355 -0
- lino/core/boundaction.py +8 -4
- lino/core/constants.py +3 -1
- lino/core/dbtables.py +4 -3
- lino/core/elems.py +33 -21
- lino/core/fields.py +40 -210
- lino/core/kernel.py +18 -13
- lino/core/layouts.py +30 -57
- lino/core/model.py +6 -4
- lino/core/permissions.py +18 -0
- lino/core/renderer.py +5 -1
- lino/core/requests.py +13 -7
- lino/core/signals.py +1 -1
- lino/core/site.py +7 -8
- lino/core/store.py +13 -156
- lino/core/tables.py +10 -1
- lino/core/utils.py +124 -1
- lino/locale/bn/LC_MESSAGES/django.po +1034 -868
- lino/locale/de/LC_MESSAGES/django.mo +0 -0
- lino/locale/de/LC_MESSAGES/django.po +996 -892
- lino/locale/django.pot +968 -869
- lino/locale/es/LC_MESSAGES/django.po +1032 -869
- lino/locale/et/LC_MESSAGES/django.po +1032 -866
- lino/locale/fr/LC_MESSAGES/django.po +1034 -866
- lino/locale/nl/LC_MESSAGES/django.po +1040 -868
- lino/locale/pt_BR/LC_MESSAGES/django.po +1029 -868
- lino/locale/zh_Hant/LC_MESSAGES/django.po +1029 -868
- lino/mixins/duplicable.py +8 -2
- lino/mixins/registrable.py +1 -1
- lino/modlib/changes/utils.py +4 -3
- lino/modlib/export_excel/models.py +7 -3
- lino/modlib/extjs/ext_renderer.py +1 -1
- lino/modlib/extjs/views.py +5 -0
- lino/modlib/linod/mixins.py +10 -11
- lino/modlib/memo/mixins.py +1 -3
- lino/modlib/summaries/__init__.py +2 -2
- lino/modlib/uploads/ui.py +6 -8
- lino/modlib/users/fixtures/demo_users.py +16 -13
- lino/utils/choosers.py +11 -1
- lino/utils/diag.py +6 -4
- lino/utils/fieldutils.py +14 -11
- lino/utils/instantiator.py +4 -2
- lino/utils/report.py +5 -3
- {lino-25.5.1.dist-info → lino-25.5.3.dist-info}/METADATA +1 -1
- {lino-25.5.1.dist-info → lino-25.5.3.dist-info}/RECORD +54 -53
- {lino-25.5.1.dist-info → lino-25.5.3.dist-info}/WHEEL +0 -0
- {lino-25.5.1.dist-info → lino-25.5.3.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.5.1.dist-info → lino-25.5.3.dist-info}/licenses/COPYING +0 -0
lino/core/actions.py
CHANGED
@@ -1,464 +1,76 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
|
-
# Copyright 2009-
|
2
|
+
# Copyright 2009-2025 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
|
-
|
5
|
-
decorator, and some of the standard actions. See :ref:`dev.actions`.
|
4
|
+
# See src/core/actions.rst
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
from .utils import
|
6
|
+
from django.conf import settings
|
7
|
+
from django.utils.translation import gettext_lazy as _
|
8
|
+
from django.utils.text import format_lazy
|
9
|
+
from django.utils.encoding import force_str
|
10
|
+
from django.utils.translation import gettext
|
11
|
+
from lino.core import constants
|
12
|
+
from lino.core import keyboard
|
10
13
|
from .utils import traverse_ddh_fklist
|
11
|
-
from .utils import Parametrizable
|
12
14
|
from .utils import navinfo
|
13
|
-
from .utils import resolve_model
|
14
15
|
from .utils import obj2unicode
|
15
|
-
from .
|
16
|
-
from lino.utils.choosers import check_for_chooser
|
17
|
-
from lino.modlib.users.utils import get_user_profile
|
18
|
-
from lino.core import keyboard
|
19
|
-
from lino.core import fields
|
20
|
-
from lino.core import layouts
|
21
|
-
from lino.core import constants
|
22
|
-
from lino import logger
|
16
|
+
# from .action import Action
|
23
17
|
|
24
|
-
from django.utils.translation import gettext_lazy as _
|
25
|
-
from django.utils.translation import gettext
|
26
|
-
from django.utils.text import format_lazy
|
27
|
-
from django.utils.encoding import force_str
|
28
|
-
from django.conf import settings
|
29
|
-
from django.db import models
|
30
18
|
from django.core.exceptions import BadRequest
|
31
19
|
|
32
|
-
from
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
check_for_chooser(model, field)
|
40
|
-
|
41
|
-
|
42
|
-
def resolve_layout(cls, k, spec, layout_class, **options):
|
43
|
-
# k: just for naming the culprit in error messages
|
44
|
-
if isinstance(spec, str):
|
45
|
-
if "\n" in spec or not "." in spec:
|
46
|
-
return layout_class(spec, cls, **options)
|
47
|
-
else:
|
48
|
-
layout_class = settings.SITE.models.resolve(spec)
|
49
|
-
if layout_class is None:
|
50
|
-
raise Exception(
|
51
|
-
"Unresolved {} {!r} for {}".format(k, spec, cls))
|
52
|
-
return layout_class(None, cls, **options)
|
53
|
-
elif isinstance(spec, layouts.Panel):
|
54
|
-
options.update(spec.options)
|
55
|
-
return layout_class(spec.desc, cls, **options)
|
56
|
-
else:
|
57
|
-
if not isinstance(spec, layout_class):
|
58
|
-
if not isinstance(cls, type):
|
59
|
-
# cls is an action instance
|
60
|
-
cls = cls.__class__
|
61
|
-
msg = (
|
62
|
-
"{}.{}.{} must be a string, " "a Panel or an instance of {} (not {!r})"
|
63
|
-
)
|
64
|
-
raise Exception(
|
65
|
-
msg.format(cls.__module__, cls.__name__,
|
66
|
-
k, layout_class.__name__, spec)
|
67
|
-
)
|
68
|
-
if spec._datasource is None:
|
69
|
-
spec.set_datasource(cls)
|
70
|
-
return spec
|
71
|
-
elif not issubclass(cls, spec._datasource):
|
72
|
-
raise Exception(
|
73
|
-
"Cannot reuse %s instance (%s of %r) for %r"
|
74
|
-
% (spec.__class__, k, spec._datasource, cls)
|
75
|
-
)
|
76
|
-
return spec
|
77
|
-
|
78
|
-
|
79
|
-
def install_layout(cls, k, layout_class, **options):
|
80
|
-
"""
|
81
|
-
- `cls` is the actor (a class object)
|
82
|
-
|
83
|
-
- `k` is one of 'grid_layout', 'detail_layout', 'insert_layout',
|
84
|
-
'params_layout', 'card_layout'
|
85
|
-
|
86
|
-
- `layout_class`
|
87
|
-
|
88
|
-
"""
|
89
|
-
# if str(cls) == 'courses.Pupils':
|
90
|
-
# print("20160329 install_layout", k, layout_class)
|
91
|
-
dl = cls.__dict__.get(k, None)
|
92
|
-
if dl is None: # and not cls._class_init_done:
|
93
|
-
dl = getattr(cls, k, None)
|
94
|
-
if dl is None:
|
95
|
-
return
|
96
|
-
setattr(cls, k, resolve_layout(cls, k, dl, layout_class, **options))
|
97
|
-
|
98
|
-
|
99
|
-
def register_params(cls):
|
100
|
-
"""`cls` is either an actor (a class object) or an action (an
|
101
|
-
instance).
|
102
|
-
|
103
|
-
"""
|
104
|
-
if cls.parameters is not None:
|
105
|
-
for k, v in cls.parameters.items():
|
106
|
-
v.set_attributes_from_name(k)
|
107
|
-
v.table = cls
|
108
|
-
# v.model = cls # 20181023 experimentally
|
109
|
-
|
110
|
-
if cls.params_layout is None:
|
111
|
-
cls.params_layout = cls._params_layout_class.join_str.join(
|
112
|
-
cls.parameters.keys()
|
113
|
-
)
|
114
|
-
install_layout(cls, "params_layout", cls._params_layout_class)
|
115
|
-
|
116
|
-
# e.g. accounting.ByJournal is just a mixin but provides a default value for its children
|
117
|
-
elif cls.params_layout is not None:
|
118
|
-
raise Exception("{} has a params_layout but no parameters".format(cls))
|
119
|
-
|
120
|
-
# if isinstance(cls, type) and cls.__name__.endswith("Users"):
|
121
|
-
# # if isinstance(cls, type) and cls.model is not None and cls.model.__name__ == "User":
|
122
|
-
# # if str(cls.model) != "users.User":
|
123
|
-
# # raise Exception("{} {}".format(cls, cls.model))
|
124
|
-
# print("20200825 {}.register_params {} {}".format(
|
125
|
-
# cls, cls.parameters, cls.params_layout))
|
126
|
-
|
127
|
-
|
128
|
-
def setup_params_choosers(self):
|
129
|
-
if self.parameters:
|
130
|
-
for k, fld in self.parameters.items():
|
131
|
-
if isinstance(fld, models.ForeignKey):
|
132
|
-
msg = "Invalid target %s in parameter {} of {}".format(k, self)
|
133
|
-
fld.remote_field.model = resolve_model(
|
134
|
-
fld.remote_field.model, strict=msg
|
135
|
-
)
|
136
|
-
fields.set_default_verbose_name(fld)
|
137
|
-
|
138
|
-
check_for_chooser(self, fld)
|
139
|
-
|
140
|
-
|
141
|
-
def make_params_layout_handle(self):
|
142
|
-
# `self` is either an Action instance or an Actor class object
|
143
|
-
return self.params_layout.get_layout_handle()
|
20
|
+
from .utils import InstanceAction
|
21
|
+
from .permissions import Permittable
|
22
|
+
from lino.core import layouts
|
23
|
+
from lino.core import fields
|
24
|
+
from lino.core.utils import Parametrizable
|
25
|
+
# from lino.core.fields import setup_params_choosers
|
26
|
+
from lino.core.utils import register_params, make_params_layout_handle
|
144
27
|
|
145
28
|
|
146
29
|
class Action(Parametrizable, Permittable):
|
147
|
-
"""
|
148
|
-
Abstract base class for all actions.
|
149
|
-
|
150
|
-
The first argument is the optional `label`, other arguments should
|
151
|
-
be specified as keywords and can be any of the existing class
|
152
|
-
attributes.
|
153
|
-
|
154
|
-
"""
|
155
|
-
|
156
|
-
# ~ __metaclass__ = ActionMetaClass
|
157
30
|
_params_layout_class = layouts.ActionParamsLayout
|
158
|
-
|
159
31
|
label = None
|
160
32
|
button_text: str = None
|
161
|
-
|
162
33
|
button_color = None
|
163
|
-
"""
|
164
|
-
The color to be used on icon-less buttons for this action
|
165
|
-
(i.e. which have no :attr:`icon_name`). See also
|
166
|
-
:attr:`lino.core.site.Site.use_silk_icons`.
|
167
|
-
|
168
|
-
Not yet implemented. This is currently being ignored.
|
169
|
-
"""
|
170
|
-
|
171
34
|
debug_permissions = False
|
172
35
|
save_action_name = None
|
173
|
-
|
174
36
|
disable_primary_key = True
|
175
|
-
"""
|
176
|
-
Whether primary key fields should be disabled when using this
|
177
|
-
action. This is `True` for all actions except :class:`ShowInsert`.
|
178
|
-
"""
|
179
|
-
|
180
37
|
keep_user_values = False
|
181
|
-
"""
|
182
|
-
Whether the parameter window should keep its values between
|
183
|
-
different calls. If this is True, Lino does not fill any default
|
184
|
-
values and leaves those from a previous call.
|
185
|
-
|
186
|
-
Deprecated because it (1) is not used on any production site, (2) has a
|
187
|
-
least two side effect: the fields *never* get a default value, even not on
|
188
|
-
first execution, and you cannot explicitly specify programmatic field
|
189
|
-
values. And (3) we actually wouldn't want to specify this per action but per
|
190
|
-
field.
|
191
|
-
|
192
|
-
"""
|
193
38
|
icon_name = None
|
194
|
-
"""
|
195
|
-
The class name of an icon to be used for this action when
|
196
|
-
rendered as toolbar button. Allowed icon names are defined in
|
197
|
-
:data:`lino.core.constants.ICON_NAMES`.
|
198
|
-
|
199
|
-
"""
|
200
39
|
ui5_icon_name = None
|
201
40
|
react_icon_name = None
|
202
41
|
hidden_elements = frozenset()
|
203
|
-
|
204
42
|
combo_group = None
|
205
|
-
"""
|
206
|
-
The name of another action to which to "attach" this action.
|
207
|
-
Both actions will then be rendered as a single combobutton.
|
208
|
-
|
209
|
-
"""
|
210
43
|
parameters = None
|
211
|
-
"""
|
212
|
-
See :attr:`lino.core.utils.Parametrizable.parameters`.
|
213
|
-
"""
|
214
44
|
|
215
45
|
use_param_panel = False
|
216
|
-
"""
|
217
|
-
Used internally. This is True for window actions whose window use
|
218
|
-
the parameter panel: grid and emptytable (but not showdetail)
|
219
|
-
|
220
|
-
"""
|
221
46
|
no_params_window = False
|
222
|
-
"""
|
223
|
-
Set this to `True` if your action has :attr:`parameters` but you
|
224
|
-
do *not* want it to open a window where the user can edit these
|
225
|
-
parameters before calling the action.
|
226
|
-
|
227
|
-
Setting this attribute to `True` means that the calling code must
|
228
|
-
explicitly set all parameter values. Usage example are the
|
229
|
-
:attr:`lino_xl.lib.polls.models.AnswersByResponse.answer_buttons`
|
230
|
-
and :attr:`lino_xl.lib-tickets.Ticket.quick_assign_to`
|
231
|
-
virtual fields.
|
232
|
-
|
233
|
-
"""
|
234
47
|
sort_index = 90
|
235
|
-
"""
|
236
|
-
Determines the sort order in which the actions will be presented
|
237
|
-
to the user.
|
238
|
-
|
239
|
-
List actions are negative and come first.
|
240
|
-
|
241
|
-
Predefined `sort_index` values are:
|
242
|
-
|
243
|
-
===== =================================
|
244
|
-
value action
|
245
|
-
===== =================================
|
246
|
-
-1 :class:`as_pdf <lino_xl.lib.appypod.PrintTableAction>`
|
247
|
-
10 :class:`ShowInsert`
|
248
|
-
11 :attr:`duplicate <lino.mixins.duplicable.Duplicable.duplicate>`
|
249
|
-
20 :class:`detail <ShowDetail>`
|
250
|
-
30 :class:`delete <DeleteSelected>`
|
251
|
-
31 :class:`merge <lino.core.merge.MergeAction>`
|
252
|
-
50 :class:`Print <lino.mixins.printable.BasePrintAction>`
|
253
|
-
51 :class:`Clear Cache <lino.mixins.printable.ClearCacheAction>`
|
254
|
-
52 :attr:`lino.modlib.users.UserPlan.start_plan`
|
255
|
-
53 :attr:`lino.modlib.users.UserPlan.update_plan`
|
256
|
-
60 :class:`ShowSlaveTable`
|
257
|
-
90 default for all custom row actions
|
258
|
-
100 :class:`SubmitDetail`
|
259
|
-
200 default for all workflow actions (:class:`ChangeStateAction <lino.core.workflows.ChangeStateAction>`)
|
260
|
-
===== =================================
|
261
|
-
|
262
|
-
|
263
|
-
"""
|
264
48
|
help_text = None
|
265
49
|
|
266
50
|
auto_save = True
|
267
|
-
"""
|
268
|
-
What to do when this action is being called while the user is on a
|
269
|
-
dirty record.
|
270
|
-
|
271
|
-
- `False` means: forget any changes in current record and run the
|
272
|
-
action.
|
273
|
-
|
274
|
-
- `True` means: save any changes in current record before running
|
275
|
-
the action. `None` means: ask the user.
|
276
|
-
|
277
|
-
"""
|
278
51
|
|
279
52
|
extjs_main_panel = None
|
280
|
-
"""
|
281
|
-
Used by :mod:`lino_xl.lib.extensible` and
|
282
|
-
:mod:`lino.modlib.awesome_uploader`.
|
283
|
-
|
284
|
-
Example::
|
285
|
-
|
286
|
-
class CalendarAction(dd.Action):
|
287
|
-
extjs_main_panel = "Lino.CalendarApp().get_main_panel()"
|
288
|
-
...
|
289
|
-
|
290
|
-
|
291
|
-
"""
|
292
|
-
|
293
53
|
js_handler = None
|
294
|
-
"""
|
295
|
-
This is usually `None`. Otherwise it is the name of a Javascript
|
296
|
-
callable to be called without arguments. That callable must have
|
297
|
-
been defined in a :attr:`lino.core.plugin.Plugin.site_js_snippets`
|
298
|
-
of the plugin.
|
299
|
-
|
300
|
-
Also can be defined as a class method, that takes the actor as the only
|
301
|
-
argument and should return a JavaScript executable.
|
302
|
-
An example use case is defined in
|
303
|
-
:class:`lino.modlib.help.OpenHelpWindow` where the return string
|
304
|
-
follows the format::
|
305
|
-
|
306
|
-
return "let _ = window.open('URL')"
|
307
|
-
|
308
|
-
Callable Example::
|
309
|
-
|
310
|
-
def js_handler(self, actor):
|
311
|
-
...
|
312
|
-
return JS_EXECUTABLE
|
313
|
-
|
314
|
-
"""
|
315
|
-
|
316
54
|
action_name = None
|
317
|
-
"""
|
318
|
-
Internally used to store the name of this action within the
|
319
|
-
defining Actor's namespace.
|
320
|
-
|
321
|
-
"""
|
322
|
-
|
323
55
|
defining_actor = None
|
324
|
-
"""
|
325
|
-
The :class:`lino.core.actors.Actor` who uses this action for the
|
326
|
-
first time. This is set during :meth:`attach_to_actor`. This is
|
327
|
-
used internally e.g. by :mod:`lino.modlib.extjs` when generating
|
328
|
-
JavaScript code for certain actions.
|
329
|
-
"""
|
330
|
-
|
331
56
|
hotkey = None
|
332
|
-
"""
|
333
|
-
An instance of :class:`lino.core.keyboard.Hotkey`. Used as a keyboard
|
334
|
-
shortcut to trigger actions.
|
335
|
-
"""
|
336
|
-
|
337
57
|
default_format = "html"
|
338
|
-
"""
|
339
|
-
Used internally.
|
340
|
-
"""
|
341
|
-
|
342
58
|
editable = True
|
343
|
-
"""
|
344
|
-
|
345
|
-
Whether the parameter fields should be editable.
|
346
|
-
Setting this to False seems nonsense.
|
347
|
-
"""
|
348
|
-
|
349
59
|
readonly = True
|
350
|
-
|
351
60
|
opens_a_window = False
|
352
|
-
|
353
61
|
hide_top_toolbar = False # 20210509
|
354
|
-
"""
|
355
|
-
This is set to `True` for :class:`ShowInsert`.
|
356
|
-
|
357
|
-
As an applicationdeveloper you don't need this action attribute, but
|
358
|
-
see :attr:`lino.core.actors.Actor.hide_top_toolbar`.
|
359
|
-
|
360
|
-
"""
|
361
62
|
hide_navigator = False # 20210509
|
362
|
-
"""
|
363
|
-
Hide navigator actions on window opened by this action.
|
364
|
-
|
365
|
-
"""
|
366
|
-
|
367
63
|
never_collapse = False
|
368
|
-
"""
|
369
|
-
When `True` the action will always be visible, regardless of whether
|
370
|
-
the toolbar collapsed or not.
|
371
|
-
"""
|
372
|
-
|
373
64
|
show_in_side_toolbar = False
|
374
|
-
|
375
65
|
show_in_plain = False
|
376
|
-
"""
|
377
|
-
Whether this action should be displayed as a button in the toolbar
|
378
|
-
of a plain html view.
|
379
|
-
"""
|
380
|
-
|
381
66
|
show_in_toolbar = True
|
382
|
-
"""
|
383
|
-
|
384
|
-
Whether this action should be displayed in the toolbar.
|
385
|
-
|
386
|
-
In ExtJS this will also cause it to be in the context menu of a grid.
|
387
|
-
|
388
|
-
For example the :class:`CheckinVisitor
|
389
|
-
<lino_xl.lib.reception.CheckinVisitor>`,
|
390
|
-
:class:`ReceiveVisitor
|
391
|
-
<lino_xl.lib.reception.ReceiveVisitor>` and
|
392
|
-
:class:`CheckoutVisitor
|
393
|
-
<lino_xl.lib.reception.CheckoutVisitor>` actions have this
|
394
|
-
attribute explicitly set to `False` because otherwise they would be
|
395
|
-
visible in the toolbar.
|
396
|
-
"""
|
397
|
-
|
398
67
|
show_in_workflow = False
|
399
|
-
"""
|
400
|
-
Whether this action should be displayed in the
|
401
|
-
:attr:`workflow_buttons <lino.core.model.Model.workflow_buttons>`
|
402
|
-
column. If this is True, then Lino will automatically set
|
403
|
-
:attr:`custom_handler` to True.
|
404
|
-
"""
|
405
|
-
|
406
68
|
custom_handler = False
|
407
|
-
"""
|
408
|
-
Whether this action is implemented as Javascript function call.
|
409
|
-
This is necessary if you want your action to be callable using an
|
410
|
-
"action link" (html button).
|
411
|
-
"""
|
412
|
-
|
413
69
|
select_rows = True
|
414
|
-
"""
|
415
|
-
True if this action needs an object to act on.
|
416
|
-
|
417
|
-
Set this to `False` if this action is a list action, not a row
|
418
|
-
action.
|
419
|
-
"""
|
420
70
|
http_method = "GET"
|
421
|
-
"""
|
422
|
-
HTTP method to use when this action is called using an AJAX call.
|
423
|
-
|
424
|
-
"""
|
425
|
-
|
426
71
|
preprocessor = "null" # None
|
427
|
-
"""
|
428
|
-
Name of a Javascript function to be invoked on the web client when
|
429
|
-
this action is called.
|
430
|
-
"""
|
431
|
-
|
432
72
|
window_type = None
|
433
|
-
"""
|
434
|
-
On actions that opens_a_window this must be a unique one-letter
|
435
|
-
string expressing the window type.
|
436
|
-
|
437
|
-
See `constants.WINDOW_TYPES`.
|
438
|
-
|
439
|
-
Allowed values are:
|
440
|
-
|
441
|
-
- None : opens_a_window is False
|
442
|
-
- 't' : ShowTable
|
443
|
-
- 'd' : ShowDetail
|
444
|
-
- 'i' : ShowInsert
|
445
|
-
|
446
|
-
This can be used e.g. by a summary view to decide how to present the
|
447
|
-
summary data (usage example
|
448
|
-
:meth:`lino.modlib.uploads.AreaUploads.get_table_summary`).
|
449
|
-
|
450
|
-
"""
|
451
|
-
|
452
73
|
callable_from = "td"
|
453
|
-
"""
|
454
|
-
A string that specifies from which :attr:`window_type` this action
|
455
|
-
is callable. None means that it is only callable from code.
|
456
|
-
|
457
|
-
Default value is 'td' which means from both table and detail
|
458
|
-
(including ShowEmptyTable which is subclass of ShowDetail). But
|
459
|
-
not callable from ShowInsert.
|
460
|
-
"""
|
461
|
-
|
462
74
|
hide_virtual_fields = False
|
463
75
|
required_states = None
|
464
76
|
default_record_id = None
|
@@ -488,57 +100,40 @@ class Action(Parametrizable, Permittable):
|
|
488
100
|
|
489
101
|
if self.callable_from is not None:
|
490
102
|
for c in self.callable_from:
|
491
|
-
if not
|
103
|
+
if c not in constants.WINDOW_TYPES:
|
492
104
|
raise Exception(f"Invalid window_type spec {c} in {self}")
|
493
105
|
|
494
106
|
def __get__(self, instance, owner):
|
495
|
-
"""
|
496
|
-
When a model has an action "foo", then getting an attribute
|
497
|
-
"foo" of a model instance will return an :class:`InstanceAction`.
|
498
|
-
"""
|
499
107
|
if instance is None:
|
500
108
|
return self
|
501
109
|
return InstanceAction(self, None, instance, owner)
|
502
110
|
|
503
|
-
# def
|
504
|
-
#
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
)(),
|
524
|
-
)
|
525
|
-
return LinoForm
|
111
|
+
# def get_django_form(self):
|
112
|
+
# """returns a django form object based on the params of this action"""
|
113
|
+
# from django import forms
|
114
|
+
#
|
115
|
+
# mapping = {"PasswordField": "CharField"}
|
116
|
+
#
|
117
|
+
# class LinoForm(forms.Form):
|
118
|
+
# pass
|
119
|
+
#
|
120
|
+
# for name, field in self.parameters.items():
|
121
|
+
# setattr(
|
122
|
+
# LinoForm,
|
123
|
+
# name,
|
124
|
+
# getattr(
|
125
|
+
# forms,
|
126
|
+
# mapping.get(field.__class__.__name__,
|
127
|
+
# field.__class__.__name__),
|
128
|
+
# )(),
|
129
|
+
# )
|
130
|
+
# return LinoForm
|
526
131
|
|
527
132
|
@classmethod
|
528
133
|
def decorate(cls, *args, **kw):
|
529
|
-
"""
|
530
|
-
Return a decorator that turns an instance method on a model or a
|
531
|
-
class method on an actor into an action of this class.
|
532
|
-
|
533
|
-
The decorated method will be installed as the actions's
|
534
|
-
:meth:`run_from_ui <Action.run_from_ui>` method.
|
535
|
-
|
536
|
-
All arguments are forwarded to :meth:`Action.__init__`.
|
537
|
-
|
538
|
-
"""
|
539
134
|
|
540
135
|
def decorator(fn):
|
541
|
-
assert
|
136
|
+
assert "required" not in kw
|
542
137
|
# print 20140422, fn.__name__
|
543
138
|
kw.setdefault("custom_handler", True)
|
544
139
|
a = cls(*args, **kw)
|
@@ -556,12 +151,6 @@ class Action(Parametrizable, Permittable):
|
|
556
151
|
return actor.required_roles
|
557
152
|
|
558
153
|
def is_callable_from(self, caller):
|
559
|
-
"""
|
560
|
-
Return `True` if this action makes sense as a button from within
|
561
|
-
the specified `caller` (an action instance which must have a
|
562
|
-
:attr:`window_type`). Do not override this method on your
|
563
|
-
subclass ; rather specify :attr:`callable_from`.
|
564
|
-
"""
|
565
154
|
assert caller.window_type is not None
|
566
155
|
if self.callable_from is None:
|
567
156
|
return False
|
@@ -569,10 +158,6 @@ class Action(Parametrizable, Permittable):
|
|
569
158
|
# return isinstance(caller, self.callable_from)
|
570
159
|
|
571
160
|
def is_window_action(self):
|
572
|
-
"""Return `True` if this is a "window action" (i.e. which opens a GUI
|
573
|
-
window on the client before executing).
|
574
|
-
|
575
|
-
"""
|
576
161
|
return self.opens_a_window or (self.parameters and not self.no_params_window)
|
577
162
|
|
578
163
|
def get_status(self, ar, **kw):
|
@@ -612,10 +197,6 @@ class Action(Parametrizable, Permittable):
|
|
612
197
|
return options
|
613
198
|
|
614
199
|
def get_label(self):
|
615
|
-
"""
|
616
|
-
Return the `label` of this action, or the `action_name` if the
|
617
|
-
action has no explicit label.
|
618
|
-
"""
|
619
200
|
return self.label or self.action_name
|
620
201
|
|
621
202
|
def get_button_label(self, actor):
|
@@ -662,18 +243,9 @@ class Action(Parametrizable, Permittable):
|
|
662
243
|
assert self.action_name == name
|
663
244
|
self.action_name = name
|
664
245
|
self.defining_actor = wf
|
665
|
-
setup_params_choosers(self)
|
246
|
+
fields.setup_params_choosers(self)
|
666
247
|
|
667
248
|
def attach_to_actor(self, owner, name):
|
668
|
-
"""
|
669
|
-
Called once per actor and per action on startup before a
|
670
|
-
:class:`BoundAction` instance is created. If this returns
|
671
|
-
False, then the action won't be attached to the given actor.
|
672
|
-
|
673
|
-
The owner is the actor which "defines" the action, i.e. uses
|
674
|
-
that instance for the first time. Subclasses of the owner may
|
675
|
-
re-use the same instance without becoming the owner.
|
676
|
-
"""
|
677
249
|
# if not actor.editable and not self.readonly:
|
678
250
|
# return False
|
679
251
|
if self.defining_actor is not None:
|
@@ -690,50 +262,25 @@ class Action(Parametrizable, Permittable):
|
|
690
262
|
# "tried to attach named action %s.%s as %s" %
|
691
263
|
# (actor, self.action_name, name))
|
692
264
|
self.action_name = name
|
693
|
-
setup_params_choosers(self)
|
694
|
-
# setup_params_choosers(self.__class__)
|
265
|
+
fields.setup_params_choosers(self)
|
266
|
+
# fields.setup_params_choosers(self.__class__)
|
695
267
|
return True
|
696
268
|
|
697
269
|
def get_action_permission(self, ar, obj, state):
|
698
|
-
"""Return (True or False) whether the given :class:`ActionRequest
|
699
|
-
<lino.core.requests.BaseRequest>` `ar` should get permission
|
700
|
-
to run on the given Model instance `obj` (which is in the
|
701
|
-
given `state`).
|
702
|
-
|
703
|
-
Derived Action classes may override this to add vetos.
|
704
|
-
E.g. the MoveUp action of a Sequenced is not available on the
|
705
|
-
first row of given `ar`.
|
706
|
-
|
707
|
-
This should be used only for light-weight tests. If this
|
708
|
-
requires a database lookup, consider disabling the action in
|
709
|
-
:meth:`disabled_fields
|
710
|
-
<lino.core.model.Model.disabled_fields>` where you can disable
|
711
|
-
multiple actions and fields at once.
|
712
|
-
|
713
|
-
"""
|
714
270
|
return True
|
715
271
|
|
716
272
|
def get_view_permission(self, user_type):
|
717
|
-
|
718
|
-
|
273
|
+
return self.get_action_view_permission(self.defining_actor, user_type)
|
274
|
+
# raise Exception("20250323 replaced by get_action_view_permission()")
|
719
275
|
|
720
|
-
|
276
|
+
def get_action_view_permission(self, actor, user_type):
|
721
277
|
return True
|
722
278
|
|
723
279
|
def run_from_ui(self, ar, **kwargs):
|
724
|
-
"""
|
725
|
-
Execute the action. `ar` is a :class:`BaseRequest
|
726
|
-
<lino.core.requests.BaseRequest>` object.
|
727
|
-
"""
|
728
280
|
raise BadRequest("{} has no run_from_ui() method".format(
|
729
281
|
self.__class__.__name__))
|
730
282
|
|
731
283
|
def run_from_code(self, ar=None, *args, **kwargs):
|
732
|
-
"""
|
733
|
-
Probably to be deprecated.
|
734
|
-
Execute the action. The default calls :meth:`run_from_ui`. You
|
735
|
-
may override this to define special behaviour
|
736
|
-
"""
|
737
284
|
self.run_from_ui(ar, *args, **kwargs)
|
738
285
|
|
739
286
|
def run_from_session(self, ses, *args, **kw): # 20130820
|
@@ -745,17 +292,6 @@ class Action(Parametrizable, Permittable):
|
|
745
292
|
return ia.run_from_session(ses, **kw)
|
746
293
|
|
747
294
|
def action_param_defaults(self, ar, obj, **kw):
|
748
|
-
"""Same as :meth:`lino.core.actors.Actor.param_defaults`, except that
|
749
|
-
on an action it is a instance method.
|
750
|
-
|
751
|
-
Note that this method is not called for actions which are rendered
|
752
|
-
in a toolbar (:ticket:`1336`).
|
753
|
-
|
754
|
-
Usage examples:
|
755
|
-
:class:`lino.modlib.users.actions.SendWelcomeMail`
|
756
|
-
|
757
|
-
"""
|
758
|
-
|
759
295
|
for k, pf in self.parameters.items():
|
760
296
|
# print 20151203, pf.name, repr(pf.rel.to)
|
761
297
|
kw[k] = pf.get_default()
|
@@ -765,27 +301,15 @@ class Action(Parametrizable, Permittable):
|
|
765
301
|
pass
|
766
302
|
|
767
303
|
def get_layout_aliases(self):
|
768
|
-
|
304
|
+
return []
|
769
305
|
|
770
|
-
Yield a series of (ALIAS, repl) tuples that cause a name ALIAS in a
|
771
|
-
layout based on this action to be replaced by its replacement `repl`.
|
772
306
|
|
773
|
-
|
774
|
-
return []
|
307
|
+
action = Action.decorate
|
775
308
|
|
776
309
|
|
777
310
|
class TableAction(Action):
|
778
311
|
pass
|
779
312
|
|
780
|
-
# def get_action_title(self, ar):
|
781
|
-
# return ar.get_title()
|
782
|
-
|
783
|
-
|
784
|
-
# class RedirectAction(Action):
|
785
|
-
|
786
|
-
# def get_target_url(self, elem):
|
787
|
-
# raise NotImplementedError
|
788
|
-
|
789
313
|
|
790
314
|
class ShowTable(TableAction):
|
791
315
|
use_param_panel = True
|
@@ -846,9 +370,6 @@ class ShowDetail(Action):
|
|
846
370
|
|
847
371
|
|
848
372
|
class ShowEmptyTable(ShowDetail):
|
849
|
-
"""
|
850
|
-
The default action for :class:`lino.utils.report.EmptyTable`.
|
851
|
-
"""
|
852
373
|
|
853
374
|
use_param_panel = True
|
854
375
|
action_name = "show"
|
@@ -930,13 +451,15 @@ class ShowInsert(TableAction):
|
|
930
451
|
if wl is not None:
|
931
452
|
return wl.window_size
|
932
453
|
|
933
|
-
def
|
454
|
+
def get_action_view_permission(self, actor, user_type):
|
934
455
|
# the action is readonly because it doesn't write to the
|
935
456
|
# current object, but since it does modify the database we
|
936
457
|
# want to hide it for readonly users.
|
458
|
+
if not actor.allow_create:
|
459
|
+
return False
|
937
460
|
if user_type and user_type.readonly:
|
938
461
|
return False
|
939
|
-
return super().
|
462
|
+
return super().get_action_view_permission(actor, user_type)
|
940
463
|
|
941
464
|
def create_instance(self, ar):
|
942
465
|
"""
|
@@ -993,11 +516,6 @@ class ValidateForm(Action):
|
|
993
516
|
|
994
517
|
|
995
518
|
class SaveGridCell(Action):
|
996
|
-
"""
|
997
|
-
Called when user edited a cell of a non-phantom record in a grid.
|
998
|
-
Installed as `update_action` on every :class:`Actor`.
|
999
|
-
|
1000
|
-
"""
|
1001
519
|
|
1002
520
|
sort_index = 10
|
1003
521
|
show_in_workflow = False
|
@@ -1019,13 +537,6 @@ class SaveGridCell(Action):
|
|
1019
537
|
|
1020
538
|
|
1021
539
|
class SubmitDetail(SaveGridCell):
|
1022
|
-
"""Save changes in the detail form.
|
1023
|
-
|
1024
|
-
This is rendered as the "Save" button of a :term:`detail window`.
|
1025
|
-
|
1026
|
-
Installed as `submit_detail` on every actor.
|
1027
|
-
|
1028
|
-
"""
|
1029
540
|
|
1030
541
|
sort_index = 100
|
1031
542
|
icon_name = "disk"
|
@@ -1051,9 +562,6 @@ class SubmitDetail(SaveGridCell):
|
|
1051
562
|
|
1052
563
|
|
1053
564
|
class CreateRow(Action):
|
1054
|
-
"""
|
1055
|
-
Called when user edited a cell of a phantom record in a grid.
|
1056
|
-
"""
|
1057
565
|
|
1058
566
|
sort_index = 10
|
1059
567
|
auto_save = False
|
@@ -1188,10 +696,6 @@ class ExplicitRefresh(Action): # experimental 20170929
|
|
1188
696
|
|
1189
697
|
|
1190
698
|
class ShowSlaveTable(Action):
|
1191
|
-
"""
|
1192
|
-
An action that opens a window showing another table (to be
|
1193
|
-
specified when instantiating the action).
|
1194
|
-
"""
|
1195
699
|
|
1196
700
|
TABLE2ACTION_ATTRS = (
|
1197
701
|
"icon_name",
|
@@ -1211,9 +715,10 @@ class ShowSlaveTable(Action):
|
|
1211
715
|
self._help_text = help_text
|
1212
716
|
super().__init__(**kw)
|
1213
717
|
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
718
|
+
# Removed 20250521 because I don't see why it is needed
|
719
|
+
# @classmethod
|
720
|
+
# def get_actor_label(self):
|
721
|
+
# return self.get_label() or self.slave_table.label
|
1217
722
|
|
1218
723
|
def attach_to_actor(self, actor, name):
|
1219
724
|
if isinstance(self.slave_table, str):
|
@@ -1246,15 +751,6 @@ class ShowSlaveTable(Action):
|
|
1246
751
|
|
1247
752
|
|
1248
753
|
class WrappedAction(Action):
|
1249
|
-
"""
|
1250
|
-
|
1251
|
-
On instantiation it takes a :class:`BoundAction
|
1252
|
-
<lino.core.boundaction.BoundAction>` as a positional argument and returns an
|
1253
|
-
action instance that behaves as a wrapper around the given
|
1254
|
-
*BoundAction.action* useful when binding to another :class:`Actor
|
1255
|
-
<lino.core.actors.Actor>`.
|
1256
|
-
|
1257
|
-
"""
|
1258
754
|
|
1259
755
|
instance = None # for Renderer.menu_item_button()
|
1260
756
|
show_in_toolbar = True
|
@@ -1308,7 +804,6 @@ class WrappedAction(Action):
|
|
1308
804
|
|
1309
805
|
|
1310
806
|
class MultipleRowAction(Action):
|
1311
|
-
"""Base class for actions that update something on every selected row."""
|
1312
807
|
|
1313
808
|
custom_handler = True
|
1314
809
|
|
@@ -1333,11 +828,6 @@ class MultipleRowAction(Action):
|
|
1333
828
|
|
1334
829
|
|
1335
830
|
class DeleteSelected(MultipleRowAction):
|
1336
|
-
"""Delete the selected row(s).
|
1337
|
-
|
1338
|
-
This action is automatically installed on every editable actor.
|
1339
|
-
|
1340
|
-
"""
|
1341
831
|
|
1342
832
|
action_name = "delete_selected" # because...
|
1343
833
|
if True: # settings.SITE.use_silk_icons:
|
@@ -1436,21 +926,9 @@ class DeleteSelected(MultipleRowAction):
|
|
1436
926
|
return 1
|
1437
927
|
|
1438
928
|
|
1439
|
-
action
|
929
|
+
# Some actions are described by a single action instance used by most actors:
|
1440
930
|
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
# e.g. pcsw.ClientDetail has a tab "Other", visible only to system
|
1446
|
-
# admins but the "Other" contains a GridElement RolesByPerson
|
1447
|
-
# which is not per se reserved for system admins. js of normal
|
1448
|
-
# users should not try to call on_master_changed() on it
|
1449
|
-
parent = e.parent
|
1450
|
-
while parent is not None:
|
1451
|
-
if isinstance(parent, Permittable) and not parent.get_view_permission(
|
1452
|
-
get_user_profile()
|
1453
|
-
):
|
1454
|
-
return False # bug 3 (bcss_summary) blog/2012/0927
|
1455
|
-
parent = parent.parent
|
1456
|
-
return True
|
931
|
+
SUBMIT_DETAIL = SubmitDetail()
|
932
|
+
DELETE_ACTION = DeleteSelected()
|
933
|
+
UPDATE_ACTION = SaveGridCell()
|
934
|
+
VALIDATE_FORM = ValidateForm()
|