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.
Files changed (70) hide show
  1. lino/__init__.py +8 -3
  2. lino/api/dd.py +11 -35
  3. lino/api/doctest.py +49 -17
  4. lino/api/selenium.py +1 -1
  5. lino/core/actions.py +25 -23
  6. lino/core/actors.py +52 -23
  7. lino/core/choicelists.py +10 -8
  8. lino/core/dbtables.py +1 -1
  9. lino/core/elems.py +47 -31
  10. lino/core/fields.py +19 -9
  11. lino/core/kernel.py +26 -20
  12. lino/core/model.py +27 -16
  13. lino/core/renderer.py +2 -2
  14. lino/core/requests.py +103 -56
  15. lino/core/site.py +5 -5
  16. lino/core/store.py +5 -2
  17. lino/core/utils.py +12 -7
  18. lino/help_texts.py +7 -8
  19. lino/mixins/duplicable.py +6 -4
  20. lino/mixins/sequenced.py +17 -6
  21. lino/modlib/__init__.py +0 -2
  22. lino/modlib/changes/models.py +21 -10
  23. lino/modlib/checkdata/models.py +59 -24
  24. lino/modlib/comments/fixtures/demo2.py +12 -3
  25. lino/modlib/comments/models.py +7 -7
  26. lino/modlib/comments/ui.py +8 -5
  27. lino/modlib/export_excel/models.py +7 -5
  28. lino/modlib/extjs/__init__.py +2 -2
  29. lino/modlib/extjs/views.py +66 -22
  30. lino/modlib/help/config/makehelp/conf.tpl.py +1 -1
  31. lino/modlib/jinja/mixins.py +73 -0
  32. lino/modlib/jinja/models.py +6 -0
  33. lino/modlib/linod/__init__.py +1 -0
  34. lino/modlib/linod/choicelists.py +21 -0
  35. lino/modlib/linod/consumers.py +13 -4
  36. lino/modlib/linod/fixtures/__init__.py +0 -0
  37. lino/modlib/linod/fixtures/linod.py +32 -0
  38. lino/modlib/linod/management/commands/linod.py +6 -2
  39. lino/modlib/linod/mixins.py +18 -14
  40. lino/modlib/linod/models.py +4 -2
  41. lino/modlib/memo/mixins.py +2 -1
  42. lino/modlib/memo/parser.py +1 -1
  43. lino/modlib/notify/models.py +19 -11
  44. lino/modlib/printing/actions.py +47 -42
  45. lino/modlib/printing/choicelists.py +17 -15
  46. lino/modlib/printing/mixins.py +22 -20
  47. lino/modlib/publisher/models.py +5 -5
  48. lino/modlib/summaries/models.py +3 -2
  49. lino/modlib/system/models.py +28 -29
  50. lino/modlib/uploads/__init__.py +14 -11
  51. lino/modlib/uploads/actions.py +2 -8
  52. lino/modlib/uploads/choicelists.py +10 -10
  53. lino/modlib/uploads/fixtures/std.py +17 -0
  54. lino/modlib/uploads/mixins.py +20 -8
  55. lino/modlib/uploads/models.py +62 -38
  56. lino/modlib/uploads/ui.py +15 -9
  57. lino/utils/__init__.py +0 -1
  58. lino/utils/jscompressor.py +4 -4
  59. lino/utils/media.py +45 -23
  60. lino/utils/report.py +5 -4
  61. lino/utils/restify.py +2 -2
  62. lino/utils/soup.py +26 -8
  63. lino/utils/xml.py +19 -5
  64. {lino-25.2.2.dist-info → lino-25.3.0.dist-info}/METADATA +1 -1
  65. {lino-25.2.2.dist-info → lino-25.3.0.dist-info}/RECORD +68 -65
  66. lino/mixins/uploadable.py +0 -3
  67. lino/utils/requests.py +0 -55
  68. {lino-25.2.2.dist-info → lino-25.3.0.dist-info}/WHEEL +0 -0
  69. {lino-25.2.2.dist-info → lino-25.3.0.dist-info}/licenses/AUTHORS.rst +0 -0
  70. {lino-25.2.2.dist-info → lino-25.3.0.dist-info}/licenses/COPYING +0 -0
@@ -1,5 +1,5 @@
1
1
  # -*- coding: UTF-8 -*-
2
- # Copyright 2009-2024 Rumma & Ko Ltd
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(BasePrintAction, self).__init__(label, **kwargs)
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(BasePrintAction, self).attach_to_actor(actor, name)
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
- # raise Exception("20170519 before_build didn't warn")
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
- if os.path.exists(filename):
78
- logger.debug("%s %s -> overwrite existing %s.", bm, elem, filename)
79
- os.remove(filename)
80
- else:
81
- # logger.info("20121221 makedirs_if_missing %s",os.path.dirname(filename))
82
- rt.makedirs_if_missing(os.path.dirname(filename))
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(E.a(leaf, href=url), encoding="unicode"))
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
- if has_davlink and bm.use_webdav and ar.request is not None:
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.get_target(self, elem)
115
- leaf = mf.parts[-1]
116
- self.notify_done(ar, bm, leaf, mf.get_url(ar.request), **kw)
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.get_target(self, obj)
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.get_url(ar.request))
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.get_target_name()
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
- merge_pdfs(pdfs, mf.name)
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 (settings.SITE.webdav_protocol or has_davlink):
227
+ if not settings.SITE.webdav_protocol:
225
228
  return False
226
- return super(EditTemplate, self).attach_to_actor(actor, name)
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 (settings.SITE.webdav_protocol or has_davlink):
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", ar.user, local_file)
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(ClearCacheAction, self).get_action_permission(ar, obj, state)
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.") % elem, refresh=True)
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-2023 Rumma & Ko Ltd
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(BuildMethod, self).__init__(
46
- names, self.__class__.__name__, names, **kwargs
47
- )
45
+ super().__init__(names, self.__class__.__name__, names, **kwargs)
48
46
 
49
- def get_target(self, action, obj):
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.filename_root() + self.target_ext,
54
+ obj.get_printable_target_stem() + self.target_ext,
57
55
  )
58
56
 
59
- def get_target_name(self, action, elem):
60
- return self.get_target(action, elem).name
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.get_target(action, elem).url
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(TemplatedBuildMethod, self).__init__(*args, **kwargs)
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("Error while loading template for %s : %s" % (tpls2, e))
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)] + "_" + lang + 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(cls.get_list_items())
292
+ settings.SITE.default_build_method, tuple(
293
+ cls.get_list_items())
292
294
  )
293
295
  )
294
296
  return bm
@@ -1,5 +1,5 @@
1
1
  # -*- coding: UTF-8 -*-
2
- # Copyright 2009-2024 Rumma & Ko Ltd
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() or translation.get_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 before_printable_build(self, bm):
147
- pass
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(_("build time"), null=True, editable=False)
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(CachedPrintable, self).full_clean(*args, **kwargs)
166
+ super().full_clean(*args, **kwargs)
166
167
 
167
168
  def on_duplicate(self, ar, master):
168
- super(CachedPrintable, self).on_duplicate(ar, master)
169
+ super().on_duplicate(ar, master)
169
170
  self.build_time = None
170
171
  self.build_method = None
171
172
 
172
- def get_target_name(self):
173
- if self.build_time:
174
- return self.get_build_method().get_target_name(self.do_print, self)
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.get_target_name()
191
+ filename = self.get_target_file().path
188
192
  if not filename:
189
193
  return None
190
- try:
191
- t = os.path.getmtime(filename)
192
- except OSError:
194
+ if not filename.exists():
193
195
  return None
194
- return datetime.datetime.fromtimestamp(t)
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(TypedPrintable, self).get_template_groups()
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(TypedPrintable, self).get_default_build_method()
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(TypedPrintable, self).get_build_method()
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(TypedPrintable, self).get_print_templates(bm, action)
249
+ return super().get_print_templates(bm, action)
248
250
 
249
251
  if ptype.template:
250
252
  return [ptype.template]
@@ -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(_("Reference"), max_length=200, blank=True, null=True)
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
- @dd.schedule_daily()
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 *
@@ -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
- @dd.schedule_daily()
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)
@@ -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(_("Simulated date"), blank=True, null=True)
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("{}: {} -> {}".format(fld.attname, oldval, newval))
196
+ diffs.append(
197
+ "{}: {} -> {}".format(fld.attname, oldval, newval))
194
198
  if len(diffs):
195
199
  print(
196
- "20220824 Overriding SiteConfig instance (diffs={})".format(diffs)
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))
@@ -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
- # return join(self.site.django_settings["MEDIA_ROOT"], "uploads")
40
- return self.site.media_root / UPLOADS_ROOT
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
- return self.site.media_root / VOLUMES_ROOT
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)) for pk in text.split()]
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) for obj in photos])
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.get_volumes_root())
82
+ # site.makedirs_if_missing(self.volumes_root)
80
83
 
81
84
  def get_requirements(self, site):
82
85
  if self.with_thumbnails: