lino 24.11.0__py3-none-any.whl → 25.1.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/core/actions.py +2 -4
- lino/core/actors.py +35 -14
- lino/core/dbtables.py +12 -12
- lino/core/fields.py +27 -18
- lino/core/inject.py +9 -2
- lino/core/kernel.py +11 -12
- lino/core/model.py +25 -39
- lino/core/renderer.py +3 -4
- lino/core/requests.py +91 -92
- lino/core/site.py +5 -46
- lino/core/store.py +4 -4
- lino/core/tables.py +0 -16
- lino/core/utils.py +5 -2
- lino/help_texts.py +7 -5
- lino/locale/bn/LC_MESSAGES/django.po +1225 -909
- lino/locale/de/LC_MESSAGES/django.mo +0 -0
- lino/locale/de/LC_MESSAGES/django.po +1748 -1392
- lino/locale/django.pot +1150 -909
- lino/locale/es/LC_MESSAGES/django.po +1721 -1347
- lino/locale/et/LC_MESSAGES/django.po +1267 -954
- lino/locale/fr/LC_MESSAGES/django.mo +0 -0
- lino/locale/fr/LC_MESSAGES/django.po +1207 -925
- lino/locale/nl/LC_MESSAGES/django.po +1261 -942
- lino/locale/pt_BR/LC_MESSAGES/django.po +1204 -906
- lino/locale/zh_Hant/LC_MESSAGES/django.po +1204 -906
- lino/mixins/dupable.py +8 -7
- lino/mixins/periods.py +10 -8
- lino/modlib/checkdata/__init__.py +1 -1
- lino/modlib/comments/choicelists.py +1 -1
- lino/modlib/comments/fixtures/demo2.py +4 -1
- lino/modlib/comments/mixins.py +9 -10
- lino/modlib/comments/models.py +4 -4
- lino/modlib/comments/ui.py +11 -2
- lino/modlib/dupable/models.py +10 -14
- lino/modlib/gfks/mixins.py +8 -1
- lino/modlib/jinja/choicelists.py +3 -3
- lino/modlib/jinja/renderer.py +2 -0
- lino/modlib/languages/fixtures/all_languages.py +4 -6
- lino/modlib/linod/mixins.py +3 -2
- lino/modlib/memo/mixins.py +17 -215
- lino/modlib/memo/models.py +41 -12
- lino/modlib/memo/parser.py +7 -3
- lino/modlib/notify/mixins.py +35 -34
- lino/modlib/periods/choicelists.py +46 -0
- lino/modlib/periods/mixins.py +26 -1
- lino/modlib/periods/models.py +17 -45
- lino/modlib/printing/choicelists.py +3 -3
- lino/modlib/publisher/choicelists.py +1 -4
- lino/modlib/search/models.py +17 -11
- lino/modlib/system/fixtures/__init__.py +0 -0
- lino/modlib/system/fixtures/std.py +5 -0
- lino/modlib/system/models.py +3 -2
- lino/modlib/uploads/__init__.py +10 -1
- lino/modlib/uploads/choicelists.py +3 -3
- lino/modlib/uploads/mixins.py +49 -51
- lino/modlib/uploads/models.py +144 -61
- lino/modlib/uploads/ui.py +12 -6
- lino/modlib/uploads/utils.py +113 -0
- lino/modlib/users/mixins.py +9 -6
- lino/modlib/weasyprint/choicelists.py +17 -7
- lino/utils/__init__.py +6 -0
- lino/utils/choosers.py +21 -8
- lino/utils/html.py +1 -1
- lino/utils/instantiator.py +9 -0
- lino/utils/media.py +2 -3
- lino/utils/soup.py +311 -0
- {lino-24.11.0.dist-info → lino-25.1.0.dist-info}/METADATA +2 -2
- {lino-24.11.0.dist-info → lino-25.1.0.dist-info}/RECORD +72 -67
- {lino-24.11.0.dist-info → lino-25.1.0.dist-info}/WHEEL +1 -1
- {lino-24.11.0.dist-info → lino-25.1.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-24.11.0.dist-info → lino-25.1.0.dist-info}/licenses/COPYING +0 -0
lino/core/requests.py
CHANGED
@@ -81,6 +81,26 @@ Subject: {subject}
|
|
81
81
|
def noop(ar):
|
82
82
|
return ar.success(gettext("Aborted"))
|
83
83
|
|
84
|
+
def mi2bp(master_instance, bp):
|
85
|
+
if master_instance is not None:
|
86
|
+
if isinstance(master_instance, (models.Model, TableRow)):
|
87
|
+
bp[constants.URL_PARAM_MASTER_PK] = master_instance.pk
|
88
|
+
if ContentType is not None and isinstance(
|
89
|
+
master_instance, models.Model
|
90
|
+
):
|
91
|
+
assert not master_instance._meta.abstract
|
92
|
+
mt = ContentType.objects.get_for_model(
|
93
|
+
master_instance.__class__).pk
|
94
|
+
bp[constants.URL_PARAM_MASTER_TYPE] = mt
|
95
|
+
# else:
|
96
|
+
# logger.warning("20141205 %s %s",
|
97
|
+
# self.master_instance,
|
98
|
+
# ContentType)
|
99
|
+
else: # if self.master is None:
|
100
|
+
# e.g. in accounting.MovementsByMatch the master is a `str`
|
101
|
+
bp[constants.URL_PARAM_MASTER_PK] = master_instance
|
102
|
+
# raise Exception("No master key for {} {!r}".format(
|
103
|
+
# self.master_instance.__class__, self.master_instance))
|
84
104
|
|
85
105
|
class StringLogger(logging.Logger):
|
86
106
|
# Instantiated by BaseRequest.capture_logger()
|
@@ -152,7 +172,7 @@ class ValidActionResponses(object):
|
|
152
172
|
|
153
173
|
no_data_text = None
|
154
174
|
title = None
|
155
|
-
"""The dynamic title to give to the window or component
|
175
|
+
"""The dynamic title to give to the window or component that shows this response.
|
156
176
|
TODO: is this still being used?
|
157
177
|
"""
|
158
178
|
|
@@ -308,7 +328,7 @@ class BaseRequest:
|
|
308
328
|
action_param_values = None
|
309
329
|
param_values = None
|
310
330
|
bound_action = None
|
311
|
-
known_values =
|
331
|
+
known_values = None
|
312
332
|
no_data_text = _("No data to display")
|
313
333
|
is_on_main_actor = True
|
314
334
|
permalink_uris = False
|
@@ -317,6 +337,12 @@ class BaseRequest:
|
|
317
337
|
master_instance = None
|
318
338
|
request = None
|
319
339
|
selected_rows = []
|
340
|
+
obvious_fields = None
|
341
|
+
order_by = None
|
342
|
+
filter = None
|
343
|
+
gridfilters = None,
|
344
|
+
quick_search = None
|
345
|
+
extra = None
|
320
346
|
content_type = "application/json"
|
321
347
|
requesting_panel = None
|
322
348
|
xcallback_answers = {}
|
@@ -331,11 +357,19 @@ class BaseRequest:
|
|
331
357
|
parent=None,
|
332
358
|
is_on_main_actor=True,
|
333
359
|
permalink_uris=None,
|
360
|
+
known_values=None,
|
361
|
+
obvious_fields=None,
|
334
362
|
**kw
|
335
363
|
):
|
336
364
|
self.response = dict()
|
365
|
+
self.obvious_fields = set()
|
366
|
+
if obvious_fields is not None:
|
367
|
+
self.obvious_fields |= obvious_fields
|
337
368
|
# if str(kw.get('actor', None)) == "cal.EntriesByController":
|
338
369
|
# if kw.get('actor', None):
|
370
|
+
|
371
|
+
self.known_values = dict()
|
372
|
+
|
339
373
|
if request is not None:
|
340
374
|
assert parent is None
|
341
375
|
self.request = request
|
@@ -356,9 +390,11 @@ class BaseRequest:
|
|
356
390
|
raise Exception("%s : %s is None" % (kw, k))
|
357
391
|
else:
|
358
392
|
kw[k] = getattr(parent, k)
|
359
|
-
kv = kw.setdefault("known_values", {})
|
360
|
-
if parent.actor is self.actor:
|
361
|
-
|
393
|
+
# kv = kw.setdefault("known_values", {})
|
394
|
+
if parent.actor is not None and parent.actor is self.actor:
|
395
|
+
kw['param_values'] = parent.param_values
|
396
|
+
if parent.known_values is not None:
|
397
|
+
self.known_values.update(parent.known_values)
|
362
398
|
# kw.setdefault('user', parent.user)
|
363
399
|
# kw.setdefault('subst_user', parent.subst_user)
|
364
400
|
# kw.setdefault('renderer', parent.renderer)
|
@@ -377,10 +413,27 @@ class BaseRequest:
|
|
377
413
|
self.is_on_main_actor = is_on_main_actor
|
378
414
|
self.permalink_uris = permalink_uris
|
379
415
|
|
416
|
+
if known_values is not None:
|
417
|
+
self.known_values.update(known_values)
|
418
|
+
|
380
419
|
self.setup(**kw)
|
381
420
|
|
382
421
|
# print("20241101", self.__class__.__name__, self.show_urls)
|
383
422
|
|
423
|
+
self.obvious_fields |= set(self.known_values.keys())
|
424
|
+
|
425
|
+
if self.actor is not None:
|
426
|
+
|
427
|
+
for k, v in self.actor.known_values.items():
|
428
|
+
self.known_values.setdefault(k, v)
|
429
|
+
|
430
|
+
self.obvious_fields |= self.actor.obvious_fields
|
431
|
+
# self.obvious_fields.update(obvious_fields.split())
|
432
|
+
# if parent.obvious_fields is not None:
|
433
|
+
if parent is not None and parent.actor is self.actor and parent.obvious_fields:
|
434
|
+
self.obvious_fields |= parent.obvious_fields
|
435
|
+
self.obvious_fields.add(self.actor.master_key)
|
436
|
+
|
384
437
|
if self.master is not None and settings.SITE.strict_master_check:
|
385
438
|
if self.master_instance is None:
|
386
439
|
raise exceptions.BadRequest(
|
@@ -409,12 +462,21 @@ class BaseRequest:
|
|
409
462
|
requesting_panel=None,
|
410
463
|
renderer=None,
|
411
464
|
xcallback_answers=None,
|
412
|
-
known_values={},
|
413
465
|
show_urls=None,
|
466
|
+
quick_search=None,
|
467
|
+
order_by=None,
|
468
|
+
filter=None,
|
469
|
+
gridfilters=None,
|
470
|
+
extra=None,
|
414
471
|
):
|
415
472
|
if logger is not None:
|
416
473
|
self.logger = logger
|
417
474
|
self.requesting_panel = requesting_panel
|
475
|
+
self.quick_search = quick_search
|
476
|
+
self.order_by = order_by
|
477
|
+
self.filter = filter
|
478
|
+
self.gridfilters = gridfilters
|
479
|
+
self.extra = extra
|
418
480
|
if user is None:
|
419
481
|
self.user = settings.SITE.get_anonymous_user()
|
420
482
|
else:
|
@@ -532,36 +594,11 @@ class BaseRequest:
|
|
532
594
|
master_key=rqdata.get(constants.URL_PARAM_MASTER_PK, None),
|
533
595
|
)
|
534
596
|
|
535
|
-
# if settings.SITE.use_filterRow:
|
536
|
-
# exclude = dict()
|
537
|
-
# for f in self.ah.store.fields:
|
538
|
-
# if f.field:
|
539
|
-
# filterOption = rqdata.get(
|
540
|
-
# 'filter[%s_filterOption]' % f.field.name)
|
541
|
-
# if filterOption == 'empty':
|
542
|
-
# kw[f.field.name + "__isnull"] = True
|
543
|
-
# elif filterOption == 'notempty':
|
544
|
-
# kw[f.field.name + "__isnull"] = False
|
545
|
-
# else:
|
546
|
-
# filterValue = rqdata.get('filter[%s]' % f.field.name)
|
547
|
-
# if filterValue:
|
548
|
-
# if not filterOption:
|
549
|
-
# filterOption = 'contains'
|
550
|
-
# if filterOption == 'contains':
|
551
|
-
# kw[f.field.name + "__icontains"] = filterValue
|
552
|
-
# elif filterOption == 'doesnotcontain':
|
553
|
-
# exclude[f.field.name +
|
554
|
-
# "__icontains"] = filterValue
|
555
|
-
# else:
|
556
|
-
# print("unknown filterOption %r" % filterOption)
|
557
|
-
# if len(exclude):
|
558
|
-
# kw.update(exclude=exclude)
|
559
|
-
|
560
597
|
if settings.SITE.use_gridfilters:
|
561
|
-
|
562
|
-
if
|
563
|
-
|
564
|
-
kw["gridfilters"] = [constants.dict2kw(flt) for flt in
|
598
|
+
v = rqdata.get(constants.URL_PARAM_GRIDFILTER, None)
|
599
|
+
if v is not None:
|
600
|
+
v = json.loads(v)
|
601
|
+
kw["gridfilters"] = [constants.dict2kw(flt) for flt in v]
|
565
602
|
|
566
603
|
# kw = ActionRequest.parse_req(self, request, rqdata, **kw)
|
567
604
|
if settings.SITE.user_model:
|
@@ -962,6 +999,8 @@ class BaseRequest:
|
|
962
999
|
)
|
963
1000
|
return
|
964
1001
|
|
1002
|
+
self.logger.info("Send email '%s' from %s to %s", subject, sender, recipients)
|
1003
|
+
|
965
1004
|
recipients = [a for a in recipients if "@example.com" not in a]
|
966
1005
|
if not len(recipients):
|
967
1006
|
self.logger.info(
|
@@ -970,8 +1009,6 @@ class BaseRequest:
|
|
970
1009
|
# self.logger.info("Email body would have been %s", body)
|
971
1010
|
return
|
972
1011
|
|
973
|
-
self.logger.info("Send email '%s' from %s to %s", subject, sender, recipients)
|
974
|
-
|
975
1012
|
kw = {}
|
976
1013
|
if body.startswith("<"):
|
977
1014
|
kw["html_message"] = body
|
@@ -1128,14 +1165,17 @@ class BaseRequest:
|
|
1128
1165
|
an obvious value.
|
1129
1166
|
|
1130
1167
|
"""
|
1131
|
-
|
1132
|
-
|
1133
|
-
# if self.param_values is not None and name in self.param_values:
|
1168
|
+
return name in self.obvious_fields
|
1169
|
+
# if name in self.known_values:
|
1134
1170
|
# return True
|
1135
|
-
if self.
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1171
|
+
# # if self.param_values is not None and name in self.param_values:
|
1172
|
+
# # return True
|
1173
|
+
# if self.actor is not None:
|
1174
|
+
# if self.actor.master_key == name:
|
1175
|
+
# return True
|
1176
|
+
# if name in self.actor.obvious_fields:
|
1177
|
+
# return True
|
1178
|
+
# return False
|
1139
1179
|
|
1140
1180
|
def get_user(self):
|
1141
1181
|
"""
|
@@ -1338,10 +1378,8 @@ class BaseRequest:
|
|
1338
1378
|
return self.renderer.obj2url(self, *args, **kwargs)
|
1339
1379
|
|
1340
1380
|
def obj2html(self, obj, text=None, **kwargs):
|
1341
|
-
|
1342
|
-
|
1343
|
-
given database object.
|
1344
|
-
"""
|
1381
|
+
# Return a clickable HTML element that points to a detail view of the
|
1382
|
+
# given database object.
|
1345
1383
|
if obj is None:
|
1346
1384
|
return ""
|
1347
1385
|
return self.renderer.obj2html(self, obj, text, **kwargs)
|
@@ -1666,11 +1704,8 @@ class ActionRequest(BaseRequest):
|
|
1666
1704
|
renderer = None
|
1667
1705
|
offset = None
|
1668
1706
|
limit = None
|
1669
|
-
order_by = None
|
1670
1707
|
master = None
|
1671
|
-
extra = None
|
1672
1708
|
title = None
|
1673
|
-
filter = None
|
1674
1709
|
limit = None
|
1675
1710
|
offset = None
|
1676
1711
|
|
@@ -1703,28 +1738,16 @@ class ActionRequest(BaseRequest):
|
|
1703
1738
|
|
1704
1739
|
def setup(
|
1705
1740
|
self,
|
1706
|
-
known_values=None,
|
1707
1741
|
param_values=None,
|
1708
1742
|
action_param_values={},
|
1709
|
-
quick_search=None,
|
1710
|
-
order_by=None,
|
1711
1743
|
offset=None,
|
1712
1744
|
limit=None,
|
1713
1745
|
title=None,
|
1714
|
-
filter=None,
|
1715
|
-
gridfilters=None,
|
1716
1746
|
exclude=None,
|
1717
1747
|
selected_pks=None,
|
1718
1748
|
selected_rows=None,
|
1719
|
-
extra=None,
|
1720
1749
|
**kw
|
1721
1750
|
):
|
1722
|
-
self.quick_search = quick_search
|
1723
|
-
self.order_by = order_by
|
1724
|
-
self.filter = filter
|
1725
|
-
self.gridfilters = gridfilters
|
1726
|
-
self.extra = extra
|
1727
|
-
|
1728
1751
|
if title is not None:
|
1729
1752
|
self.title = title
|
1730
1753
|
if offset is not None:
|
@@ -1743,12 +1766,7 @@ class ActionRequest(BaseRequest):
|
|
1743
1766
|
assert self.actor is not None
|
1744
1767
|
request = self.request
|
1745
1768
|
|
1746
|
-
|
1747
|
-
for k, v in self.actor.known_values.items():
|
1748
|
-
kv.setdefault(k, v)
|
1749
|
-
if known_values:
|
1750
|
-
kv.update(known_values)
|
1751
|
-
self.known_values = kv
|
1769
|
+
self.actor.setup_request(self)
|
1752
1770
|
|
1753
1771
|
if self.actor.parameters is not None:
|
1754
1772
|
pv = self.actor.param_defaults(self)
|
@@ -1826,8 +1844,6 @@ class ActionRequest(BaseRequest):
|
|
1826
1844
|
self.set_action_param_values(**action_param_values)
|
1827
1845
|
self.bound_action.setup_action_request(self)
|
1828
1846
|
|
1829
|
-
self.actor.setup_request(self)
|
1830
|
-
|
1831
1847
|
# if str(self.actor) == 'outbox.MyOutbox':
|
1832
1848
|
# if self.master_instance is None:
|
1833
1849
|
# raise Exception("20230426 b {}".format(self.master))
|
@@ -1899,6 +1915,8 @@ class ActionRequest(BaseRequest):
|
|
1899
1915
|
if target is None:
|
1900
1916
|
target = self.actor
|
1901
1917
|
if self._insert_sar is None:
|
1918
|
+
if target.insert_action is None:
|
1919
|
+
raise Exception(f"20241212 {target} has no insert_action")
|
1902
1920
|
self._insert_sar = target.insert_action.request_from(self)
|
1903
1921
|
else:
|
1904
1922
|
assert self._insert_sar.actor is target
|
@@ -2132,26 +2150,7 @@ class ActionRequest(BaseRequest):
|
|
2132
2150
|
for k, v in self.known_values.items():
|
2133
2151
|
if self.actor.known_values.get(k, None) != v:
|
2134
2152
|
bp[k] = v
|
2135
|
-
|
2136
|
-
if isinstance(self.master_instance, (models.Model, TableRow)):
|
2137
|
-
bp[constants.URL_PARAM_MASTER_PK] = self.master_instance.pk
|
2138
|
-
if ContentType is not None and isinstance(
|
2139
|
-
self.master_instance, models.Model
|
2140
|
-
):
|
2141
|
-
assert not self.master_instance._meta.abstract
|
2142
|
-
mt = ContentType.objects.get_for_model(
|
2143
|
-
self.master_instance.__class__
|
2144
|
-
).pk
|
2145
|
-
bp[constants.URL_PARAM_MASTER_TYPE] = mt
|
2146
|
-
# else:
|
2147
|
-
# logger.warning("20141205 %s %s",
|
2148
|
-
# self.master_instance,
|
2149
|
-
# ContentType)
|
2150
|
-
else: # if self.master is None:
|
2151
|
-
# e.g. in accounting.MovementsByMatch the master is a `str`
|
2152
|
-
bp[constants.URL_PARAM_MASTER_PK] = self.master_instance
|
2153
|
-
# raise Exception("No master key for {} {!r}".format(
|
2154
|
-
# self.master_instance.__class__, self.master_instance))
|
2153
|
+
mi2bp(self.master_instance, bp)
|
2155
2154
|
self._status = kw
|
2156
2155
|
return kw
|
2157
2156
|
|
@@ -2445,7 +2444,7 @@ class ActionRequest(BaseRequest):
|
|
2445
2444
|
oh = ar.actor.override_column_headers(ar)
|
2446
2445
|
if oh:
|
2447
2446
|
for i, e in enumerate(columns):
|
2448
|
-
header = oh.get(e
|
2447
|
+
header = oh.get(e, None)
|
2449
2448
|
if header is not None:
|
2450
2449
|
headers[i] = header
|
2451
2450
|
# ~ print 20120507, oh, headers
|
lino/core/site.py
CHANGED
@@ -18,11 +18,6 @@ from pprint import pprint
|
|
18
18
|
from logging.handlers import SocketHandler
|
19
19
|
import time
|
20
20
|
|
21
|
-
try:
|
22
|
-
from bleach.sanitizer import ALLOWED_ATTRIBUTES
|
23
|
-
except ImportError:
|
24
|
-
ALLOWED_ATTRIBUTES = dict()
|
25
|
-
|
26
21
|
NO_REMOTE_AUTH = True
|
27
22
|
# 20240518 We have only one production site still using remote http
|
28
23
|
# authentication, and they will migrate to sessions-based auth with their next
|
@@ -359,44 +354,6 @@ class Site(object):
|
|
359
354
|
|
360
355
|
detail_main_name = "main"
|
361
356
|
|
362
|
-
bleach_allowed_tags = [
|
363
|
-
"a",
|
364
|
-
"b",
|
365
|
-
"i",
|
366
|
-
"em",
|
367
|
-
"ul",
|
368
|
-
"ol",
|
369
|
-
"li",
|
370
|
-
"strong",
|
371
|
-
"p",
|
372
|
-
"br",
|
373
|
-
"span",
|
374
|
-
"pre",
|
375
|
-
"def",
|
376
|
-
"div",
|
377
|
-
"img",
|
378
|
-
"table",
|
379
|
-
"th",
|
380
|
-
"tr",
|
381
|
-
"td",
|
382
|
-
"thead",
|
383
|
-
"tfoot",
|
384
|
-
"tbody",
|
385
|
-
]
|
386
|
-
|
387
|
-
ALLOWED_ATTRIBUTES["span"] = [
|
388
|
-
"class",
|
389
|
-
"data-index",
|
390
|
-
"data-denotation-char",
|
391
|
-
"data-link",
|
392
|
-
"data-title",
|
393
|
-
"data-value",
|
394
|
-
"contenteditable",
|
395
|
-
]
|
396
|
-
ALLOWED_ATTRIBUTES["p"] = ["href", "title", "align"]
|
397
|
-
|
398
|
-
bleach_allowed_attributes = ALLOWED_ATTRIBUTES
|
399
|
-
|
400
357
|
textfield_bleached = True
|
401
358
|
textfield_format = "plain"
|
402
359
|
verbose_client_info_message = False
|
@@ -627,6 +584,8 @@ class Site(object):
|
|
627
584
|
for k in ("DATABASES", "SECRET_KEY"):
|
628
585
|
self.django_settings[k] = self.master_site.django_settings[k]
|
629
586
|
|
587
|
+
self.update_settings(
|
588
|
+
EMAIL_SUBJECT_PREFIX=f'[{self.project_name}] ')
|
630
589
|
self.update_settings(
|
631
590
|
SERIALIZATION_MODULES={
|
632
591
|
"py": "lino.utils.dpy",
|
@@ -896,7 +855,7 @@ class Site(object):
|
|
896
855
|
# the default_ui.
|
897
856
|
return
|
898
857
|
raise Exception(
|
899
|
-
"Tried to install {}
|
858
|
+
"Tried to install {}, but {} is already installed.".format(
|
900
859
|
app_name, other
|
901
860
|
)
|
902
861
|
)
|
@@ -1009,7 +968,7 @@ class Site(object):
|
|
1009
968
|
for r in p.get_requirements(self):
|
1010
969
|
reqs.add(r)
|
1011
970
|
if self.textfield_bleached:
|
1012
|
-
reqs.add("
|
971
|
+
reqs.add("beautifulsoup4")
|
1013
972
|
return sorted(reqs)
|
1014
973
|
|
1015
974
|
def setup_plugins(self):
|
@@ -2206,7 +2165,7 @@ class Site(object):
|
|
2206
2165
|
site_prefix = "/"
|
2207
2166
|
"""The string to prefix to every URL of the Lino web interface.
|
2208
2167
|
|
2209
|
-
This must *start and end with a *slash*. Default value is
|
2168
|
+
This must *start and end* with a *slash*. Default value is
|
2210
2169
|
``'/'``.
|
2211
2170
|
|
2212
2171
|
This must be set if your project is not being served at the "root"
|
lino/core/store.py
CHANGED
@@ -1227,10 +1227,10 @@ class Store(BaseStore):
|
|
1227
1227
|
dh = form.get_layout_handle()
|
1228
1228
|
self.collect_fields(self.card_fields, dh)
|
1229
1229
|
|
1230
|
-
form = rh.actor.list_layout
|
1231
|
-
if form:
|
1232
|
-
|
1233
|
-
|
1230
|
+
# form = rh.actor.list_layout
|
1231
|
+
# if form:
|
1232
|
+
# dh = form.get_layout_handle()
|
1233
|
+
# self.collect_fields(self.item_fields, dh)
|
1234
1234
|
|
1235
1235
|
if self.pk is not None:
|
1236
1236
|
self.pk_index = 0
|
lino/core/tables.py
CHANGED
@@ -521,22 +521,6 @@ method in order to sort the rows of the queryset.
|
|
521
521
|
return cls.detail_action
|
522
522
|
return actions.ShowTable()
|
523
523
|
|
524
|
-
@classmethod
|
525
|
-
def row_as_paragraph(cls, ar, row):
|
526
|
-
"""Return an HTML string that represents the given row as a single
|
527
|
-
paragraph.
|
528
|
-
|
529
|
-
See :ref:`dev.as_paragraph`.
|
530
|
-
"""
|
531
|
-
return row.as_paragraph(ar)
|
532
|
-
|
533
|
-
@classmethod
|
534
|
-
def row_as_page(cls, ar, row, **kwargs):
|
535
|
-
"""
|
536
|
-
Return an HTML string that represent the given row as a plain page.
|
537
|
-
"""
|
538
|
-
return row.as_page(ar, **kwargs)
|
539
|
-
|
540
524
|
@classmethod
|
541
525
|
def get_actor_editable(self):
|
542
526
|
if self._editable is None:
|
lino/core/utils.py
CHANGED
@@ -379,7 +379,7 @@ class UnresolvedModel(object):
|
|
379
379
|
# ~ print(self)
|
380
380
|
|
381
381
|
def __repr__(self):
|
382
|
-
return self.__class__.__name__ + "(%s
|
382
|
+
return self.__class__.__name__ + "(%r, %s)" % (self.model_spec, self.app_label)
|
383
383
|
|
384
384
|
# ~ def __getattr__(self,name):
|
385
385
|
# ~ raise AttributeError("%s has no attribute %r" % (self,name))
|
@@ -421,6 +421,9 @@ def resolve_model(model_spec, app_label=None, strict=False):
|
|
421
421
|
# settings.SITE.logger.info("20181230 resolve %s --> %r, %r",
|
422
422
|
# model_spec, app, model)
|
423
423
|
else:
|
424
|
+
# 20241112 Helped to explore #5797 (Could not resolve target
|
425
|
+
# 'uploads.UploadType' of ForeignKey 'type' in <class
|
426
|
+
# 'lino.modlib.uploads.models.Upload'>)
|
424
427
|
from django.apps import apps
|
425
428
|
|
426
429
|
try:
|
@@ -1113,7 +1116,7 @@ class PhantomRow(VirtualRow):
|
|
1113
1116
|
|
1114
1117
|
|
1115
1118
|
def login(username=None, **kwargs):
|
1116
|
-
"""Return a basic :term:`action request` with the specified user signed in.
|
1119
|
+
"""Return a basic :term:`action request` with the specified user signed in.
|
1117
1120
|
"""
|
1118
1121
|
from lino.core.requests import BaseRequest # avoid circular import
|
1119
1122
|
# settings.SITE.startup()
|
lino/help_texts.py
CHANGED
@@ -56,8 +56,8 @@ help_texts = {
|
|
56
56
|
'lino.mixins.periods.Started.save' : _("""Fills default value “today” to start_date"""),
|
57
57
|
'lino.mixins.periods.Ended' : _("""Mixin for models with two fields end_date and end_time."""),
|
58
58
|
'lino.mixins.periods.Ended.get_duration' : _("""Return the duration in hours."""),
|
59
|
-
'lino.mixins.periods.DateRange' : _("""
|
60
|
-
'lino.mixins.periods.ObservedDateRange' : _("""lino.core.param_panel.ParameterPanel with two fields start_date and end_date
|
59
|
+
'lino.mixins.periods.DateRange' : _("""A model mixin that adds two fields start_date and end_date. DateRangeObservable"""),
|
60
|
+
'lino.mixins.periods.ObservedDateRange' : _("""lino.core.param_panel.ParameterPanel with two fields start_date and end_date."""),
|
61
61
|
'lino.mixins.periods.Yearly' : _("""An ObservedDateRange for which start_date defaults to Jan 1st and end_date to Dec 31 of the current year."""),
|
62
62
|
'lino.mixins.periods.Monthly' : _("""An ObservedDateRange which defaults to the current month."""),
|
63
63
|
'lino.mixins.periods.Weekly' : _("""An ObservedDateRange which defaults to the current week."""),
|
@@ -174,6 +174,7 @@ help_texts = {
|
|
174
174
|
'lino.modlib.tinymce.Plugin.media_name' : _("""Lino currently includes three versions of TinyMCE, but for production sites we still use the eldest version 3.4.8."""),
|
175
175
|
'lino.modlib.uploads.Plugin' : _("""See /dev/plugins."""),
|
176
176
|
'lino.modlib.uploads.Plugin.remove_orphaned_files' : _("""Whether checkdata –fix should automatically delete orphaned files in the uploads folder."""),
|
177
|
+
'lino.modlib.uploads.Plugin.with_thumbnails' : _("""Whether to use PIL, the Python Imaging Library."""),
|
177
178
|
'lino.modlib.weasyprint.Plugin' : _("""See /dev/plugins."""),
|
178
179
|
'lino.modlib.weasyprint.Plugin.header_height' : _("""Height of header in mm. Set to None if you want no header."""),
|
179
180
|
'lino.modlib.weasyprint.Plugin.footer_height' : _("""Height of footer in mm. Set to None if you want no header."""),
|
@@ -322,8 +323,10 @@ help_texts = {
|
|
322
323
|
'lino.core.model.Model.allow_cascaded_delete' : _("""A set of names of ForeignKey or GenericForeignKey fields of this model that allow for cascaded delete."""),
|
323
324
|
'lino.core.model.Model.disabled_fields' : _("""Return a set of field names that should be disabled (i.e. not editable) for this database object."""),
|
324
325
|
'lino.core.model.Model.__str__' : _("""Return a translatable text that describes this database row."""),
|
325
|
-
'lino.core.model.Model.
|
326
|
+
'lino.core.model.Model.as_str' : _("""Return a translatable text that describes this database row. Unlike __str__() this method gets an action request when it is called, so it knows the context."""),
|
327
|
+
'lino.core.model.Model.get_str_words' : _("""Yield a series of words that describe this database row in plain text."""),
|
326
328
|
'lino.core.model.Model.as_summary_item' : _("""Return a HTML element that represents this database row in a data window in display mode “summary”."""),
|
329
|
+
'lino.core.model.Model.as_paragraph' : _("""Return a safe HTML string that represents this database row as a paragraph."""),
|
327
330
|
'lino.core.model.Model.set_widget_options' : _("""Set default values for the widget options of a given element."""),
|
328
331
|
'lino.core.model.Model.get_overview_elems' : _("""Return a list of HTML elements to be shown in overview field."""),
|
329
332
|
'lino.core.model.Model.merge_row' : _("""Merge this object into another object of same class."""),
|
@@ -376,7 +379,7 @@ help_texts = {
|
|
376
379
|
'lino.modlib.linod.SystemTask.log_level' : _("""The logging level to apply when running this task."""),
|
377
380
|
'lino.modlib.linod.SystemTask.run' : _("""Performs a routine job."""),
|
378
381
|
'lino.modlib.linod.SystemTasks' : _("""The default actor for the SystemTask model."""),
|
379
|
-
'lino.modlib.periods.StoredYear' : _("""The Django model used to store
|
382
|
+
'lino.modlib.periods.StoredYear' : _("""The Django model used to store a fiscal year."""),
|
380
383
|
'lino.modlib.periods.StoredPeriod' : _("""The Django model used to store an accounting period."""),
|
381
384
|
'lino.modlib.periods.StoredYears' : _("""The fiscal years defined in this database."""),
|
382
385
|
'lino.modlib.periods.StoredPeriods' : _("""The accounting periods defined in this database."""),
|
@@ -515,7 +518,6 @@ help_texts = {
|
|
515
518
|
'lino.modlib.comments.Commentable.add_comments_filter' : _("""Add filters to the given queryset of comments, requested by the given user."""),
|
516
519
|
'lino.modlib.comments.Commentable.get_rfc_description' : _("""Return a HTML formatted string with the description of this Commentable as it should be displayed by the slave summary of CommentsByOwner."""),
|
517
520
|
'lino.modlib.comments.Commentable.on_commented' : _("""This is automatically called when a comment has been created or modified."""),
|
518
|
-
'lino.modlib.comments.Commentable.get_comment_group' : _("""(Currently not used)"""),
|
519
521
|
'lino.modlib.files.Volume' : _("""The Django model representing a file volume."""),
|
520
522
|
'lino.modlib.files.Volume.id' : _("""The primary key used to point to this volume from a database object."""),
|
521
523
|
'lino.modlib.files.Volume.ref' : _("""The full path of the root folder."""),
|