lino 25.2.2__py3-none-any.whl → 25.3.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 +8 -3
- lino/api/dd.py +11 -35
- lino/api/doctest.py +49 -17
- lino/api/selenium.py +1 -1
- lino/core/actions.py +25 -23
- lino/core/actors.py +52 -23
- lino/core/choicelists.py +10 -8
- lino/core/dbtables.py +1 -1
- lino/core/elems.py +47 -31
- lino/core/fields.py +19 -9
- lino/core/kernel.py +26 -20
- lino/core/model.py +27 -16
- lino/core/renderer.py +2 -2
- lino/core/requests.py +103 -56
- lino/core/site.py +5 -5
- lino/core/store.py +5 -2
- lino/core/utils.py +12 -7
- lino/help_texts.py +7 -8
- lino/mixins/duplicable.py +6 -4
- lino/mixins/sequenced.py +17 -6
- lino/modlib/__init__.py +0 -2
- lino/modlib/changes/models.py +21 -10
- lino/modlib/checkdata/models.py +59 -24
- lino/modlib/comments/fixtures/demo2.py +12 -3
- lino/modlib/comments/models.py +7 -7
- lino/modlib/comments/ui.py +8 -5
- lino/modlib/export_excel/models.py +7 -5
- lino/modlib/extjs/__init__.py +2 -2
- lino/modlib/extjs/views.py +66 -22
- lino/modlib/help/config/makehelp/conf.tpl.py +1 -1
- lino/modlib/jinja/mixins.py +73 -0
- lino/modlib/jinja/models.py +6 -0
- lino/modlib/linod/__init__.py +1 -0
- lino/modlib/linod/choicelists.py +21 -0
- lino/modlib/linod/consumers.py +13 -4
- lino/modlib/linod/fixtures/__init__.py +0 -0
- lino/modlib/linod/fixtures/linod.py +32 -0
- lino/modlib/linod/management/commands/linod.py +6 -2
- lino/modlib/linod/mixins.py +18 -14
- lino/modlib/linod/models.py +4 -2
- lino/modlib/memo/mixins.py +2 -1
- lino/modlib/memo/parser.py +1 -1
- lino/modlib/notify/models.py +19 -11
- lino/modlib/printing/actions.py +47 -42
- lino/modlib/printing/choicelists.py +17 -15
- lino/modlib/printing/mixins.py +22 -20
- lino/modlib/publisher/models.py +5 -5
- lino/modlib/summaries/models.py +3 -2
- lino/modlib/system/models.py +28 -29
- lino/modlib/uploads/__init__.py +14 -11
- lino/modlib/uploads/actions.py +2 -8
- lino/modlib/uploads/choicelists.py +10 -10
- lino/modlib/uploads/fixtures/std.py +17 -0
- lino/modlib/uploads/mixins.py +20 -8
- lino/modlib/uploads/models.py +62 -38
- lino/modlib/uploads/ui.py +15 -9
- lino/utils/__init__.py +0 -1
- lino/utils/jscompressor.py +4 -4
- lino/utils/media.py +45 -23
- lino/utils/report.py +5 -4
- lino/utils/restify.py +2 -2
- lino/utils/soup.py +26 -8
- lino/utils/xml.py +19 -5
- {lino-25.2.2.dist-info → lino-25.3.0.dist-info}/METADATA +1 -1
- {lino-25.2.2.dist-info → lino-25.3.0.dist-info}/RECORD +68 -65
- lino/mixins/uploadable.py +0 -3
- lino/utils/requests.py +0 -55
- {lino-25.2.2.dist-info → lino-25.3.0.dist-info}/WHEEL +0 -0
- {lino-25.2.2.dist-info → lino-25.3.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.2.2.dist-info → lino-25.3.0.dist-info}/licenses/COPYING +0 -0
lino/modlib/printing/actions.py
CHANGED
@@ -1,5 +1,5 @@
|
|
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
4
|
|
5
5
|
from lino import logger
|
@@ -24,10 +24,6 @@ from lino.utils.xml import validate_xml
|
|
24
24
|
|
25
25
|
from .choicelists import BuildMethods
|
26
26
|
|
27
|
-
# davlink = settings.SITE.plugins.get('davlink', None)
|
28
|
-
# has_davlink = davlink is not None and settings.SITE.use_java
|
29
|
-
has_davlink = False
|
30
|
-
|
31
27
|
|
32
28
|
class BasePrintAction(Action):
|
33
29
|
sort_index = 50
|
@@ -37,7 +33,7 @@ class BasePrintAction(Action):
|
|
37
33
|
hotkey = Hotkey("p", code="KeyP", ctrl=True)
|
38
34
|
|
39
35
|
def __init__(self, build_method=None, label=None, **kwargs):
|
40
|
-
super(
|
36
|
+
super().__init__(label, **kwargs)
|
41
37
|
if build_method is not None:
|
42
38
|
self.build_method = build_method
|
43
39
|
|
@@ -50,7 +46,7 @@ class BasePrintAction(Action):
|
|
50
46
|
return False
|
51
47
|
# if actor.__name__ == 'ExcerptsByProject':
|
52
48
|
# logger.info("20140401 attach_to_actor() %r", self)
|
53
|
-
return super(
|
49
|
+
return super().attach_to_actor(actor, name)
|
54
50
|
|
55
51
|
def get_print_templates(self, bm, elem):
|
56
52
|
# print("20190506 BasePrintAction.get_print_templates", elem)
|
@@ -69,17 +65,19 @@ class BasePrintAction(Action):
|
|
69
65
|
"""Return the target filename if a document needs to be built,
|
70
66
|
otherwise return ``None``.
|
71
67
|
"""
|
72
|
-
elem.before_printable_build(bm)
|
73
|
-
|
74
|
-
filename = bm.get_target_name(self, elem)
|
75
|
-
if not filename:
|
68
|
+
# elem.before_printable_build(bm)
|
69
|
+
if not elem.must_build_printable(bm):
|
76
70
|
return
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
71
|
+
# raise Exception("20170519 before_build didn't warn")
|
72
|
+
filename = bm.get_target_file(self, elem).path
|
73
|
+
filename.unlink(missing_ok=True)
|
74
|
+
filename.parent.mkdir(exist_ok=True, parents=True)
|
75
|
+
# if filename.exists():
|
76
|
+
# logger.debug("%s %s -> overwrite existing %s.", bm, elem, filename)
|
77
|
+
# os.remove(filename)
|
78
|
+
# else:
|
79
|
+
# # logger.info("20121221 makedirs_if_missing %s",os.path.dirname(filename))
|
80
|
+
# rt.makedirs_if_missing(os.path.dirname(filename))
|
83
81
|
logger.debug("%s : %s -> %s", bm, elem, filename)
|
84
82
|
return filename
|
85
83
|
|
@@ -94,14 +92,12 @@ class BasePrintAction(Action):
|
|
94
92
|
"If it doesn't, please "
|
95
93
|
"ask your system administrator."
|
96
94
|
)
|
97
|
-
msg = msg.format(etree.tostring(
|
95
|
+
msg = msg.format(etree.tostring(
|
96
|
+
E.a(leaf, href=url), encoding="unicode"))
|
98
97
|
# msg %= dict(doc=leaf, help=etree.tostring(
|
99
98
|
# help_url, encoding="unicode"))
|
100
99
|
kw.update(message=msg, alert=True)
|
101
|
-
|
102
|
-
kw.update(open_webdav_url=ar.request.build_absolute_uri(url))
|
103
|
-
else:
|
104
|
-
kw.update(open_url=url)
|
100
|
+
kw.update(open_url=url)
|
105
101
|
ar.success(**kw)
|
106
102
|
return
|
107
103
|
|
@@ -111,9 +107,10 @@ class BasePrintAction(Action):
|
|
111
107
|
if isinstance(bm, str):
|
112
108
|
bm = BuildMethods.get_by_value(bm)
|
113
109
|
bm.build(ar, self, elem)
|
114
|
-
mf = bm.
|
115
|
-
leaf = mf.parts[-1]
|
116
|
-
|
110
|
+
mf = bm.get_target_file(self, elem)
|
111
|
+
# leaf = mf.parts[-1]
|
112
|
+
leaf = mf.path.name
|
113
|
+
self.notify_done(ar, bm, leaf, mf.url, **kw)
|
117
114
|
|
118
115
|
|
119
116
|
class DirectPrintAction(BasePrintAction):
|
@@ -137,7 +134,7 @@ class DirectPrintAction(BasePrintAction):
|
|
137
134
|
class WriteXmlAction(DirectPrintAction):
|
138
135
|
"""Generate an XML file from this database object."""
|
139
136
|
|
140
|
-
combo_group = "writexml"
|
137
|
+
# combo_group = "writexml"
|
141
138
|
label = _("XML")
|
142
139
|
|
143
140
|
build_method = "xml"
|
@@ -148,6 +145,7 @@ class WriteXmlAction(DirectPrintAction):
|
|
148
145
|
def validate_result_file(self, bm, xmlfile):
|
149
146
|
if self.xsd_file:
|
150
147
|
logger.info("Validate %s against %s ...", xmlfile, self.xsd_file)
|
148
|
+
# doc = etree.parse(xmlfile)
|
151
149
|
if True:
|
152
150
|
validate_xml(xmlfile, self.xsd_file)
|
153
151
|
else:
|
@@ -174,23 +172,25 @@ class CachedPrintAction(BasePrintAction):
|
|
174
172
|
if len(ar.selected_rows) == 1:
|
175
173
|
obj = ar.selected_rows[0]
|
176
174
|
bm = obj.get_build_method()
|
177
|
-
mf = bm.
|
178
|
-
leaf = mf.parts[-1]
|
175
|
+
mf = bm.get_target_file(self, obj)
|
176
|
+
# leaf = mf.parts[-1]
|
177
|
+
leaf = mf.path.name
|
179
178
|
if obj.build_time is None:
|
180
179
|
obj.build_target(ar)
|
181
180
|
ar.debug("%s has been built.", leaf)
|
182
181
|
else:
|
183
182
|
ar.debug("Reused %s from cache.", leaf)
|
184
183
|
|
185
|
-
url = mf.get_url(ar.request)
|
186
|
-
self.notify_done(ar, bm, leaf, url, **kw)
|
184
|
+
# url = mf.get_url(ar.request)
|
185
|
+
self.notify_done(ar, bm, leaf, mf.url, **kw)
|
187
186
|
ar.set_response(refresh=True)
|
188
187
|
return
|
189
188
|
|
190
189
|
def ok(ar2):
|
191
190
|
# qs = [ar.actor.get_row_by_pk(pk) for pk in ar.selected_pks]
|
192
191
|
mf = self.print_multiple(ar, ar.selected_rows)
|
193
|
-
ar2.success(open_url=mf.
|
192
|
+
ar2.success(open_url=mf.url)
|
193
|
+
# ar2.success(open_url=mf.get_url(ar.request))
|
194
194
|
# kw.update(refresh_all=True)
|
195
195
|
# return kw
|
196
196
|
|
@@ -203,13 +203,16 @@ class CachedPrintAction(BasePrintAction):
|
|
203
203
|
# assert isinstance(obj,CachedPrintable)
|
204
204
|
if obj.printed_by_id is None:
|
205
205
|
obj.build_target(ar)
|
206
|
-
pdf = obj.
|
207
|
-
assert pdf is not None
|
208
|
-
pdfs.append(pdf)
|
206
|
+
# pdf = obj.get_target_file().name
|
207
|
+
# assert pdf is not None
|
208
|
+
# pdfs.append(pdf)
|
209
|
+
mf = obj.get_target_file()
|
210
|
+
pdfs.append(mf.path)
|
209
211
|
|
210
212
|
mf = TmpMediaFile(ar, "pdf")
|
211
|
-
rt.makedirs_if_missing(os.path.dirname(mf.name))
|
212
|
-
|
213
|
+
# rt.makedirs_if_missing(os.path.dirname(mf.name))
|
214
|
+
rt.makedirs_if_missing(mf.path.parent)
|
215
|
+
merge_pdfs(pdfs, mf.path)
|
213
216
|
return mf
|
214
217
|
|
215
218
|
|
@@ -221,9 +224,9 @@ class EditTemplate(BasePrintAction):
|
|
221
224
|
|
222
225
|
def attach_to_actor(self, actor, name):
|
223
226
|
# if not settings.SITE.is_installed('davlink'):
|
224
|
-
if not
|
227
|
+
if not settings.SITE.webdav_protocol:
|
225
228
|
return False
|
226
|
-
return super(
|
229
|
+
return super().attach_to_actor(actor, name)
|
227
230
|
|
228
231
|
def run_from_ui(self, ar, **kw):
|
229
232
|
lcd = settings.SITE.confdirs.LOCAL_CONFIG_DIR
|
@@ -253,7 +256,7 @@ class EditTemplate(BasePrintAction):
|
|
253
256
|
url = settings.SITE.build_media_url(*parts)
|
254
257
|
# url = ar.build_webdav_uri(url)
|
255
258
|
|
256
|
-
if not
|
259
|
+
if not settings.SITE.webdav_protocol:
|
257
260
|
msg = "cp %s %s" % (filename, local_file)
|
258
261
|
ar.debug(msg)
|
259
262
|
raise Warning(
|
@@ -275,7 +278,8 @@ class EditTemplate(BasePrintAction):
|
|
275
278
|
ar.debug("Gonna copy %s to %s", filename, local_file)
|
276
279
|
|
277
280
|
def ok(ar2):
|
278
|
-
logger.info("%s made local template copy %s",
|
281
|
+
logger.info("%s made local template copy %s",
|
282
|
+
ar.user, local_file)
|
279
283
|
rt.makedirs_if_missing(os.path.dirname(local_file))
|
280
284
|
shutil.copyfile(filename, local_file)
|
281
285
|
# shutil.copy() can cause PermissionError if dst exists and is
|
@@ -305,14 +309,15 @@ class ClearCacheAction(Action):
|
|
305
309
|
# should be visible in the UI
|
306
310
|
if obj is not None and not obj.build_time:
|
307
311
|
return False
|
308
|
-
return super(
|
312
|
+
return super().get_action_permission(ar, obj, state)
|
309
313
|
|
310
314
|
def run_from_ui(self, ar):
|
311
315
|
elem = ar.selected_rows[0]
|
312
316
|
|
313
317
|
def doit(ar):
|
314
318
|
elem.clear_cache()
|
315
|
-
ar.success(_("%s printable cache has been cleared.") %
|
319
|
+
ar.success(_("%s printable cache has been cleared.") %
|
320
|
+
elem, refresh=True)
|
316
321
|
|
317
322
|
t = elem.get_cache_mtime()
|
318
323
|
if t is not None:
|
@@ -1,16 +1,15 @@
|
|
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
4
|
"""
|
5
5
|
Choicelists for `lino.modlib.printing`.
|
6
6
|
|
7
7
|
"""
|
8
8
|
|
9
|
-
from lino import logger
|
10
|
-
|
11
9
|
import os
|
12
10
|
import io
|
13
11
|
from copy import copy
|
12
|
+
from pathlib import Path
|
14
13
|
|
15
14
|
from django.conf import settings
|
16
15
|
from django.utils import translation
|
@@ -25,6 +24,7 @@ from django.template.loader import select_template
|
|
25
24
|
from lino.core.choicelists import ChoiceList, Choice
|
26
25
|
from lino.utils.media import MediaFile
|
27
26
|
from lino.api import rt, _
|
27
|
+
from lino import logger
|
28
28
|
|
29
29
|
try:
|
30
30
|
import pyratemp
|
@@ -42,25 +42,23 @@ class BuildMethod(Choice):
|
|
42
42
|
# same.
|
43
43
|
if names is None:
|
44
44
|
names = self.name
|
45
|
-
super(
|
46
|
-
names, self.__class__.__name__, names, **kwargs
|
47
|
-
)
|
45
|
+
super().__init__(names, self.__class__.__name__, names, **kwargs)
|
48
46
|
|
49
|
-
def
|
47
|
+
def get_target_file(self, action, obj):
|
50
48
|
# Used by get_target_name()
|
51
49
|
# assert self.name is not None
|
52
50
|
return MediaFile(
|
53
51
|
self.use_webdav,
|
54
52
|
self.cache_name,
|
55
53
|
self.value,
|
56
|
-
obj.
|
54
|
+
obj.get_printable_target_stem() + self.target_ext,
|
57
55
|
)
|
58
56
|
|
59
|
-
def get_target_name(self, action, elem):
|
60
|
-
|
57
|
+
# def get_target_name(self, action, elem):
|
58
|
+
# return self.get_target_file(action, elem).name
|
61
59
|
|
62
60
|
def get_target_url(self, action, elem):
|
63
|
-
return self.
|
61
|
+
return self.get_target_file(action, elem).url
|
64
62
|
|
65
63
|
def build(self, ar, action, elem):
|
66
64
|
raise NotImplementedError
|
@@ -72,7 +70,7 @@ class TemplatedBuildMethod(BuildMethod):
|
|
72
70
|
default_template = "" # overridden by lino_xl.lib.appypod
|
73
71
|
|
74
72
|
def __init__(self, *args, **kwargs):
|
75
|
-
super(
|
73
|
+
super().__init__(*args, **kwargs)
|
76
74
|
if self.templates_name is None:
|
77
75
|
assert len(self.names) == 1
|
78
76
|
self.templates_name = self.names[0]
|
@@ -110,7 +108,8 @@ class DjangoBuildMethod(TemplatedBuildMethod):
|
|
110
108
|
except TemplateDoesNotExist as e:
|
111
109
|
raise Warning("No template found for %s (%s)" % (e, tpls2))
|
112
110
|
except Exception as e:
|
113
|
-
raise Exception(
|
111
|
+
raise Exception(
|
112
|
+
"Error while loading template for %s : %s" % (tpls2, e))
|
114
113
|
|
115
114
|
# ,MEDIA_URL=settings.MEDIA_URL):
|
116
115
|
def render_template(self, elem, tpl, **context):
|
@@ -172,7 +171,8 @@ class SimpleBuildMethod(TemplatedBuildMethod):
|
|
172
171
|
|
173
172
|
lang = elem.get_print_language() or translation.get_language()
|
174
173
|
if lang != settings.SITE.DEFAULT_LANGUAGE.django_code:
|
175
|
-
name = tpl_leaf[: -len(self.template_ext)] +
|
174
|
+
name = tpl_leaf[: -len(self.template_ext)] + \
|
175
|
+
"_" + lang + self.template_ext
|
176
176
|
if rt.find_config_file(name, *elem.get_template_groups()):
|
177
177
|
return name
|
178
178
|
return tpl_leaf
|
@@ -234,6 +234,7 @@ class XmlBuildMethod(DjangoBuildMethod):
|
|
234
234
|
filename = action.before_build(self, elem)
|
235
235
|
if filename is None:
|
236
236
|
return
|
237
|
+
filename = Path(filename)
|
237
238
|
tpl = self.get_template(action, elem)
|
238
239
|
|
239
240
|
lang = str(elem.get_print_language() or translation.get_language())
|
@@ -288,7 +289,8 @@ class BuildMethods(ChoiceList):
|
|
288
289
|
if bm is None:
|
289
290
|
raise Exception(
|
290
291
|
"Invalid default_build_method '{}', choices are {}".format(
|
291
|
-
settings.SITE.default_build_method, tuple(
|
292
|
+
settings.SITE.default_build_method, tuple(
|
293
|
+
cls.get_list_items())
|
292
294
|
)
|
293
295
|
)
|
294
296
|
return bm
|
lino/modlib/printing/mixins.py
CHANGED
@@ -1,5 +1,5 @@
|
|
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
4
|
|
5
5
|
import os
|
@@ -137,14 +137,15 @@ class Printable(Model):
|
|
137
137
|
kw = ar.get_printable_context(**kw)
|
138
138
|
kw.update(this=self) # for backward compatibility
|
139
139
|
kw.update(obj=self) # preferred in new templates
|
140
|
-
kw.update(language=self.get_print_language()
|
140
|
+
kw.update(language=self.get_print_language()
|
141
|
+
or translation.get_language())
|
141
142
|
# settings.SITE.DEFAULT_LANGUAGE.django_code)
|
142
143
|
kw.update(site=settings.SITE)
|
143
144
|
kw.update(weekdays=weekdays)
|
144
145
|
return kw
|
145
146
|
|
146
|
-
def
|
147
|
-
|
147
|
+
def must_build_printable(self, bm):
|
148
|
+
return True
|
148
149
|
|
149
150
|
|
150
151
|
class CachedPrintable(Duplicable, Printable):
|
@@ -155,23 +156,26 @@ class CachedPrintable(Duplicable, Printable):
|
|
155
156
|
do_clear_cache = ClearCacheAction()
|
156
157
|
edit_template = EditTemplate()
|
157
158
|
|
158
|
-
build_time = models.DateTimeField(
|
159
|
-
|
159
|
+
build_time = models.DateTimeField(
|
160
|
+
_("build time"), null=True, editable=False)
|
160
161
|
build_method = BuildMethods.field(blank=True, null=True)
|
161
162
|
|
162
163
|
def full_clean(self, *args, **kwargs):
|
163
164
|
if not self.build_method:
|
164
165
|
self.build_method = self.get_default_build_method()
|
165
|
-
super(
|
166
|
+
super().full_clean(*args, **kwargs)
|
166
167
|
|
167
168
|
def on_duplicate(self, ar, master):
|
168
|
-
super(
|
169
|
+
super().on_duplicate(ar, master)
|
169
170
|
self.build_time = None
|
170
171
|
self.build_method = None
|
171
172
|
|
172
|
-
def
|
173
|
-
|
174
|
-
|
173
|
+
def must_build_printable(self, bm):
|
174
|
+
return self.build_time is None
|
175
|
+
|
176
|
+
# def get_target_name(self):
|
177
|
+
# if self.build_time:
|
178
|
+
# return self.get_build_method().get_target_name(self.do_print, self)
|
175
179
|
|
176
180
|
def get_build_method(self):
|
177
181
|
return self.build_method or self.get_default_build_method()
|
@@ -184,14 +188,12 @@ class CachedPrintable(Duplicable, Printable):
|
|
184
188
|
file, or `None` if no such file exists.
|
185
189
|
|
186
190
|
"""
|
187
|
-
filename = self.
|
191
|
+
filename = self.get_target_file().path
|
188
192
|
if not filename:
|
189
193
|
return None
|
190
|
-
|
191
|
-
t = os.path.getmtime(filename)
|
192
|
-
except OSError:
|
194
|
+
if not filename.exists():
|
193
195
|
return None
|
194
|
-
return datetime.datetime.fromtimestamp(
|
196
|
+
return datetime.datetime.fromtimestamp(filename.lstat().st_mtime)
|
195
197
|
|
196
198
|
def clear_cache(self):
|
197
199
|
self.build_time = None
|
@@ -223,14 +225,14 @@ class TypedPrintable(CachedPrintable):
|
|
223
225
|
def get_template_groups(self):
|
224
226
|
ptype = self.get_printable_type()
|
225
227
|
if ptype is None:
|
226
|
-
return super(
|
228
|
+
return super().get_template_groups()
|
227
229
|
return ptype.get_template_groups()
|
228
230
|
|
229
231
|
def get_default_build_method(self):
|
230
232
|
ptype = self.get_printable_type()
|
231
233
|
if ptype and ptype.build_method:
|
232
234
|
return ptype.build_method
|
233
|
-
return super(
|
235
|
+
return super().get_default_build_method()
|
234
236
|
|
235
237
|
# def get_build_method(self):
|
236
238
|
# if not self.build_method:
|
@@ -239,12 +241,12 @@ class TypedPrintable(CachedPrintable):
|
|
239
241
|
# ptype = self.get_printable_type()
|
240
242
|
# if ptype and ptype.build_method:
|
241
243
|
# return ptype.build_method
|
242
|
-
# return super(
|
244
|
+
# return super().get_build_method()
|
243
245
|
|
244
246
|
def get_print_templates(self, bm, action):
|
245
247
|
ptype = self.get_printable_type()
|
246
248
|
if ptype is None:
|
247
|
-
return super(
|
249
|
+
return super().get_print_templates(bm, action)
|
248
250
|
|
249
251
|
if ptype.template:
|
250
252
|
return [ptype.template]
|
lino/modlib/publisher/models.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
# Copyright 2012-2024 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
|
+
from .ui import *
|
5
6
|
from lino.api import rt, dd
|
6
7
|
|
7
8
|
from .choicelists import PublishingStates, PageFillers, SpecialPages
|
@@ -31,6 +32,7 @@ from lino.mixins import Hierarchical, Sequenced, Referrable
|
|
31
32
|
from lino.modlib.office.roles import OfficeUser
|
32
33
|
from lino.modlib.publisher.mixins import Publishable, PublishableContent
|
33
34
|
from lino.modlib.comments.mixins import Commentable
|
35
|
+
from lino.modlib.linod.choicelists import schedule_daily
|
34
36
|
from lino.modlib.memo.mixins import Previewable
|
35
37
|
from lino.mixins.polymorphic import Polymorphic
|
36
38
|
from lino_xl.lib.topics.mixins import Taggable
|
@@ -50,7 +52,8 @@ class Page(
|
|
50
52
|
|
51
53
|
memo_command = "page"
|
52
54
|
|
53
|
-
ref = models.CharField(
|
55
|
+
ref = models.CharField(
|
56
|
+
_("Reference"), max_length=200, blank=True, null=True)
|
54
57
|
|
55
58
|
title = dd.CharField(_("Title"), max_length=250, blank=True)
|
56
59
|
child_node_depth = models.IntegerField(default=1)
|
@@ -382,7 +385,7 @@ if dd.plugins.memo.use_markup:
|
|
382
385
|
# language = dd.LanguageField()
|
383
386
|
|
384
387
|
|
385
|
-
@
|
388
|
+
@schedule_daily()
|
386
389
|
def update_publisher_pages(ar):
|
387
390
|
# BaseRequest(parent=ar).run(settings.SITE.site_config.check_all_summaries)
|
388
391
|
# rt.login().run(settings.SITE.site_config.check_all_summaries)
|
@@ -398,6 +401,3 @@ def update_publisher_pages(ar):
|
|
398
401
|
prev = obj
|
399
402
|
count += 1
|
400
403
|
ar.logger.info("%d pages have been updated.", count)
|
401
|
-
|
402
|
-
|
403
|
-
from .ui import *
|
lino/modlib/summaries/models.py
CHANGED
@@ -5,7 +5,8 @@ from django.conf import settings
|
|
5
5
|
|
6
6
|
# from django.db import models
|
7
7
|
from lino.api import dd, rt, _
|
8
|
-
from lino.core.requests import BaseRequest
|
8
|
+
# from lino.core.requests import BaseRequest
|
9
|
+
from lino.modlib.linod.choicelists import schedule_daily
|
9
10
|
|
10
11
|
from .mixins import UpdateSummariesByMaster, SlaveSummarized, Summarized
|
11
12
|
|
@@ -54,7 +55,7 @@ def masters_with_summaries():
|
|
54
55
|
return summary_masters
|
55
56
|
|
56
57
|
|
57
|
-
@
|
58
|
+
@schedule_daily()
|
58
59
|
def checksummaries(ar):
|
59
60
|
# BaseRequest(parent=ar).run(settings.SITE.site_config.check_all_summaries)
|
60
61
|
# rt.login().run(settings.SITE.site_config.check_all_summaries)
|
lino/modlib/system/models.py
CHANGED
@@ -2,6 +2,27 @@
|
|
2
2
|
# Copyright 2009-2023 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
|
+
from lino.utils.report import EmptyTable
|
6
|
+
from lino.core.signals import testcase_setup
|
7
|
+
from django.test.signals import setting_changed
|
8
|
+
from .mixins import Lockable
|
9
|
+
from .choicelists import (
|
10
|
+
YesNo,
|
11
|
+
Genders,
|
12
|
+
PeriodEvents,
|
13
|
+
DurationUnits,
|
14
|
+
Recurrences,
|
15
|
+
Weekdays,
|
16
|
+
DisplayColors
|
17
|
+
)
|
18
|
+
from lino.modlib.checkdata.choicelists import Checker
|
19
|
+
from lino.modlib.printing.choicelists import BuildMethods
|
20
|
+
from lino.core.actors import resolve_action
|
21
|
+
from lino.core.roles import SiteStaff
|
22
|
+
from lino.core.utils import is_devserver
|
23
|
+
from lino.core.utils import full_model_name
|
24
|
+
from lino.core import actions
|
25
|
+
from lino.api import dd, rt
|
5
26
|
from lino.utils.html import E
|
6
27
|
from django.conf import settings
|
7
28
|
from django.utils.encoding import force_str
|
@@ -14,27 +35,8 @@ from django.apps import apps
|
|
14
35
|
|
15
36
|
get_models = apps.get_models
|
16
37
|
|
17
|
-
from lino.api import dd, rt
|
18
|
-
from lino.core import actions
|
19
|
-
from lino.core.utils import full_model_name
|
20
|
-
from lino.core.utils import is_devserver
|
21
|
-
from lino.core.roles import SiteStaff
|
22
|
-
from lino.core.actors import resolve_action
|
23
|
-
|
24
|
-
from lino.modlib.printing.choicelists import BuildMethods
|
25
|
-
from lino.modlib.checkdata.choicelists import Checker
|
26
38
|
|
27
39
|
# import them here to have them on rt.models.system:
|
28
|
-
from .choicelists import (
|
29
|
-
YesNo,
|
30
|
-
Genders,
|
31
|
-
PeriodEvents,
|
32
|
-
DurationUnits,
|
33
|
-
Recurrences,
|
34
|
-
Weekdays,
|
35
|
-
DisplayColors
|
36
|
-
)
|
37
|
-
from .mixins import Lockable
|
38
40
|
|
39
41
|
|
40
42
|
class BuildSiteCache(dd.Action):
|
@@ -89,7 +91,8 @@ class SiteConfig(dd.Model):
|
|
89
91
|
verbose_name=_("Default build method"), blank=True, null=True
|
90
92
|
)
|
91
93
|
|
92
|
-
simulate_today = models.DateField(
|
94
|
+
simulate_today = models.DateField(
|
95
|
+
_("Simulated date"), blank=True, null=True)
|
93
96
|
|
94
97
|
site_company = dd.ForeignKey(
|
95
98
|
"contacts.Company",
|
@@ -190,10 +193,12 @@ class SiteConfig(dd.Model):
|
|
190
193
|
oldval = getattr(cls._site_config, fld.attname)
|
191
194
|
newval = getattr(self, fld.attname)
|
192
195
|
if oldval != newval:
|
193
|
-
diffs.append(
|
196
|
+
diffs.append(
|
197
|
+
"{}: {} -> {}".format(fld.attname, oldval, newval))
|
194
198
|
if len(diffs):
|
195
199
|
print(
|
196
|
-
"20220824 Overriding SiteConfig instance (diffs={})".format(
|
200
|
+
"20220824 Overriding SiteConfig instance (diffs={})".format(
|
201
|
+
diffs)
|
197
202
|
)
|
198
203
|
cls._site_config = self
|
199
204
|
# cls._site_config.update_from(self)
|
@@ -212,9 +217,6 @@ def my_handler(sender, **kw):
|
|
212
217
|
# ~ dd.database_connected.send(sender,**kw)
|
213
218
|
|
214
219
|
|
215
|
-
from django.test.signals import setting_changed
|
216
|
-
from lino.core.signals import testcase_setup
|
217
|
-
|
218
220
|
setting_changed.connect(my_handler)
|
219
221
|
testcase_setup.connect(my_handler)
|
220
222
|
dd.connection_created.connect(my_handler)
|
@@ -244,9 +246,6 @@ class SiteConfigs(dd.Table):
|
|
244
246
|
do_build = BuildSiteCache()
|
245
247
|
|
246
248
|
|
247
|
-
from lino.utils.report import EmptyTable
|
248
|
-
|
249
|
-
|
250
249
|
class Dashboard(EmptyTable):
|
251
250
|
# label = _("D")
|
252
251
|
hide_navigator = True
|
@@ -328,7 +327,7 @@ class BleachChecker(Checker):
|
|
328
327
|
yield m
|
329
328
|
|
330
329
|
def get_checkdata_problems(self, obj, fix=False):
|
331
|
-
t = tuple(obj.fields_to_bleach())
|
330
|
+
t = tuple(obj.fields_to_bleach(save=False))
|
332
331
|
if len(t):
|
333
332
|
fldnames = ", ".join([f.name for f, old, new in t])
|
334
333
|
yield (True, _("Fields {} have unbleached content.").format(fldnames))
|
lino/modlib/uploads/__init__.py
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
# Copyright 2010-2024 Rumma & Ko Ltd
|
2
2
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
3
|
-
"""See :doc:`/specs/uploads`.
|
4
3
|
|
5
|
-
|
6
|
-
"""
|
7
4
|
from os import symlink
|
8
5
|
from os.path import join
|
9
6
|
from lino import ad, _
|
@@ -12,6 +9,7 @@ from lino.modlib.memo.parser import split_name_rest
|
|
12
9
|
UPLOADS_ROOT = 'uploads'
|
13
10
|
VOLUMES_ROOT = 'volumes'
|
14
11
|
|
12
|
+
|
15
13
|
class Plugin(ad.Plugin):
|
16
14
|
"See :doc:`/dev/plugins`."
|
17
15
|
|
@@ -35,12 +33,12 @@ class Plugin(ad.Plugin):
|
|
35
33
|
# TODO: Also remove the Volume model and its actors when with_volumes is set
|
36
34
|
# to False.
|
37
35
|
|
38
|
-
def get_uploads_root(self):
|
39
|
-
|
40
|
-
|
36
|
+
# def get_uploads_root(self):
|
37
|
+
# # return join(self.site.django_settings["MEDIA_ROOT"], "uploads")
|
38
|
+
# return self.site.media_root / UPLOADS_ROOT
|
41
39
|
|
42
|
-
def get_volumes_root(self):
|
43
|
-
|
40
|
+
# def get_volumes_root(self):
|
41
|
+
# return self.site.media_root / VOLUMES_ROOT
|
44
42
|
|
45
43
|
def setup_main_menu(self, site, user_type, m, ar=None):
|
46
44
|
mg = self.get_menu_group()
|
@@ -61,14 +59,19 @@ class Plugin(ad.Plugin):
|
|
61
59
|
|
62
60
|
def post_site_startup(self, site):
|
63
61
|
|
62
|
+
self.uploads_root = site.media_root / UPLOADS_ROOT
|
63
|
+
self.volumes_root = site.media_root / VOLUMES_ROOT
|
64
|
+
|
64
65
|
if site.is_installed("memo"):
|
65
66
|
|
66
67
|
def gallery(ar, text, cmdname, mentions, context):
|
67
68
|
Upload = site.models.uploads.Upload
|
68
|
-
photos = [Upload.objects.get(pk=int(pk))
|
69
|
+
photos = [Upload.objects.get(pk=int(pk))
|
70
|
+
for pk in text.split()]
|
69
71
|
# ctx = dict(width="{}%".format(int(100/len(photos))))
|
70
72
|
mentions.update(photos)
|
71
|
-
html = "".join([obj.memo2html(ar, obj.description)
|
73
|
+
html = "".join([obj.memo2html(ar, obj.description)
|
74
|
+
for obj in photos])
|
72
75
|
return '<p align="center">{}</p>'.format(html)
|
73
76
|
|
74
77
|
site.plugins.memo.parser.register_command("gallery", gallery)
|
@@ -76,7 +79,7 @@ class Plugin(ad.Plugin):
|
|
76
79
|
super().post_site_startup(site)
|
77
80
|
|
78
81
|
# site.makedirs_if_missing(self.get_uploads_root())
|
79
|
-
# site.makedirs_if_missing(self.
|
82
|
+
# site.makedirs_if_missing(self.volumes_root)
|
80
83
|
|
81
84
|
def get_requirements(self, site):
|
82
85
|
if self.with_thumbnails:
|