lino 25.7.0__py3-none-any.whl → 25.7.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lino/__init__.py +1 -1
- lino/api/dd.py +1 -0
- lino/api/doctest.py +13 -10
- lino/core/__init__.py +0 -2
- lino/core/actions.py +24 -5
- lino/core/actors.py +22 -177
- lino/core/dashboard.py +3 -2
- lino/core/dbtables.py +1 -1
- lino/core/elems.py +1 -1
- lino/core/fields.py +3 -0
- lino/core/kernel.py +6 -0
- lino/core/layouts.py +5 -7
- lino/core/model.py +1 -0
- lino/core/plugin.py +1 -1
- lino/core/renderer.py +7 -5
- lino/core/requests.py +4 -5
- lino/core/site.py +1 -1
- lino/core/utils.py +6 -4
- lino/help_texts.py +2 -1
- lino/mixins/registrable.py +4 -2
- lino/modlib/checkdata/management/commands/checkdata.py +3 -3
- lino/modlib/extjs/views.py +7 -0
- lino/modlib/help/models.py +3 -1
- lino/modlib/linod/__init__.py +1 -1
- lino/modlib/memo/__init__.py +1 -2
- 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 +2 -2
- lino/modlib/publisher/__init__.py +21 -30
- lino/modlib/publisher/models.py +3 -1
- lino/modlib/publisher/renderer.py +2 -5
- lino/modlib/publisher/views.py +4 -11
- 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/utils/jsgen.py +2 -1
- {lino-25.7.0.dist-info → lino-25.7.2.dist-info}/METADATA +1 -1
- {lino-25.7.0.dist-info → lino-25.7.2.dist-info}/RECORD +42 -46
- 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.0.dist-info → lino-25.7.2.dist-info}/WHEEL +0 -0
- {lino-25.7.0.dist-info → lino-25.7.2.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.7.0.dist-info → lino-25.7.2.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.2'
|
35
35
|
|
36
36
|
# import setuptools # avoid UserWarning "Distutils was imported before Setuptools"?
|
37
37
|
|
lino/api/dd.py
CHANGED
@@ -62,6 +62,7 @@ from lino.core.actions import ShowSlaveTable
|
|
62
62
|
from lino.core.actions import ShowTable, ShowDetail
|
63
63
|
from lino.core.actions import ShowInsert, DeleteSelected
|
64
64
|
from lino.core.actions import SubmitDetail, SubmitInsert
|
65
|
+
from lino.core.actions import ShowEditor
|
65
66
|
|
66
67
|
from lino.core.choicelists import ChoiceList, Choice
|
67
68
|
from lino.core.workflows import State, Workflow, ChangeStateAction
|
lino/api/doctest.py
CHANGED
@@ -83,6 +83,8 @@ HttpQuery = collections.namedtuple(
|
|
83
83
|
|
84
84
|
# settings.SITE.is_testing = True
|
85
85
|
|
86
|
+
ADMIN_FRONT_END = settings.SITE.kernel.editing_front_end
|
87
|
+
|
86
88
|
|
87
89
|
def get_json_dict(username, uri, an="detail", **kwargs):
|
88
90
|
url = "/api/{0}?fmt=json&an={1}".format(uri, an)
|
@@ -381,27 +383,28 @@ def walk_menu_items(username=None, severe=True):
|
|
381
383
|
mnu = settings.SITE.get_site_menu(user_type)
|
382
384
|
items = []
|
383
385
|
for mi in mnu.walk_items():
|
384
|
-
if mi.bound_action:
|
386
|
+
if ba := mi.bound_action:
|
385
387
|
item = menuselection_text(mi)
|
386
388
|
# item += " ({})".format(mi)
|
387
389
|
item += " : "
|
388
|
-
if isinstance(
|
390
|
+
if isinstance(ba.action, ShowTable) and not mi.params:
|
389
391
|
# url = settings.SITE.kernel.default_ui.renderer.request_handler()
|
390
|
-
# sar =
|
392
|
+
# sar = ba.request(parent=ar)
|
391
393
|
if False:
|
392
|
-
url = ar.get_permalink(
|
394
|
+
url = ar.get_permalink(ba, mi.instance)
|
393
395
|
else:
|
394
|
-
mt =
|
396
|
+
mt = ba.actor
|
395
397
|
baseurl = "api/{}/{}".format(mt.app_label, mt.__name__)
|
396
398
|
kwargs = dict(fmt="json")
|
397
|
-
url = settings.SITE.buildurl(baseurl, **kwargs)
|
399
|
+
# url = settings.SITE.buildurl(baseurl, **kwargs)
|
400
|
+
url = ADMIN_FRONT_END.build_plain_url(baseurl, **kwargs)
|
401
|
+
|
398
402
|
try:
|
399
403
|
response = test_client.get(url)
|
400
404
|
# response = test_client.get(url,
|
401
405
|
# REMOTE_USER=str(username))
|
402
406
|
result = check_json_result(
|
403
|
-
response, None, "GET
|
404
|
-
)
|
407
|
+
response, None, f"GET {url} for user {username}")
|
405
408
|
item += str(result["count"])
|
406
409
|
# Also ask in display_mode "list" to cover assert_safe() bugs.
|
407
410
|
# But not e.g. UserRoles because it's not on a model and doesn't have a list mode
|
@@ -409,10 +412,10 @@ def walk_menu_items(username=None, severe=True):
|
|
409
412
|
kwargs[
|
410
413
|
constants.URL_PARAM_DISPLAY_MODE
|
411
414
|
] = constants.DISPLAY_MODE_LIST
|
412
|
-
url =
|
415
|
+
url = ADMIN_FRONT_END.build_plain_url(baseurl, **kwargs)
|
413
416
|
response = test_client.get(url)
|
414
417
|
result = check_json_result(
|
415
|
-
response, None, "GET
|
418
|
+
response, None, f"GET {url} for user {username}"
|
416
419
|
)
|
417
420
|
# if ar is not None:
|
418
421
|
# sar = mt.request(parent=ar)
|
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"
|
@@ -960,6 +958,27 @@ class DeleteSelected(MultipleRowAction):
|
|
960
958
|
return 1
|
961
959
|
|
962
960
|
|
961
|
+
class ShowEditor(Action):
|
962
|
+
select_rows = True
|
963
|
+
button_text = "⏏"
|
964
|
+
show_in_toolbar = False
|
965
|
+
|
966
|
+
def __init__(self, fieldname, *args, **kwargs):
|
967
|
+
self.buddy_name = fieldname
|
968
|
+
super().__init__(*args, **kwargs)
|
969
|
+
|
970
|
+
def run_from_ui(self, ar, **kwargs):
|
971
|
+
kw = dict()
|
972
|
+
if ar.master_instance:
|
973
|
+
kw.update({
|
974
|
+
constants.URL_PARAM_MASTER_PK: ar.master_instance.pk,
|
975
|
+
constants.URL_PARAM_MASTER_TYPE: settings.SITE.models.gfks.ContentType.objects.get_for_model(
|
976
|
+
ar.master_instance.__class__).pk
|
977
|
+
})
|
978
|
+
ar.set_response(goto_url=ar.renderer.front_end.build_plain_url(
|
979
|
+
"api", *ar.actor.actor_id.split("."), str(ar.selected_rows[0].pk), self.buddy_name, **kw))
|
980
|
+
|
981
|
+
|
963
982
|
# Some actions are described by a single action instance used by most actors:
|
964
983
|
|
965
984
|
SHOW_INSERT = ShowInsert()
|
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
|
@@ -1960,8 +1800,9 @@ class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
1960
1800
|
is_on_main_actor=False)
|
1961
1801
|
grp = Grouper(sar)
|
1962
1802
|
html_text = grp.begin()
|
1803
|
+
limit = ar.limit or cls.preview_limit
|
1963
1804
|
for i, obj in enumerate(sar.data_iterator):
|
1964
|
-
if i ==
|
1805
|
+
if i == limit:
|
1965
1806
|
break
|
1966
1807
|
par = sar.row_as_paragraph(obj) # 20230207
|
1967
1808
|
# assert_safe(par) # temporary 20240506
|
@@ -1980,10 +1821,10 @@ class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
1980
1821
|
html_text += grp.after_row(obj)
|
1981
1822
|
html_text += grp.stop()
|
1982
1823
|
|
1983
|
-
# 20250713
|
1984
|
-
if len(toolbar := sar.plain_toolbar_buttons()):
|
1985
|
-
|
1986
|
-
|
1824
|
+
# 20250713
|
1825
|
+
# if len(toolbar := sar.plain_toolbar_buttons()):
|
1826
|
+
# p = mark_safe(btn_sep.join([tostring(b) for b in toolbar]))
|
1827
|
+
# html_text = p + html_text
|
1987
1828
|
|
1988
1829
|
# if cls.editable and cls.insert_action is not None:
|
1989
1830
|
# ir = cls.insert_action.request_from(sar)
|
@@ -2000,8 +1841,9 @@ class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
2000
1841
|
is_on_main_actor=False)
|
2001
1842
|
tiles = SAFE_EMPTY
|
2002
1843
|
prev = None
|
1844
|
+
limit = ar.limit or cls.preview_limit
|
2003
1845
|
for i, obj in enumerate(sar.data_iterator):
|
2004
|
-
if i ==
|
1846
|
+
if i == limit:
|
2005
1847
|
break
|
2006
1848
|
tiles += obj.as_tile(sar, prev)
|
2007
1849
|
prev = obj
|
@@ -2013,8 +1855,9 @@ class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
2013
1855
|
sar = cls.create_request(parent=ar, master_instance=obj,
|
2014
1856
|
is_on_main_actor=False)
|
2015
1857
|
html = SAFE_EMPTY
|
1858
|
+
limit = ar.limit or cls.preview_limit
|
2016
1859
|
for i, obj in enumerate(sar.data_iterator):
|
2017
|
-
if i ==
|
1860
|
+
if i == limit:
|
2018
1861
|
break
|
2019
1862
|
s = obj.as_story_item(sar)
|
2020
1863
|
# assert_safe(s) # temporary 20240506
|
@@ -2080,14 +1923,16 @@ class Actor(Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
2080
1923
|
# assert isinstance(p, str)
|
2081
1924
|
# assert_safe(p) # temporary 20240506
|
2082
1925
|
# assert not "<" in p
|
2083
|
-
toolbar
|
2084
|
-
|
2085
|
-
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
2090
|
-
|
1926
|
+
# No toolbar needed after 20250714 #6202 ("Tickets to work" has its
|
1927
|
+
# insert button (+) duplicated in the dashboard):
|
1928
|
+
# toolbar = ar.plain_toolbar_buttons()
|
1929
|
+
# if len(toolbar):
|
1930
|
+
# # p += "<br/>"
|
1931
|
+
# # p += " | "
|
1932
|
+
# if p:
|
1933
|
+
# p += cls.summary_sep
|
1934
|
+
# for b in toolbar:
|
1935
|
+
# p += tostring(b) + btn_sep
|
2091
1936
|
return p
|
2092
1937
|
|
2093
1938
|
@classmethod
|
lino/core/dashboard.py
CHANGED
@@ -67,7 +67,7 @@ class DashboardItem(Permittable):
|
|
67
67
|
if self.header_level is not None:
|
68
68
|
buttons = sar.plain_toolbar_buttons()
|
69
69
|
# 20250713 Maybe add the ⏏ button already in plain_toolbar_buttons()
|
70
|
-
buttons.append(sar.open_in_own_window_button())
|
70
|
+
# buttons.append(sar.open_in_own_window_button())
|
71
71
|
elems = []
|
72
72
|
for b in buttons:
|
73
73
|
elems.append(b)
|
@@ -93,7 +93,7 @@ class DashboardItem(Permittable):
|
|
93
93
|
# yield tostring(e)
|
94
94
|
else:
|
95
95
|
raise Exception("20240908 Cannot render {}".format(sar))
|
96
|
-
yield "Cannot render {}".format(sar)
|
96
|
+
# yield "Cannot render {}".format(sar)
|
97
97
|
yield mark_safe("</div>")
|
98
98
|
|
99
99
|
def serialize(self):
|
@@ -156,6 +156,7 @@ class ActorItem(DashboardItem):
|
|
156
156
|
# sar = ar.spawn_request(actor=T, limit=T.preview_limit)
|
157
157
|
# raise Exception("20230331 {}".format(ar.subst_user))
|
158
158
|
|
159
|
+
# print("20250714 render()", sar.limit)
|
159
160
|
# print("20210112 render()", ar, sar, ar.get_user(), sar.get_user())
|
160
161
|
|
161
162
|
for i in self.render_request(ar, sar, **kwargs):
|
lino/core/dbtables.py
CHANGED
@@ -72,7 +72,7 @@ def add_gridfilters(qs, gridfilters):
|
|
72
72
|
flttype = flt["type"]
|
73
73
|
kw = {}
|
74
74
|
if flttype == "string":
|
75
|
-
if isinstance(field, models.CharField):
|
75
|
+
if isinstance(field, (models.CharField, models.FileField)):
|
76
76
|
kw[field.name + "__icontains"] = flt["value"]
|
77
77
|
q = q & models.Q(**kw)
|
78
78
|
elif isinstance(field, models.ForeignKey):
|
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
lino/core/kernel.py
CHANGED
@@ -610,6 +610,7 @@ class Kernel(object):
|
|
610
610
|
# print("\n".join(["- {0.url_prefix} --> {0.app_name} {1}".format(p, hash(p))
|
611
611
|
# for p in self.web_front_ends]))
|
612
612
|
|
613
|
+
editing_wf = None
|
613
614
|
for p in self.web_front_ends:
|
614
615
|
if p.ui_handle_attr_name is not None:
|
615
616
|
editing_wf = p
|
@@ -686,6 +687,7 @@ class Kernel(object):
|
|
686
687
|
# trigger creation of params_layout.params_store and
|
687
688
|
# virtual fields in LightWeightContainer
|
688
689
|
for res in actors.actors_list:
|
690
|
+
field_actions = dict()
|
689
691
|
for ba in res.get_actions():
|
690
692
|
# ba._started = True
|
691
693
|
if ba.help_text is None:
|
@@ -694,6 +696,10 @@ class Kernel(object):
|
|
694
696
|
ba.action.params_layout.get_layout_handle()
|
695
697
|
if ba.action.is_window_action():
|
696
698
|
ba.get_layout_handel()
|
699
|
+
if ba.action.buddy_name:
|
700
|
+
lst = field_actions.setdefault(ba.action.buddy_name, [])
|
701
|
+
lst.append(ba)
|
702
|
+
res._field_actions = field_actions
|
697
703
|
|
698
704
|
# logger.info("20161219 kernel_startup done")
|
699
705
|
|
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)
|
lino/core/model.py
CHANGED
lino/core/plugin.py
CHANGED
lino/core/renderer.py
CHANGED
@@ -19,6 +19,7 @@ from django.utils.translation import gettext as _
|
|
19
19
|
from django.utils.translation import get_language
|
20
20
|
|
21
21
|
from etgen.html2rst import RstTable
|
22
|
+
from etgen.utils import join_elems
|
22
23
|
# from lino import logger
|
23
24
|
from lino.utils import isiterable
|
24
25
|
from lino.utils.jsgen import py2js, js_code
|
@@ -497,7 +498,7 @@ class HtmlRenderer(Renderer):
|
|
497
498
|
return E.p(*buttons)
|
498
499
|
|
499
500
|
def get_home_url(self, *args, **kw):
|
500
|
-
return settings.SITE.kernel.
|
501
|
+
return settings.SITE.kernel.editing_front_end.build_plain_url(*args, **kw)
|
501
502
|
|
502
503
|
def obj2url(self, ar, obj):
|
503
504
|
ba = obj.get_detail_action(ar)
|
@@ -836,10 +837,11 @@ class TextRenderer(HtmlRenderer):
|
|
836
837
|
# print(f"20240929 {nosummary} {display_mode} {ar}")
|
837
838
|
# yield "20240506 {}".format(ar)
|
838
839
|
if display_mode == constants.DISPLAY_MODE_SUMMARY:
|
839
|
-
s = to_rst(
|
840
|
-
|
841
|
-
|
842
|
-
|
840
|
+
s = to_rst(tostring(E.span(*join_elems(ar.plain_toolbar_buttons()))),
|
841
|
+
stripped=stripped)
|
842
|
+
if s:
|
843
|
+
s += " | "
|
844
|
+
s += to_rst(ar.actor.get_table_summary(ar), stripped=stripped)
|
843
845
|
if stripped:
|
844
846
|
s = s.strip()
|
845
847
|
yield s
|
lino/core/requests.py
CHANGED
@@ -1562,7 +1562,7 @@ class BaseRequest:
|
|
1562
1562
|
# assert iselement(btn)
|
1563
1563
|
buttons.append(btn)
|
1564
1564
|
# print("20181106", cls, self.bound_action, buttons)
|
1565
|
-
|
1565
|
+
buttons.append(self.open_in_own_window_button()) # 20250713
|
1566
1566
|
return buttons
|
1567
1567
|
# if len(buttons) == 0:
|
1568
1568
|
# return None
|
@@ -1842,10 +1842,9 @@ class ActionRequest(BaseRequest):
|
|
1842
1842
|
pv = self.actor.param_defaults(self)
|
1843
1843
|
for k in pv.keys():
|
1844
1844
|
if k not in self.actor.parameters:
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1848
|
-
)
|
1845
|
+
msg = f"{self.actor} param_defaults() returned keyword {k}"
|
1846
|
+
msg += f" (must be one of {sorted(self.actor.parameters.keys())})"
|
1847
|
+
raise Exception(msg)
|
1849
1848
|
|
1850
1849
|
# New since 20120913. E.g. newcomers.Newcomers is a
|
1851
1850
|
# simple pcsw.Clients with
|
lino/core/site.py
CHANGED
@@ -862,7 +862,7 @@ class Site(object):
|
|
862
862
|
needed_by = ip
|
863
863
|
# while needed_by.needed_by is not None:
|
864
864
|
# needed_by = needed_by.needed_by
|
865
|
-
for dep in ip.
|
865
|
+
for dep in ip.get_needed_plugins():
|
866
866
|
k2 = dep.rsplit(".")[-1]
|
867
867
|
if k2 not in self.plugins:
|
868
868
|
install_plugin(dep, needed_by=needed_by)
|