lino 25.7.1__py3-none-any.whl → 25.7.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lino/__init__.py +1 -1
- lino/api/doctest.py +41 -12
- lino/core/__init__.py +0 -2
- lino/core/actions.py +15 -7
- lino/core/actors.py +2 -162
- lino/core/atomizer.py +9 -8
- lino/core/auth/utils.py +9 -1
- lino/core/callbacks.py +2 -2
- lino/core/elems.py +1 -1
- lino/core/fields.py +3 -1
- lino/core/kernel.py +14 -18
- lino/core/layouts.py +5 -7
- lino/core/model.py +12 -3
- lino/core/plugin.py +1 -1
- lino/core/renderer.py +1 -1
- lino/core/requests.py +3 -4
- lino/core/site.py +1 -1
- lino/core/store.py +3 -3
- lino/core/utils.py +20 -17
- lino/help_texts.py +10 -7
- lino/mixins/__init__.py +3 -2
- lino/mixins/{duplicable.py → clonable.py} +45 -50
- lino/mixins/dupable.py +2 -2
- lino/mixins/registrable.py +7 -5
- lino/mixins/sequenced.py +12 -14
- lino/modlib/dupable/models.py +2 -2
- lino/modlib/extjs/views.py +7 -0
- lino/modlib/linod/__init__.py +1 -1
- lino/modlib/memo/__init__.py +1 -2
- lino/modlib/notify/api.py +5 -0
- lino/modlib/office/roles.py +0 -1
- lino/modlib/printing/actions.py +2 -6
- lino/modlib/printing/choicelists.py +6 -6
- lino/modlib/printing/mixins.py +4 -4
- lino/modlib/publisher/__init__.py +21 -30
- lino/modlib/publisher/models.py +3 -1
- lino/modlib/publisher/views.py +4 -11
- lino/modlib/summaries/mixins.py +6 -4
- lino/modlib/users/actions.py +5 -0
- lino/modlib/weasyprint/__init__.py +9 -0
- lino/modlib/weasyprint/choicelists.py +14 -9
- lino/modlib/weasyprint/config/weasyprint/base.weasy.html +15 -13
- lino/sphinxcontrib/__init__.py +1 -1
- lino/sphinxcontrib/actordoc.py +1 -1
- lino/utils/diag.py +2 -2
- lino/utils/instantiator.py +21 -1
- {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/METADATA +1 -1
- {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/RECORD +51 -55
- lino/modlib/forms/__init__.py +0 -51
- lino/modlib/forms/models.py +0 -0
- lino/modlib/forms/renderer.py +0 -74
- lino/modlib/forms/views.py +0 -311
- {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/WHEEL +0 -0
- {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/licenses/COPYING +0 -0
lino/modlib/printing/actions.py
CHANGED
@@ -294,19 +294,15 @@ class EditTemplate(BasePrintAction):
|
|
294
294
|
ar.confirm(ok, msg, _("Are you sure?"))
|
295
295
|
|
296
296
|
|
297
|
-
class
|
297
|
+
class ClearCache(Action):
|
298
298
|
sort_index = 51
|
299
299
|
url_action_name = "clear"
|
300
300
|
label = _("Clear cache")
|
301
301
|
icon_name = "printer_delete"
|
302
302
|
|
303
|
-
# def disabled_for(self,obj,request):
|
304
|
-
# if not obj.build_time:
|
305
|
-
# return True
|
306
|
-
|
307
303
|
def get_action_permission(self, ar, obj, state):
|
308
304
|
# obj may be None when Lino asks whether this action
|
309
|
-
# should be visible in the
|
305
|
+
# should be visible in the table toolbar
|
310
306
|
if obj is not None and not obj.build_time:
|
311
307
|
return False
|
312
308
|
return super().get_action_permission(ar, obj, state)
|
@@ -33,9 +33,9 @@ except ImportError:
|
|
33
33
|
|
34
34
|
|
35
35
|
class BuildMethod(Choice):
|
36
|
-
target_ext = None
|
37
|
-
cache_name = "cache"
|
38
|
-
use_webdav = False
|
36
|
+
target_ext: str = None
|
37
|
+
cache_name: str = "cache"
|
38
|
+
use_webdav: bool = False
|
39
39
|
|
40
40
|
def __init__(self, names=None, **kwargs):
|
41
41
|
# For build methods, `Choice.names` and `Choice.value` are the
|
@@ -65,9 +65,9 @@ class BuildMethod(Choice):
|
|
65
65
|
|
66
66
|
|
67
67
|
class TemplatedBuildMethod(BuildMethod):
|
68
|
-
template_ext = None
|
69
|
-
templates_name = None
|
70
|
-
default_template = "" # overridden by lino_xl.lib.appypod
|
68
|
+
template_ext: str = None
|
69
|
+
templates_name: str = None
|
70
|
+
default_template: str = "" # overridden by lino_xl.lib.appypod
|
71
71
|
|
72
72
|
def __init__(self, *args, **kwargs):
|
73
73
|
super().__init__(*args, **kwargs)
|
lino/modlib/printing/mixins.py
CHANGED
@@ -16,13 +16,13 @@ from lino.modlib.checkdata.choicelists import Checker
|
|
16
16
|
|
17
17
|
from lino.utils.choosers import chooser
|
18
18
|
from lino.core.model import Model
|
19
|
-
from lino.mixins.
|
19
|
+
from lino.mixins.clonable import Clonable
|
20
20
|
|
21
21
|
from .choicelists import BuildMethods
|
22
22
|
from .actions import (
|
23
23
|
DirectPrintAction,
|
24
24
|
CachedPrintAction,
|
25
|
-
|
25
|
+
ClearCache,
|
26
26
|
EditTemplate,
|
27
27
|
)
|
28
28
|
|
@@ -148,12 +148,12 @@ class Printable(Model):
|
|
148
148
|
return True
|
149
149
|
|
150
150
|
|
151
|
-
class CachedPrintable(
|
151
|
+
class CachedPrintable(Clonable, Printable):
|
152
152
|
class Meta(object):
|
153
153
|
abstract = True
|
154
154
|
|
155
155
|
do_print = CachedPrintAction()
|
156
|
-
do_clear_cache =
|
156
|
+
do_clear_cache = ClearCache()
|
157
157
|
edit_template = EditTemplate()
|
158
158
|
|
159
159
|
build_time = models.DateTimeField(
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
|
-
# Copyright 2020-
|
2
|
+
# Copyright 2020-2025 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
5
|
from lino.api.ad import Plugin
|
@@ -12,17 +12,7 @@ class Plugin(Plugin):
|
|
12
12
|
"lino.modlib.jinja",
|
13
13
|
"lino.modlib.bootstrap3",
|
14
14
|
]
|
15
|
-
locations = []
|
16
|
-
|
17
|
-
def setup_main_menu(self, site, user_type, m, ar=None):
|
18
|
-
mg = self.get_menu_group()
|
19
|
-
m = m.add_menu(mg.app_label, mg.verbose_name)
|
20
|
-
m.add_action("publisher.Pages")
|
21
|
-
|
22
|
-
def setup_config_menu(self, site, user_type, m, ar=None):
|
23
|
-
mg = self.get_menu_group()
|
24
|
-
m = m.add_menu(mg.app_label, mg.verbose_name)
|
25
|
-
m.add_action("publisher.SpecialPages")
|
15
|
+
locations: list[tuple[str, str]] = []
|
26
16
|
|
27
17
|
def get_requirements(self, site):
|
28
18
|
yield "python-lorem"
|
@@ -41,34 +31,35 @@ class Plugin(Plugin):
|
|
41
31
|
app = site.models.get(app_label)
|
42
32
|
cls = getattr(app, model_name, None)
|
43
33
|
if not isinstance(cls, type) or not issubclass(cls, Actor):
|
44
|
-
raise Exception("location {}: {} is not an Actor"
|
34
|
+
raise Exception(f"location {loc}: {cls} is not an Actor")
|
45
35
|
if not issubclass(cls.model, Publishable):
|
46
36
|
raise Exception(
|
47
|
-
"location {}
|
48
|
-
|
49
|
-
)
|
50
|
-
)
|
51
|
-
|
37
|
+
f"location {loc},{view}: "
|
38
|
+
f"model {type(cls.model)} is not Publishable")
|
52
39
|
cls.model._lino_publisher_location = loc
|
53
40
|
locations.append((loc, cls))
|
54
41
|
self.locations = tuple(locations)
|
55
42
|
|
56
43
|
def get_patterns(self):
|
57
44
|
from django.urls import re_path as url
|
58
|
-
from lino.core.utils import models_by_base
|
59
45
|
from . import views
|
60
|
-
# from .choicelists import PublisherViews
|
61
|
-
# raise Exception("20220927")
|
62
|
-
# print("20220927", list(PublisherViews.get_list_items()))
|
63
46
|
|
64
|
-
|
65
|
-
for publisher_location, table_class in self.locations:
|
66
|
-
# print("20220927", pv.publisher_location)
|
67
|
-
# if publisher_location is not None:
|
47
|
+
for location, table_class in self.locations:
|
68
48
|
yield url(
|
69
|
-
"^{}/(?P<pk>.+)$"
|
70
|
-
views.Element.as_view(table_class=table_class)
|
71
|
-
)
|
49
|
+
f"^{location}/(?P<pk>.+)$",
|
50
|
+
views.Element.as_view(table_class=table_class))
|
72
51
|
|
73
|
-
|
52
|
+
# Only if this is the primary front end:
|
53
|
+
if self.site.kernel.web_front_ends[0] is self:
|
54
|
+
yield url("^$", views.Index.as_view())
|
74
55
|
# yield url('^login$',views.Login.as_view())
|
56
|
+
|
57
|
+
def setup_main_menu(self, site, user_type, m, ar=None):
|
58
|
+
mg = self.get_menu_group()
|
59
|
+
m = m.add_menu(mg.app_label, mg.verbose_name)
|
60
|
+
m.add_action("publisher.Pages")
|
61
|
+
|
62
|
+
def setup_config_menu(self, site, user_type, m, ar=None):
|
63
|
+
mg = self.get_menu_group()
|
64
|
+
m = m.add_menu(mg.app_label, mg.verbose_name)
|
65
|
+
m.add_action("publisher.SpecialPages")
|
lino/modlib/publisher/models.py
CHANGED
@@ -47,6 +47,8 @@ from .ui import *
|
|
47
47
|
|
48
48
|
# class Node(Referrable, Hierarchical, Sequenced, Previewable, Publishable, Commentable):
|
49
49
|
# Polymorphic,
|
50
|
+
|
51
|
+
|
50
52
|
class Page(
|
51
53
|
Hierarchical, Sequenced, Previewable, Commentable, PublishableContent, Taggable
|
52
54
|
):
|
@@ -205,7 +207,7 @@ class Page(
|
|
205
207
|
if not self.children.exists():
|
206
208
|
return
|
207
209
|
|
208
|
-
yield "<p><b>{}</b></p>".format(_("Children:"))
|
210
|
+
# yield "<p><b>{}</b></p>".format(_("Children:"))
|
209
211
|
|
210
212
|
if hlevel > home.child_node_depth:
|
211
213
|
yield " (...)"
|
lino/modlib/publisher/views.py
CHANGED
@@ -1,19 +1,12 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
|
-
# Copyright 2020-
|
2
|
+
# Copyright 2020-2025 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
|
-
from django.conf import settings
|
6
5
|
from django import http
|
7
|
-
from django.
|
8
|
-
from django.utils import translation
|
9
|
-
|
10
|
-
from lino.api import dd
|
11
|
-
from lino.core import auth
|
12
|
-
from lino.core.requests import BaseRequest, ActionRequest
|
6
|
+
from django.conf import settings
|
13
7
|
from django.core.exceptions import ObjectDoesNotExist
|
14
|
-
|
15
|
-
from django.
|
16
|
-
from django.views.decorators.csrf import ensure_csrf_cookie
|
8
|
+
from django.utils import translation
|
9
|
+
from django.views.generic import View
|
17
10
|
|
18
11
|
|
19
12
|
class Element(View):
|
lino/modlib/summaries/mixins.py
CHANGED
@@ -38,7 +38,8 @@ class UpdateSummariesByMaster(ComputeResults):
|
|
38
38
|
|
39
39
|
|
40
40
|
class Summarized(dd.Model):
|
41
|
-
|
41
|
+
|
42
|
+
class Meta:
|
42
43
|
abstract = True
|
43
44
|
|
44
45
|
compute_results = ComputeResults()
|
@@ -86,15 +87,17 @@ class Summarized(dd.Model):
|
|
86
87
|
|
87
88
|
def get_summary_collectors(self):
|
88
89
|
raise NotImplementedError(
|
89
|
-
"{} must define get_summary_collectors()"
|
90
|
+
f"{self.__class__} must define get_summary_collectors()"
|
90
91
|
)
|
91
92
|
|
92
93
|
|
93
94
|
class SlaveSummarized(Summarized):
|
94
|
-
|
95
|
+
|
96
|
+
class Meta:
|
95
97
|
abstract = True
|
96
98
|
|
97
99
|
allow_cascaded_delete = "master"
|
100
|
+
allow_cascaded_copy = set()
|
98
101
|
|
99
102
|
@classmethod
|
100
103
|
def check_all_summaries(cls):
|
@@ -181,7 +184,6 @@ class DateSummarized(Summarized):
|
|
181
184
|
flt.update(month=period)
|
182
185
|
if year is not None:
|
183
186
|
flt.update(year=year)
|
184
|
-
# obj = cls.get_for_period(**flt)
|
185
187
|
for obj in cls.get_for_filter(**flt):
|
186
188
|
obj.compute_summary_values()
|
187
189
|
|
lino/modlib/users/actions.py
CHANGED
@@ -495,6 +495,11 @@ def get_social_auth_links_func(content_header, flex_row, ar=None):
|
|
495
495
|
else:
|
496
496
|
el = E.span(anchor, style=style)
|
497
497
|
links.append(el)
|
498
|
+
anchor = E.a(E.span(gettext("Smart ID")), href="/auth/smart_id")
|
499
|
+
if flex_row:
|
500
|
+
links.append(E.div(anchor, style=style))
|
501
|
+
else:
|
502
|
+
links.append(E.span(anchor, style=style))
|
498
503
|
elems.append(E.div(*links, style="text-align: center;"))
|
499
504
|
return tostring(E.div(*elems))
|
500
505
|
|
@@ -39,9 +39,18 @@ class Plugin(ad.Plugin):
|
|
39
39
|
margin_left = 17
|
40
40
|
margin_right = 10
|
41
41
|
space_before_recipient = 15
|
42
|
+
with_bulma = False
|
43
|
+
|
44
|
+
def get_needed_plugins(self):
|
45
|
+
for p in super().get_needed_plugins():
|
46
|
+
yield p
|
47
|
+
if self.with_bulma:
|
48
|
+
yield 'bulma'
|
42
49
|
|
43
50
|
def get_requirements(self, site):
|
44
51
|
yield "imagesize"
|
52
|
+
if self.with_bulma:
|
53
|
+
yield 'django-bulma'
|
45
54
|
|
46
55
|
def pre_site_startup(self, site):
|
47
56
|
for ext in ("jpg", "png"):
|
@@ -2,22 +2,26 @@
|
|
2
2
|
# Copyright 2016-2024 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
|
-
from pathlib import Path
|
6
5
|
from lino.modlib.jinja.choicelists import JinjaBuildMethod
|
7
6
|
from lino.modlib.printing.choicelists import BuildMethods
|
7
|
+
from lino.api import dd
|
8
8
|
|
9
9
|
try:
|
10
10
|
from weasyprint import HTML
|
11
11
|
except ImportError:
|
12
12
|
HTML = None
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
BULMA_CSS = None
|
15
|
+
|
16
|
+
if dd.plugins.weasyprint.with_bulma:
|
17
|
+
try:
|
18
|
+
from pathlib import Path
|
19
|
+
import bulma
|
20
|
+
from weasyprint import CSS
|
21
|
+
BULMA_CSS = Path(bulma.__file__).parent / "static/bulma/css/style.min.css"
|
22
|
+
assert BULMA_CSS.exists()
|
23
|
+
except ImportError:
|
24
|
+
pass
|
21
25
|
|
22
26
|
|
23
27
|
class WeasyBuildMethod(JinjaBuildMethod):
|
@@ -38,7 +42,8 @@ class WeasyPdfBuildMethod(WeasyBuildMethod):
|
|
38
42
|
def html2file(self, html, filename, context):
|
39
43
|
pdf = HTML(string=html)
|
40
44
|
if BULMA_CSS and context.get('use_bulma_css', False):
|
41
|
-
pdf.write_pdf(
|
45
|
+
pdf.write_pdf(
|
46
|
+
filename, stylesheets=[CSS(filename=BULMA_CSS)])
|
42
47
|
else:
|
43
48
|
pdf.write_pdf(filename)
|
44
49
|
|
@@ -31,13 +31,18 @@ table.footer td {
|
|
31
31
|
border: none;
|
32
32
|
padding: 6pt;
|
33
33
|
}
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
{#
|
35
|
+
Removed after #6179 (The footer of a multipage invoice shows the same
|
36
|
+
pagenumber on each page)
|
37
37
|
|
38
|
-
span.
|
39
|
-
|
40
|
-
}
|
38
|
+
span.page_num_of::after {
|
39
|
+
content: '{{_("Page")}} ' counter(page) ' {{_("of {0}").format("")}}' counter(pages);
|
40
|
+
}
|
41
|
+
|
42
|
+
span.printed_time::after {
|
43
|
+
content: '{{_("Printed")}} {{fdm(dd.today())}} {{_("at")}} {{now.time().strftime("%H:%M")}}';
|
44
|
+
}
|
45
|
+
#}
|
41
46
|
|
42
47
|
body {
|
43
48
|
font-family: "Liberation sans", "Arial", "Helvetica";
|
@@ -83,13 +88,10 @@ div.recipient {
|
|
83
88
|
margin-left: {{dd.plugins.weasyprint.margin_left}}mm;
|
84
89
|
margin-right: {{dd.plugins.weasyprint.margin_right}}mm;
|
85
90
|
{%- if dd.plugins.weasyprint.page_background_image -%}
|
86
|
-
|
87
|
-
background:
|
88
|
-
|
89
|
-
|
90
|
-
background-repeat: no-repeat;
|
91
|
-
background-attachment: fixed;
|
92
|
-
background-size: contain;
|
91
|
+
background-image: url(file://{{dd.plugins.weasyprint.page_background_image}});
|
92
|
+
background-repeat: no-repeat;
|
93
|
+
background-attachment: fixed;
|
94
|
+
background-size: contain;
|
93
95
|
{%- endif -%}
|
94
96
|
font-family: "Liberation sans", "arial";
|
95
97
|
font-size: 10pt;
|
lino/sphinxcontrib/__init__.py
CHANGED
@@ -102,7 +102,7 @@ def configure(globals_dict, django_settings_module=None):
|
|
102
102
|
rstgen.sphinxconf.configure()
|
103
103
|
|
104
104
|
You can specify an additional positional argument `django_settings_module`
|
105
|
-
(the name of a Django settings
|
105
|
+
(the name of a Django settings file). If this argument is specified, call
|
106
106
|
:meth:`lino.startup` with it.
|
107
107
|
|
108
108
|
"""
|
lino/sphinxcontrib/actordoc.py
CHANGED
@@ -11,7 +11,7 @@ for a Lino application.
|
|
11
11
|
source code. This is like :rst:dir:`py2rst` but with the following
|
12
12
|
names defined:
|
13
13
|
|
14
|
-
:settings: The Django settings
|
14
|
+
:settings: The Django settings file which is active while building
|
15
15
|
the docs.
|
16
16
|
|
17
17
|
:dd: The :mod:`lino.api.dd` module.
|
lino/utils/diag.py
CHANGED
@@ -341,9 +341,9 @@ class Analyzer(object):
|
|
341
341
|
return "{0}.{1}".format(fmn(mfk[0]), mfk[1].name)
|
342
342
|
|
343
343
|
items1 = []
|
344
|
-
for target, dp in
|
344
|
+
for target, dp in tdp.items():
|
345
345
|
items2 = []
|
346
|
-
for dh, pl in
|
346
|
+
for dh, pl in dp.items():
|
347
347
|
items2.append(
|
348
348
|
"{0} : {1}".format(
|
349
349
|
dh.__name__, ", ".join([fk2str(mfk) for mfk in pl])
|
lino/utils/instantiator.py
CHANGED
@@ -215,13 +215,33 @@ def create_and_get(model, **kw):
|
|
215
215
|
def make_if_needed(model, **values):
|
216
216
|
qs = model.objects.filter(**values)
|
217
217
|
if qs.count() == 1:
|
218
|
-
|
218
|
+
return qs.first()
|
219
|
+
# pass # ok, nothing to do
|
219
220
|
elif qs.count() == 0:
|
220
221
|
return model(**values)
|
221
222
|
else:
|
222
223
|
raise Exception(f"Multiple {model._meta.verbose_name_plural} for {values}")
|
223
224
|
|
224
225
|
|
226
|
+
def get_or_create(model, **kwargs):
|
227
|
+
# similar to Djanop's QuerySet.get_or_create() but calls full_clean
|
228
|
+
try:
|
229
|
+
obj = model.objects.get(**kwargs)
|
230
|
+
except model.DoesNotExist:
|
231
|
+
obj = model(**kwargs)
|
232
|
+
obj.full_clean()
|
233
|
+
obj.save()
|
234
|
+
return obj
|
235
|
+
|
236
|
+
|
237
|
+
def update_or_create(m, **kwargs):
|
238
|
+
if (obj := m.objects.filter(id=kwargs['id']).first()) is not None:
|
239
|
+
for k, v in kwargs.items():
|
240
|
+
setattr(obj, k, v)
|
241
|
+
return obj
|
242
|
+
return m(**kwargs)
|
243
|
+
|
244
|
+
|
225
245
|
def _test():
|
226
246
|
import doctest
|
227
247
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: lino
|
3
|
-
Version: 25.7.
|
3
|
+
Version: 25.7.3
|
4
4
|
Summary: A framework for writing desktop-like web applications using Django and ExtJS or React
|
5
5
|
Project-URL: Homepage, https://www.lino-framework.org
|
6
6
|
Project-URL: Repository, https://gitlab.com/lino-framework/lino
|