lino 24.9.4__py3-none-any.whl → 24.10.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 -5
- lino/api/doctest.py +18 -1
- lino/core/actions.py +15 -8
- lino/core/actors.py +82 -8
- lino/core/constants.py +20 -4
- lino/core/dashboard.py +1 -3
- lino/core/dbtables.py +33 -50
- lino/core/elems.py +26 -33
- lino/core/fields.py +13 -7
- lino/core/kernel.py +3 -3
- lino/core/model.py +1 -0
- lino/core/renderer.py +33 -20
- lino/core/requests.py +18 -18
- lino/core/store.py +11 -11
- lino/core/tablerequest.py +5 -5
- lino/core/tables.py +39 -25
- lino/mixins/dupable.py +1 -1
- lino/modlib/checkdata/models.py +1 -1
- lino/modlib/comments/mixins.py +2 -2
- lino/modlib/comments/ui.py +83 -77
- lino/modlib/dupable/models.py +1 -1
- lino/modlib/export_excel/models.py +0 -3
- lino/modlib/extjs/ext_renderer.py +8 -8
- lino/modlib/extjs/views.py +6 -16
- lino/modlib/help/management/commands/makehelp.py +1 -1
- lino/modlib/notify/models.py +1 -1
- lino/modlib/printing/actions.py +0 -5
- lino/modlib/publisher/ui.py +3 -3
- lino/modlib/publisher/views.py +10 -10
- lino/modlib/search/models.py +2 -2
- lino/modlib/system/mixins.py +0 -10
- lino/modlib/uploads/mixins.py +6 -3
- lino/modlib/uploads/ui.py +2 -2
- lino/modlib/users/mixins.py +5 -5
- lino/sphinxcontrib/actordoc.py +1 -1
- lino/sphinxcontrib/logo/static/linodocs.css +1 -8
- lino/utils/choosers.py +1 -1
- lino/utils/report.py +1 -1
- {lino-24.9.4.dist-info → lino-24.10.0.dist-info}/METADATA +1 -1
- {lino-24.9.4.dist-info → lino-24.10.0.dist-info}/RECORD +43 -43
- {lino-24.9.4.dist-info → lino-24.10.0.dist-info}/WHEEL +0 -0
- {lino-24.9.4.dist-info → lino-24.10.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-24.9.4.dist-info → lino-24.10.0.dist-info}/licenses/COPYING +0 -0
lino/__init__.py
CHANGED
@@ -26,11 +26,7 @@ defines no models, some template files, a series of :term:`django-admin commands
|
|
26
26
|
|
27
27
|
"""
|
28
28
|
|
29
|
-
__version__ = '24.
|
30
|
-
|
31
|
-
# from __future__ import unicode_literals
|
32
|
-
# from __future__ import absolute_import
|
33
|
-
# from builtins import str
|
29
|
+
__version__ = '24.10.0'
|
34
30
|
|
35
31
|
# import setuptools # avoid UserWarning "Distutils was imported before Setuptools"?
|
36
32
|
|
lino/api/doctest.py
CHANGED
@@ -314,7 +314,7 @@ def get_fields(model, fieldnames=None, columns=None):
|
|
314
314
|
get_field = model.get_data_elem
|
315
315
|
if fieldnames is None:
|
316
316
|
fieldnames = model.column_names
|
317
|
-
# get_handle().
|
317
|
+
# get_handle().grid_layout.main.columns
|
318
318
|
else:
|
319
319
|
get_field = model.parameters.get
|
320
320
|
if fieldnames is None:
|
@@ -773,3 +773,20 @@ def show_change_watchers():
|
|
773
773
|
[full_model_name(m), ws.master_key, " ".join(sorted(ws.ignored_fields))]
|
774
774
|
)
|
775
775
|
print(table(headers, rows, max_width=40))
|
776
|
+
|
777
|
+
def show_display_modes():
|
778
|
+
"""
|
779
|
+
Show the availble display modes per actor.
|
780
|
+
"""
|
781
|
+
dml = sorted(constants.DISPLAY_MODES - constants.BASIC_DISPLAY_MODES)
|
782
|
+
headers = ["actor"]
|
783
|
+
headers += dml
|
784
|
+
rows = []
|
785
|
+
for a in sorted(actors.actors_list, key=str):
|
786
|
+
if not a.is_abstract():
|
787
|
+
rows.append(
|
788
|
+
[str(a)] + [
|
789
|
+
("x" if dm in a.extra_display_modes else "")
|
790
|
+
for dm in dml]
|
791
|
+
)
|
792
|
+
print(table(headers, rows))
|
lino/core/actions.py
CHANGED
@@ -85,7 +85,10 @@ def resolve_layout(cls, k, spec, layout_class, **options):
|
|
85
85
|
def install_layout(cls, k, layout_class, **options):
|
86
86
|
"""
|
87
87
|
- `cls` is the actor (a class object)
|
88
|
-
|
88
|
+
|
89
|
+
- `k` is one of 'grid_layout', 'detail_layout', 'insert_layout',
|
90
|
+
'params_layout', 'card_layout', 'list_layout'
|
91
|
+
|
89
92
|
- `layout_class`
|
90
93
|
|
91
94
|
"""
|
@@ -437,6 +440,8 @@ class Action(Parametrizable, Permittable):
|
|
437
440
|
On actions that opens_a_window this must be a unique one-letter
|
438
441
|
string expressing the window type.
|
439
442
|
|
443
|
+
See `constants.WINDOW_TYPES`.
|
444
|
+
|
440
445
|
Allowed values are:
|
441
446
|
|
442
447
|
- None : opens_a_window is False
|
@@ -486,6 +491,12 @@ class Action(Parametrizable, Permittable):
|
|
486
491
|
|
487
492
|
register_params(self)
|
488
493
|
|
494
|
+
if self.callable_from is not None:
|
495
|
+
for c in self.callable_from:
|
496
|
+
if not c in constants.WINDOW_TYPES:
|
497
|
+
raise Exception(f"Invalid window_type spec {c} in {self}")
|
498
|
+
|
499
|
+
|
489
500
|
def __get__(self, instance, owner):
|
490
501
|
"""
|
491
502
|
When a model has an action "foo", then getting an attribute
|
@@ -785,7 +796,7 @@ class ShowTable(TableAction):
|
|
785
796
|
use_param_panel = True
|
786
797
|
show_in_workflow = False
|
787
798
|
opens_a_window = True
|
788
|
-
window_type =
|
799
|
+
window_type = constants.WINDOW_TYPE_TABLE
|
789
800
|
action_name = "grid"
|
790
801
|
select_rows = False
|
791
802
|
callable_from = None
|
@@ -809,7 +820,7 @@ class ShowDetail(Action):
|
|
809
820
|
icon_name = "application_form"
|
810
821
|
ui5_icon_name = "sap-icon://detail-view"
|
811
822
|
opens_a_window = True
|
812
|
-
window_type =
|
823
|
+
window_type = constants.WINDOW_TYPE_DETAIL
|
813
824
|
show_in_workflow = False
|
814
825
|
save_action_name = "submit_detail"
|
815
826
|
callable_from = "t"
|
@@ -893,7 +904,7 @@ class ShowInsert(TableAction):
|
|
893
904
|
show_in_workflow = False
|
894
905
|
show_in_side_toolbar = True
|
895
906
|
opens_a_window = True
|
896
|
-
window_type =
|
907
|
+
window_type = constants.WINDOW_TYPE_INSERT
|
897
908
|
hide_navigator = True # 20210509
|
898
909
|
hide_top_toolbar = True # 20210509
|
899
910
|
sort_index = 10
|
@@ -1342,11 +1353,7 @@ class DeleteSelected(MultipleRowAction):
|
|
1342
1353
|
sort_index = 30
|
1343
1354
|
readonly = False
|
1344
1355
|
show_in_workflow = False
|
1345
|
-
# required_roles = set([SiteUser])
|
1346
|
-
# ~ callable_from = (ShowTable,ShowDetail)
|
1347
|
-
# ~ needs_selection = True
|
1348
1356
|
label = _("Delete")
|
1349
|
-
# ~ url_action_name = 'delete'
|
1350
1357
|
hotkey = keyboard.DELETE # (ctrl=True)
|
1351
1358
|
|
1352
1359
|
# ~ client_side = True
|
lino/core/actors.py
CHANGED
@@ -27,10 +27,11 @@ from lino.utils import MissingRow
|
|
27
27
|
from lino.core import fields
|
28
28
|
from lino.core import actions
|
29
29
|
from lino.core import layouts
|
30
|
+
from lino.core import constants
|
30
31
|
from lino.core.requests import ActionRequest
|
31
32
|
from lino.core.boundaction import BoundAction
|
32
33
|
from lino.core.exceptions import ChangedAPI
|
33
|
-
from lino.core.constants import _handle_attr_name
|
34
|
+
from lino.core.constants import _handle_attr_name
|
34
35
|
from lino.core.permissions import add_requirements, Permittable
|
35
36
|
from lino.core.utils import resolve_model
|
36
37
|
from lino.core.utils import error2str
|
@@ -291,7 +292,17 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
291
292
|
# """
|
292
293
|
|
293
294
|
only_fields = None
|
294
|
-
|
295
|
+
|
296
|
+
default_display_modes = None
|
297
|
+
|
298
|
+
# extra_display_modes = set()
|
299
|
+
# extra_display_modes = {constants.DISPLAY_MODE_SUMMARY}
|
300
|
+
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
|
+
"""
|
295
306
|
|
296
307
|
app_label = None
|
297
308
|
"""
|
@@ -428,6 +439,7 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
428
439
|
default_action = None
|
429
440
|
actor_id = None
|
430
441
|
|
442
|
+
grid_layout = None
|
431
443
|
detail_layout = None
|
432
444
|
insert_layout = None
|
433
445
|
card_layout = None
|
@@ -730,8 +742,12 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
730
742
|
|
731
743
|
if hasattr(cls, "required"):
|
732
744
|
raise ChangedAPI(
|
733
|
-
"{
|
745
|
+
f"{cls} must convert `required` to `required_roles`"
|
734
746
|
)
|
747
|
+
if hasattr(cls, "display_mode"):
|
748
|
+
# cls.default_display_modes = {k:v for k, v in cls.display_mode}
|
749
|
+
# logger.info(f"{cls} uses deprecated `display_mode`, please convert to `default_display_modes`.")
|
750
|
+
raise ChangedAPI(f"{cls} must convert `display_mode` to `default_display_modes`")
|
735
751
|
|
736
752
|
master = getattr(cls, "master", None)
|
737
753
|
if isinstance(master, str):
|
@@ -760,6 +776,45 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
760
776
|
cls.virtual_fields["detail_link"] = de
|
761
777
|
# cls.add_virtual_field('detail_link', de)
|
762
778
|
|
779
|
+
if cls.abstract:
|
780
|
+
return
|
781
|
+
|
782
|
+
edm = set() # extra display modes to add automatically
|
783
|
+
if cls.default_display_modes is not None:
|
784
|
+
for v in cls.default_display_modes.values():
|
785
|
+
if v not in constants.DISPLAY_MODES:
|
786
|
+
raise Exception(f"Invalid display mode {v} in {cls}")
|
787
|
+
if v not in constants.BASIC_DISPLAY_MODES:
|
788
|
+
# be sure to have our copy the extra_display_modes set
|
789
|
+
# if str(cls) == "comments.Comments":
|
790
|
+
# print(f"20240929 extra_display_modes is {cls.extra_display_modes}, we add {v}")
|
791
|
+
# cls.extra_display_modes = cls.extra_display_modes | {v}
|
792
|
+
edm.add(v)
|
793
|
+
|
794
|
+
if cls.card_layout is not None:
|
795
|
+
edm.add(constants.DISPLAY_MODE_CARDS)
|
796
|
+
if 'row_as_paragraph' in cls.__dict__:
|
797
|
+
edm.add(constants.DISPLAY_MODE_LIST)
|
798
|
+
# if 'row_as_page' in cls.__dict__:
|
799
|
+
# edm.add(constants.DISPLAY_MODE_STORY)
|
800
|
+
if model is not None:
|
801
|
+
if model.extra_display_modes is not None:
|
802
|
+
for v in model.extra_display_modes:
|
803
|
+
if v not in constants.DISPLAY_MODES:
|
804
|
+
raise Exception(f"Invalid display mode {v} in {model}.extra_display_modes")
|
805
|
+
edm |= model.extra_display_modes
|
806
|
+
if 'as_paragraph' in model.__dict__:
|
807
|
+
edm.add(constants.DISPLAY_MODE_LIST)
|
808
|
+
if 'as_page' in model.__dict__:
|
809
|
+
edm.add(constants.DISPLAY_MODE_STORY)
|
810
|
+
# no need to automatically add summary because it's in default_display_modes of every table
|
811
|
+
# if 'as_summary_item' in model.__dict__:
|
812
|
+
# edm.add(constants.DISPLAY_MODE_SUMMARY)
|
813
|
+
if len(edm) > 0:
|
814
|
+
# if str(cls) == "comments.Comments":
|
815
|
+
# print(f"20240929 {cls} extra_display_modes add {edm}")
|
816
|
+
cls.extra_display_modes = cls.extra_display_modes | edm
|
817
|
+
|
763
818
|
@classmethod
|
764
819
|
def init_layouts(cls):
|
765
820
|
# 20200430 this was previously part of class_init, but is now called in
|
@@ -1238,7 +1293,7 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
1238
1293
|
yield t
|
1239
1294
|
for k in self.simple_parameters:
|
1240
1295
|
v = getattr(ar.param_values, k)
|
1241
|
-
if v and v != CHOICES_BLANK_FILTER_VALUE:
|
1296
|
+
if v and v != constants.CHOICES_BLANK_FILTER_VALUE:
|
1242
1297
|
if v is True:
|
1243
1298
|
# For BooleanField no need to add "True" in the title
|
1244
1299
|
yield str(self.parameters[k].verbose_name)
|
@@ -1605,10 +1660,10 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
1605
1660
|
return wl.get_layout_handle()
|
1606
1661
|
|
1607
1662
|
@classmethod
|
1608
|
-
def
|
1663
|
+
def get_grid_layout(cls, *args):
|
1609
1664
|
assert cls.default_action is not None
|
1610
1665
|
ah = cls.get_handle()
|
1611
|
-
return ah.
|
1666
|
+
return ah.get_grid_layout()
|
1612
1667
|
|
1613
1668
|
@classmethod
|
1614
1669
|
def get_detail_elems(cls):
|
@@ -1831,6 +1886,25 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
1831
1886
|
# assert_safe(html_text) # temporary 20240506
|
1832
1887
|
return format_html(DIVTPL, html_text)
|
1833
1888
|
|
1889
|
+
@classmethod
|
1890
|
+
def get_table_story(cls, obj, ar):
|
1891
|
+
sar = cls.request(parent=ar, master_instance=obj, is_on_main_actor=False)
|
1892
|
+
html = mark_safe("")
|
1893
|
+
for i, obj in enumerate(sar.data_iterator):
|
1894
|
+
if i == cls.preview_limit:
|
1895
|
+
break
|
1896
|
+
s = obj.as_story_item(sar)
|
1897
|
+
# assert_safe(s) # temporary 20240506
|
1898
|
+
html += s
|
1899
|
+
if cls.insert_action is not None:
|
1900
|
+
if not cls.editable:
|
1901
|
+
return html
|
1902
|
+
ir = cls.insert_action.request_from(sar)
|
1903
|
+
if ir.get_permission():
|
1904
|
+
html = tostring(ir.ar2button()) + html
|
1905
|
+
# assert_safe(html) # temporary 20240506
|
1906
|
+
return html
|
1907
|
+
|
1834
1908
|
@classmethod
|
1835
1909
|
def get_table_summary(cls, obj, ar):
|
1836
1910
|
"""
|
@@ -1892,7 +1966,7 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
1892
1966
|
if ar is None:
|
1893
1967
|
return str(self)
|
1894
1968
|
# cols = ar.actor.get_detail_layout().main
|
1895
|
-
cols = cls.
|
1969
|
+
cols = cls.get_grid_layout().main.columns
|
1896
1970
|
cols = [c for c in cols if not c.value.get("hidden")]
|
1897
1971
|
if fmt is None:
|
1898
1972
|
|
@@ -1914,7 +1988,7 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
|
|
1914
1988
|
values = [v for v in values if v is not None]
|
1915
1989
|
s = ", ".join(values)
|
1916
1990
|
# s = str(ar.actor)
|
1917
|
-
# print("20231218", s) # self, ar.actor.
|
1991
|
+
# print("20231218", s) # self, ar.actor.get_grid_layout().main.columns)
|
1918
1992
|
return s
|
1919
1993
|
|
1920
1994
|
@classmethod
|
lino/core/constants.py
CHANGED
@@ -47,22 +47,38 @@ URL_PARAM_WINDOW_TYPE = "wt"
|
|
47
47
|
|
48
48
|
WINDOW_TYPE_TABLE = "t"
|
49
49
|
WINDOW_TYPE_DETAIL = "d"
|
50
|
-
WINDOW_TYPE_CARDS = "c"
|
51
|
-
WINDOW_TYPE_GALLERIA = "g"
|
52
50
|
WINDOW_TYPE_INSERT = "i"
|
53
51
|
WINDOW_TYPE_PARAMS = "p"
|
54
52
|
|
53
|
+
WINDOW_TYPES = {
|
54
|
+
WINDOW_TYPE_TABLE, WINDOW_TYPE_DETAIL, WINDOW_TYPE_INSERT,
|
55
|
+
WINDOW_TYPE_PARAMS
|
56
|
+
}
|
57
|
+
|
55
58
|
URL_PARAM_DISPLAY_MODE = "dm"
|
56
59
|
|
57
|
-
DISPLAY_MODE_TABLE = "grid" # deprecated
|
58
60
|
DISPLAY_MODE_GRID = "grid"
|
59
61
|
DISPLAY_MODE_DETAIL = "detail"
|
62
|
+
DISPLAY_MODE_HTML = "html"
|
60
63
|
DISPLAY_MODE_SUMMARY = "summary"
|
61
64
|
DISPLAY_MODE_LIST = "list"
|
62
65
|
DISPLAY_MODE_CARDS = "cards"
|
63
66
|
DISPLAY_MODE_GALLERY = "gallery"
|
64
67
|
DISPLAY_MODE_STORY = "story"
|
65
|
-
|
68
|
+
|
69
|
+
DISPLAY_MODES = {
|
70
|
+
DISPLAY_MODE_GRID,
|
71
|
+
DISPLAY_MODE_DETAIL,
|
72
|
+
DISPLAY_MODE_HTML,
|
73
|
+
DISPLAY_MODE_SUMMARY,
|
74
|
+
DISPLAY_MODE_LIST,
|
75
|
+
DISPLAY_MODE_CARDS,
|
76
|
+
DISPLAY_MODE_GALLERY,
|
77
|
+
DISPLAY_MODE_STORY}
|
78
|
+
|
79
|
+
BASIC_DISPLAY_MODES = {
|
80
|
+
DISPLAY_MODE_GRID,
|
81
|
+
DISPLAY_MODE_DETAIL}
|
66
82
|
|
67
83
|
# ~ URL_PARAM_EUSER = 'euser'
|
68
84
|
# ~ URL_PARAM_EUSER = 'su'
|
lino/core/dashboard.py
CHANGED
@@ -74,9 +74,7 @@ class DashboardItem(Permittable):
|
|
74
74
|
assert sar.renderer is not None
|
75
75
|
if isinstance(sar, TableRequest):
|
76
76
|
# TODO 20220930 until now, dashboard was always acting as if
|
77
|
-
# display_mode was
|
78
|
-
# if sar.actor.display_mode == constants.DISPLAY_MODE_TABLE
|
79
|
-
|
77
|
+
# display_mode was DISPLAY_MODE_GRID
|
80
78
|
for e in sar.renderer.table2story(sar, **kwargs):
|
81
79
|
# assert_safe(tostring(e))
|
82
80
|
yield tostring(e)
|
lino/core/dbtables.py
CHANGED
@@ -212,7 +212,6 @@ class Table(AbstractTable):
|
|
212
212
|
|
213
213
|
@classmethod
|
214
214
|
def init_layouts(cls):
|
215
|
-
# if cls.list_layout is None:
|
216
215
|
if cls.model is not None and issubclass(cls.model, Model):
|
217
216
|
if not cls.abstract:
|
218
217
|
for tbl in cls.__mro__:
|
@@ -222,7 +221,7 @@ class Table(AbstractTable):
|
|
222
221
|
if issubclass(tbl, actors.Actor):
|
223
222
|
cls.list_layout = "list_item"
|
224
223
|
break
|
225
|
-
super(
|
224
|
+
super().init_layouts()
|
226
225
|
|
227
226
|
@classmethod
|
228
227
|
def add_quick_search_filter(cls, qs, search_text):
|
@@ -237,7 +236,7 @@ class Table(AbstractTable):
|
|
237
236
|
|
238
237
|
@classmethod
|
239
238
|
def get_chooser_for_field(self, fieldname):
|
240
|
-
ch = super(
|
239
|
+
ch = super().get_chooser_for_field(fieldname)
|
241
240
|
if ch is not None:
|
242
241
|
return ch
|
243
242
|
if self.model is not None:
|
@@ -292,7 +291,7 @@ class Table(AbstractTable):
|
|
292
291
|
for ds in yield_model_detail_sets(self.model):
|
293
292
|
yield ds
|
294
293
|
|
295
|
-
for s in super(
|
294
|
+
for s in super().get_detail_sets():
|
296
295
|
yield s
|
297
296
|
|
298
297
|
# @classmethod
|
@@ -302,25 +301,6 @@ class Table(AbstractTable):
|
|
302
301
|
# return vf
|
303
302
|
# return cls.model._meta.get_field(name)
|
304
303
|
|
305
|
-
@classmethod
|
306
|
-
def get_table_story(cls, obj, ar):
|
307
|
-
sar = cls.request(parent=ar, master_instance=obj, is_on_main_actor=False)
|
308
|
-
html = mark_safe("")
|
309
|
-
for i, obj in enumerate(sar.data_iterator):
|
310
|
-
if i == cls.preview_limit:
|
311
|
-
break
|
312
|
-
s = obj.as_story_item(sar)
|
313
|
-
# assert_safe(s) # temporary 20240506
|
314
|
-
html += s
|
315
|
-
if cls.insert_action is not None:
|
316
|
-
if not cls.editable:
|
317
|
-
return html
|
318
|
-
ir = cls.insert_action.request_from(sar)
|
319
|
-
if ir.get_permission():
|
320
|
-
html = tostring(ir.ar2button()) + html
|
321
|
-
# assert_safe(html) # temporary 20240506
|
322
|
-
return html
|
323
|
-
|
324
304
|
@classmethod
|
325
305
|
def get_pk_field(self):
|
326
306
|
return self.model._meta.pk
|
@@ -334,18 +314,22 @@ class Table(AbstractTable):
|
|
334
314
|
Note: `ar` may not be None.
|
335
315
|
|
336
316
|
"""
|
337
|
-
return self.model.objects.get(pk=pk)
|
317
|
+
# return self.model.objects.get(pk=pk)
|
318
|
+
# raise Exception("20240929")
|
319
|
+
qs = self.get_request_queryset(ar)
|
338
320
|
# qs = self.model.get_request_queryset(ar)
|
321
|
+
|
322
|
+
return qs.get(pk=pk)
|
339
323
|
# try:
|
340
324
|
# return qs.get(pk=pk)
|
341
325
|
# # return self.get_queryset(ar).get(pk=pk)
|
342
326
|
# except ValueError:
|
343
327
|
# return None
|
344
328
|
# except self.model.DoesNotExist:
|
345
|
-
#
|
346
|
-
#
|
347
|
-
#
|
348
|
-
#
|
329
|
+
# import sqlparse
|
330
|
+
# sql = str(qs.query).replace('"', '')
|
331
|
+
# sql = sqlparse.format(sql, reindent=True, keyword_case='upper')
|
332
|
+
# raise Exception("20240324 {}, {}".format(ar.param_values, sql))
|
349
333
|
# return None
|
350
334
|
|
351
335
|
# @classmethod
|
@@ -520,7 +504,7 @@ class Table(AbstractTable):
|
|
520
504
|
self.master_field = fk
|
521
505
|
# self.hidden_columns |= set([fk.name])
|
522
506
|
|
523
|
-
super(
|
507
|
+
super().class_init()
|
524
508
|
|
525
509
|
if self.order_by is not None:
|
526
510
|
if not isinstance(self.order_by, (list, tuple)):
|
@@ -547,7 +531,7 @@ class Table(AbstractTable):
|
|
547
531
|
|
548
532
|
@classmethod
|
549
533
|
def do_setup(self):
|
550
|
-
super(
|
534
|
+
super().do_setup()
|
551
535
|
# AbstractTable.do_setup(self)
|
552
536
|
if self.model is None:
|
553
537
|
return
|
@@ -567,7 +551,7 @@ class Table(AbstractTable):
|
|
567
551
|
|
568
552
|
@classmethod
|
569
553
|
def make_disabled_fields(cls, obj, ar):
|
570
|
-
s = super(
|
554
|
+
s = super().make_disabled_fields(obj, ar)
|
571
555
|
|
572
556
|
if obj is not None and ar is not None:
|
573
557
|
s |= obj.disabled_fields(ar)
|
@@ -605,8 +589,7 @@ class Table(AbstractTable):
|
|
605
589
|
if self.delete_action is None:
|
606
590
|
return "No delete_action"
|
607
591
|
if not self.get_row_permission(
|
608
|
-
obj, ar, self.get_row_state(obj), self.delete_action
|
609
|
-
):
|
592
|
+
obj, ar, self.get_row_state(obj), self.delete_action):
|
610
593
|
# print "20130222 ar is %r" % ar
|
611
594
|
# logger.info("20130225 dbtables.disable_delete no permission")
|
612
595
|
return _("You have no permission to delete this row.")
|
@@ -634,23 +617,23 @@ class Table(AbstractTable):
|
|
634
617
|
qs = qs.exclude(**ar.exclude)
|
635
618
|
# qs = qs.exclude(ar.exclude)
|
636
619
|
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
620
|
+
if ar.param_values is not None:
|
621
|
+
spv = dict()
|
622
|
+
for k in self.simple_parameters:
|
623
|
+
v = getattr(ar.param_values, k)
|
624
|
+
# if "room" in k:
|
625
|
+
# print("20200423", k, v, self.simple_parameters, ar.param_values)
|
626
|
+
if v == constants.CHOICES_BLANK_FILTER_VALUE:
|
627
|
+
spv[k + "__isnull"] = True
|
628
|
+
elif v == constants.CHOICES_NOT_BLANK_FILTER_VALUE:
|
629
|
+
spv[k + "__isnull"] = False
|
630
|
+
elif v is not None:
|
631
|
+
spv[k] = v
|
632
|
+
# print("20240506 {} = {}".format(k, v))
|
633
|
+
ar.known_values[k] = v # make it an obvious field
|
634
|
+
|
635
|
+
qs = self.model.add_param_filter(qs, **spv)
|
636
|
+
# qs = self.model.add_param_filter(qs, **ar.param_values)
|
654
637
|
|
655
638
|
if self.filter:
|
656
639
|
qs = qs.filter(self.filter)
|
lino/core/elems.py
CHANGED
@@ -854,11 +854,11 @@ class TextFieldElement(FieldElement):
|
|
854
854
|
|
855
855
|
def get_field_options(self, **kw):
|
856
856
|
kw = super().get_field_options(**kw)
|
857
|
-
if isinstance(self.field, fields.VirtualField) and self.field.simple_elem:
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
857
|
+
# if isinstance(self.field, fields.VirtualField) and self.field.simple_elem:
|
858
|
+
# kw["virtualField"] = True
|
859
|
+
# kw["noHeaderDecore"] = True
|
860
|
+
# kw["noEditorHeader"] = True
|
861
|
+
# kw["alwaysEditable"] = True
|
862
862
|
kw["format"] = self.format
|
863
863
|
return kw
|
864
864
|
|
@@ -2311,8 +2311,8 @@ class Panel(Container):
|
|
2311
2311
|
|
2312
2312
|
|
2313
2313
|
class GridElement(Container):
|
2314
|
-
"""Represents a Lino.GridPanel, i.e. the widget used to
|
2315
|
-
table
|
2314
|
+
"""Represents a Lino.GridPanel, i.e. the widget used to render a
|
2315
|
+
table in :term:`grid mode`.
|
2316
2316
|
|
2317
2317
|
"""
|
2318
2318
|
|
@@ -2343,14 +2343,13 @@ class GridElement(Container):
|
|
2343
2343
|
self.actor = rpt
|
2344
2344
|
if len(columns) == 0:
|
2345
2345
|
self.rh = rpt.get_handle()
|
2346
|
-
if not hasattr(self.rh, "
|
2346
|
+
if not hasattr(self.rh, "grid_layout"):
|
2347
2347
|
raise Exception(
|
2348
|
-
"Handle for {0} (model {1}) has no
|
2348
|
+
"Handle for {0} (model {1}) has no grid_layout".format(
|
2349
2349
|
rpt, rpt.model
|
2350
2350
|
)
|
2351
2351
|
)
|
2352
|
-
columns = self.rh.
|
2353
|
-
# columns = self.rh.list_layout._main.elements
|
2352
|
+
columns = self.rh.grid_layout.main.columns
|
2354
2353
|
w = 0
|
2355
2354
|
for e in columns:
|
2356
2355
|
w += e.width or e.preferred_width
|
@@ -2433,20 +2432,15 @@ class SlaveContainer(GridElement):
|
|
2433
2432
|
slaves = None
|
2434
2433
|
|
2435
2434
|
def __init__(self, layout_handle, name, rpt, *columns, **kw):
|
2436
|
-
dms = [dm[1] for dm in rpt.display_mode]
|
2437
2435
|
slaves = dict()
|
2438
|
-
if constants.DISPLAY_MODE_STORY in
|
2436
|
+
if constants.DISPLAY_MODE_STORY in rpt.extra_display_modes:
|
2439
2437
|
slaves["story"] = get_story_element(layout_handle, rpt, name, **kw)
|
2440
|
-
if constants.DISPLAY_MODE_LIST in
|
2438
|
+
if constants.DISPLAY_MODE_LIST in rpt.extra_display_modes:
|
2441
2439
|
slaves["list"] = get_list_element(layout_handle, rpt, name, **kw)
|
2442
|
-
|
2443
|
-
|
2444
|
-
|
2445
|
-
|
2446
|
-
# slaves["story"] = get_story_element(layout_handle, rpt, name, **kw)
|
2447
|
-
# slaves["list"] = get_list_element(layout_handle, rpt, name, **kw)
|
2448
|
-
slaves["html"] = get_htmlbox_element(layout_handle, rpt, name, **kw)
|
2449
|
-
slaves["summary"] = get_summary_element(layout_handle, rpt, name, **kw)
|
2440
|
+
if constants.DISPLAY_MODE_SUMMARY in rpt.extra_display_modes:
|
2441
|
+
slaves["summary"] = get_summary_element(layout_handle, rpt, name, **kw)
|
2442
|
+
if constants.DISPLAY_MODE_HTML in rpt.extra_display_modes:
|
2443
|
+
slaves["html"] = get_htmlbox_element(layout_handle, rpt, name, **kw)
|
2450
2444
|
if slaves:
|
2451
2445
|
self.slaves = slaves
|
2452
2446
|
super().__init__(layout_handle, name, rpt, *columns, **kw)
|
@@ -2845,13 +2839,11 @@ def create_layout_element(lh, name, **kw):
|
|
2845
2839
|
|
2846
2840
|
# print("20240317", de, lh)
|
2847
2841
|
if isinstance(lh.layout, FormLayout):
|
2848
|
-
# When a table is specified in the layout of a
|
2849
|
-
#
|
2850
|
-
#
|
2851
|
-
#
|
2852
|
-
#
|
2853
|
-
# `display_mode` of the table.
|
2854
|
-
|
2842
|
+
# When a table is specified in the layout of a DetailWindow, then it
|
2843
|
+
# will be rendered as a :term:`slave panel`. The panel will have a
|
2844
|
+
# tool button to "open that table in its own window". The display
|
2845
|
+
# mode of that summary is defined by the `default_display_modes` of
|
2846
|
+
# the table.
|
2855
2847
|
if lh.ui.renderer.extjs_version is not None:
|
2856
2848
|
if de.label is not None:
|
2857
2849
|
js = (
|
@@ -2871,17 +2863,18 @@ def create_layout_element(lh, name, **kw):
|
|
2871
2863
|
kw.update(label="{} {}".format(de.get_label(), tostring(btn)))
|
2872
2864
|
|
2873
2865
|
if (
|
2874
|
-
len(de.
|
2866
|
+
len(de.default_display_modes) > 1
|
2875
2867
|
and lh.ui.renderer.front_end.media_name == "react"
|
2876
2868
|
):
|
2869
|
+
# print(f"20240928 {de} in {lh} gets SlaveContainer")
|
2877
2870
|
return SlaveContainer(lh, name, de, **kw)
|
2878
2871
|
|
2879
2872
|
dm = de.get_display_mode()
|
2880
|
-
if dm in
|
2881
|
-
constants.
|
2873
|
+
if dm in {
|
2874
|
+
constants.DISPLAY_MODE_GRID,
|
2882
2875
|
constants.DISPLAY_MODE_CARDS,
|
2883
2876
|
constants.DISPLAY_MODE_GALLERY
|
2884
|
-
|
2877
|
+
}:
|
2885
2878
|
kw.update(hide_top_toolbar=True)
|
2886
2879
|
if de.preview_limit is not None:
|
2887
2880
|
kw.update(preview_limit=de.preview_limit)
|
lino/core/fields.py
CHANGED
@@ -322,7 +322,7 @@ class FakeField(object):
|
|
322
322
|
setattr(self, k, v)
|
323
323
|
|
324
324
|
def __repr__(self):
|
325
|
-
# copied from django Field
|
325
|
+
# copied from django Field
|
326
326
|
path = "%s.%s" % (self.__class__.__module__, self.__class__.__qualname__)
|
327
327
|
name = getattr(self, "name", None)
|
328
328
|
if name is not None:
|
@@ -561,10 +561,10 @@ class VirtualField(FakeField):
|
|
561
561
|
(because they inherit from that class).
|
562
562
|
"""
|
563
563
|
|
564
|
-
simple_elem = False
|
565
|
-
"""
|
566
|
-
Used in :meth:`get_field_options` to set :term:`front end` rendering options.
|
567
|
-
"""
|
564
|
+
# simple_elem = False
|
565
|
+
# """
|
566
|
+
# Used in :meth:`get_field_options` to set :term:`front end` rendering options.
|
567
|
+
# """
|
568
568
|
|
569
569
|
def __init__(self, return_type, get=return_none, **kwargs):
|
570
570
|
"""
|
@@ -579,7 +579,7 @@ class VirtualField(FakeField):
|
|
579
579
|
self.return_type = return_type # a Django Field instance
|
580
580
|
self.get = get
|
581
581
|
|
582
|
-
self.simple_elem = kwargs.get("simple_elem", self.simple_elem)
|
582
|
+
# self.simple_elem = kwargs.get("simple_elem", self.simple_elem)
|
583
583
|
|
584
584
|
# if isinstance(return_type, FakeField):
|
585
585
|
# sortable_by = return_type.sortable_by
|
@@ -1217,6 +1217,12 @@ class TableRow(object):
|
|
1217
1217
|
|
1218
1218
|
_lino_default_table = None
|
1219
1219
|
_widget_options = {}
|
1220
|
+
extra_display_modes = None
|
1221
|
+
"""
|
1222
|
+
A set of extra display modes to make available on actors that use this model.
|
1223
|
+
|
1224
|
+
See :ref:`dg.dd.table.extra_display_modes`.
|
1225
|
+
"""
|
1220
1226
|
|
1221
1227
|
pk = None
|
1222
1228
|
|
@@ -1363,7 +1369,7 @@ class TableRow(object):
|
|
1363
1369
|
return tostring(self.as_summary_item(ar, **kwargs))
|
1364
1370
|
|
1365
1371
|
def as_story_item(self, ar, **kwargs):
|
1366
|
-
kwargs.update(display_mode=
|
1372
|
+
kwargs.update(display_mode=constants.DISPLAY_MODE_STORY)
|
1367
1373
|
return mark_safe("".join(self.as_page(ar, **kwargs)))
|
1368
1374
|
|
1369
1375
|
def as_page(self, ar, **kwargs):
|