lino 25.5.2__py3-none-any.whl → 25.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +62 -582
- lino/core/actors.py +66 -32
- lino/core/atomizer.py +355 -0
- lino/core/boundaction.py +8 -4
- lino/core/constants.py +23 -1
- lino/core/dbtables.py +4 -3
- lino/core/elems.py +33 -21
- lino/core/fields.py +45 -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 +15 -1
- lino/core/requests.py +19 -8
- lino/core/signals.py +1 -1
- lino/core/site.py +1 -1
- lino/core/store.py +13 -156
- lino/core/tables.py +5 -2
- 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/extjs/ext_renderer.py +1 -1
- lino/modlib/extjs/views.py +6 -1
- lino/modlib/help/config/makehelp/plugin.tpl.rst +3 -1
- lino/modlib/help/management/commands/makehelp.py +1 -0
- lino/modlib/memo/mixins.py +1 -3
- 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 +17 -8
- lino/utils/fieldutils.py +14 -11
- lino/utils/instantiator.py +4 -2
- lino/utils/report.py +5 -3
- {lino-25.5.2.dist-info → lino-25.6.0.dist-info}/METADATA +1 -1
- {lino-25.5.2.dist-info → lino-25.6.0.dist-info}/RECORD +53 -52
- {lino-25.5.2.dist-info → lino-25.6.0.dist-info}/WHEEL +0 -0
- {lino-25.5.2.dist-info → lino-25.6.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.5.2.dist-info → lino-25.6.0.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,11 @@ 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
|
-
|
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
|
-
"""
|
249
|
+
if not owner.editable and not self.readonly:
|
250
|
+
return False
|
677
251
|
# if not actor.editable and not self.readonly:
|
678
252
|
# return False
|
679
253
|
if self.defining_actor is not None:
|
@@ -690,50 +264,25 @@ class Action(Parametrizable, Permittable):
|
|
690
264
|
# "tried to attach named action %s.%s as %s" %
|
691
265
|
# (actor, self.action_name, name))
|
692
266
|
self.action_name = name
|
693
|
-
setup_params_choosers(self)
|
694
|
-
# setup_params_choosers(self.__class__)
|
267
|
+
fields.setup_params_choosers(self)
|
268
|
+
# fields.setup_params_choosers(self.__class__)
|
695
269
|
return True
|
696
270
|
|
697
271
|
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
272
|
return True
|
715
273
|
|
716
274
|
def get_view_permission(self, user_type):
|
717
|
-
|
718
|
-
|
275
|
+
return self.get_action_view_permission(self.defining_actor, user_type)
|
276
|
+
# raise Exception("20250323 replaced by get_action_view_permission()")
|
719
277
|
|
720
|
-
|
278
|
+
def get_action_view_permission(self, actor, user_type):
|
721
279
|
return True
|
722
280
|
|
723
281
|
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
282
|
raise BadRequest("{} has no run_from_ui() method".format(
|
729
283
|
self.__class__.__name__))
|
730
284
|
|
731
285
|
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
286
|
self.run_from_ui(ar, *args, **kwargs)
|
738
287
|
|
739
288
|
def run_from_session(self, ses, *args, **kw): # 20130820
|
@@ -745,17 +294,6 @@ class Action(Parametrizable, Permittable):
|
|
745
294
|
return ia.run_from_session(ses, **kw)
|
746
295
|
|
747
296
|
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
297
|
for k, pf in self.parameters.items():
|
760
298
|
# print 20151203, pf.name, repr(pf.rel.to)
|
761
299
|
kw[k] = pf.get_default()
|
@@ -765,27 +303,15 @@ class Action(Parametrizable, Permittable):
|
|
765
303
|
pass
|
766
304
|
|
767
305
|
def get_layout_aliases(self):
|
768
|
-
|
306
|
+
return []
|
769
307
|
|
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
308
|
|
773
|
-
|
774
|
-
return []
|
309
|
+
action = Action.decorate
|
775
310
|
|
776
311
|
|
777
312
|
class TableAction(Action):
|
778
313
|
pass
|
779
314
|
|
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
315
|
|
790
316
|
class ShowTable(TableAction):
|
791
317
|
use_param_panel = True
|
@@ -846,9 +372,6 @@ class ShowDetail(Action):
|
|
846
372
|
|
847
373
|
|
848
374
|
class ShowEmptyTable(ShowDetail):
|
849
|
-
"""
|
850
|
-
The default action for :class:`lino.utils.report.EmptyTable`.
|
851
|
-
"""
|
852
375
|
|
853
376
|
use_param_panel = True
|
854
377
|
action_name = "show"
|
@@ -930,13 +453,15 @@ class ShowInsert(TableAction):
|
|
930
453
|
if wl is not None:
|
931
454
|
return wl.window_size
|
932
455
|
|
933
|
-
def
|
456
|
+
def get_action_view_permission(self, actor, user_type):
|
934
457
|
# the action is readonly because it doesn't write to the
|
935
458
|
# current object, but since it does modify the database we
|
936
459
|
# want to hide it for readonly users.
|
460
|
+
if not actor.allow_create:
|
461
|
+
return False
|
937
462
|
if user_type and user_type.readonly:
|
938
463
|
return False
|
939
|
-
return super().
|
464
|
+
return super().get_action_view_permission(actor, user_type)
|
940
465
|
|
941
466
|
def create_instance(self, ar):
|
942
467
|
"""
|
@@ -993,11 +518,6 @@ class ValidateForm(Action):
|
|
993
518
|
|
994
519
|
|
995
520
|
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
521
|
|
1002
522
|
sort_index = 10
|
1003
523
|
show_in_workflow = False
|
@@ -1019,13 +539,6 @@ class SaveGridCell(Action):
|
|
1019
539
|
|
1020
540
|
|
1021
541
|
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
542
|
|
1030
543
|
sort_index = 100
|
1031
544
|
icon_name = "disk"
|
@@ -1051,9 +564,6 @@ class SubmitDetail(SaveGridCell):
|
|
1051
564
|
|
1052
565
|
|
1053
566
|
class CreateRow(Action):
|
1054
|
-
"""
|
1055
|
-
Called when user edited a cell of a phantom record in a grid.
|
1056
|
-
"""
|
1057
567
|
|
1058
568
|
sort_index = 10
|
1059
569
|
auto_save = False
|
@@ -1188,10 +698,6 @@ class ExplicitRefresh(Action): # experimental 20170929
|
|
1188
698
|
|
1189
699
|
|
1190
700
|
class ShowSlaveTable(Action):
|
1191
|
-
"""
|
1192
|
-
An action that opens a window showing another table (to be
|
1193
|
-
specified when instantiating the action).
|
1194
|
-
"""
|
1195
701
|
|
1196
702
|
TABLE2ACTION_ATTRS = (
|
1197
703
|
"icon_name",
|
@@ -1211,9 +717,10 @@ class ShowSlaveTable(Action):
|
|
1211
717
|
self._help_text = help_text
|
1212
718
|
super().__init__(**kw)
|
1213
719
|
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
720
|
+
# Removed 20250521 because I don't see why it is needed
|
721
|
+
# @classmethod
|
722
|
+
# def get_actor_label(self):
|
723
|
+
# return self.get_label() or self.slave_table.label
|
1217
724
|
|
1218
725
|
def attach_to_actor(self, actor, name):
|
1219
726
|
if isinstance(self.slave_table, str):
|
@@ -1246,15 +753,6 @@ class ShowSlaveTable(Action):
|
|
1246
753
|
|
1247
754
|
|
1248
755
|
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
756
|
|
1259
757
|
instance = None # for Renderer.menu_item_button()
|
1260
758
|
show_in_toolbar = True
|
@@ -1308,7 +806,6 @@ class WrappedAction(Action):
|
|
1308
806
|
|
1309
807
|
|
1310
808
|
class MultipleRowAction(Action):
|
1311
|
-
"""Base class for actions that update something on every selected row."""
|
1312
809
|
|
1313
810
|
custom_handler = True
|
1314
811
|
|
@@ -1333,11 +830,6 @@ class MultipleRowAction(Action):
|
|
1333
830
|
|
1334
831
|
|
1335
832
|
class DeleteSelected(MultipleRowAction):
|
1336
|
-
"""Delete the selected row(s).
|
1337
|
-
|
1338
|
-
This action is automatically installed on every editable actor.
|
1339
|
-
|
1340
|
-
"""
|
1341
833
|
|
1342
834
|
action_name = "delete_selected" # because...
|
1343
835
|
if True: # settings.SITE.use_silk_icons:
|
@@ -1436,21 +928,9 @@ class DeleteSelected(MultipleRowAction):
|
|
1436
928
|
return 1
|
1437
929
|
|
1438
930
|
|
1439
|
-
action
|
931
|
+
# Some actions are described by a single action instance used by most actors:
|
1440
932
|
|
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
|
933
|
+
SUBMIT_DETAIL = SubmitDetail()
|
934
|
+
DELETE_ACTION = DeleteSelected()
|
935
|
+
UPDATE_ACTION = SaveGridCell()
|
936
|
+
VALIDATE_FORM = ValidateForm()
|