lino 25.8.2__py3-none-any.whl → 25.9.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/api/dd.py +0 -1
- lino/config/unused/403.html +1 -1
- lino/config/unused/404.html +1 -1
- lino/config/unused/500.html +1 -1
- lino/core/__init__.py +0 -1
- lino/core/actions.py +2 -2
- lino/core/actors.py +10 -2
- lino/core/elems.py +1 -1
- lino/core/fields.py +4 -1
- lino/core/kernel.py +5 -1
- lino/core/model.py +2 -11
- lino/core/renderer.py +2 -2
- lino/core/requests.py +12 -12
- lino/core/site.py +5 -82
- lino/core/store.py +3 -1
- lino/core/urls.py +1 -1
- lino/core/user_types.py +1 -10
- lino/help_texts.py +6 -6
- lino/management/commands/initdb.py +0 -3
- lino/modlib/__init__.py +0 -1
- lino/modlib/bootstrap5/README.txt +2 -0
- lino/modlib/bootstrap5/__init__.py +69 -0
- lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/base.html +9 -4
- lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/detail.html +1 -1
- lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/index.html +1 -1
- lino/modlib/{bootstrap3/config/bootstrap3 → bootstrap5/config/bootstrap5}/table.html +1 -1
- lino/modlib/bootstrap5/models.py +30 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.css +4085 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.min.css +6 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.min.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.css +4084 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.min.css +6 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-grid.rtl.min.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.css +597 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.min.css +6 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.min.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.css +594 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.min.css +6 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-reboot.rtl.min.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.css +5406 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.min.css +6 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.min.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.css +5397 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.min.css +6 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap-utilities.rtl.min.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.css +12043 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.min.css +6 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.min.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.css +12016 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.min.css +6 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/css/bootstrap.rtl.min.css.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.js +6315 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.js.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.min.js +7 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.bundle.min.js.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.js +4450 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.js.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.min.js +7 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.esm.min.js.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.js +4497 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.js.map +1 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.min.js +7 -0
- lino/modlib/bootstrap5/static/bootstrap-5.3.7/js/bootstrap.min.js.map +1 -0
- lino/modlib/{bootstrap3 → bootstrap5}/views.py +12 -117
- lino/modlib/checkdata/choicelists.py +1 -1
- lino/modlib/comments/fixtures/demo2.py +1 -0
- lino/modlib/comments/mixins.py +1 -8
- lino/modlib/comments/models.py +2 -0
- lino/modlib/comments/ui.py +7 -7
- lino/modlib/extjs/__init__.py +2 -4
- lino/modlib/extjs/config/extjs/index.html +1 -1
- lino/modlib/extjs/ext_renderer.py +1 -7
- lino/modlib/extjs/views.py +2 -0
- lino/modlib/help/models.py +1 -12
- lino/modlib/jinja/renderer.py +1 -1
- lino/modlib/linod/mixins.py +3 -2
- lino/modlib/memo/__init__.py +11 -11
- lino/modlib/memo/mixins.py +38 -21
- lino/modlib/memo/models.py +10 -7
- lino/modlib/memo/parser.py +3 -1
- lino/modlib/notify/models.py +6 -9
- lino/modlib/odata/views.py +7 -7
- lino/modlib/publisher/__init__.py +15 -3
- lino/modlib/publisher/choicelists.py +8 -94
- lino/modlib/publisher/config/publisher/page.pub.html +82 -19
- lino/modlib/publisher/fixtures/std.py +14 -1
- lino/modlib/publisher/fixtures/synodalworld.py +3 -1
- lino/modlib/publisher/mixins.py +59 -77
- lino/modlib/publisher/models.py +109 -204
- lino/modlib/publisher/renderer.py +31 -11
- lino/modlib/publisher/ui.py +46 -98
- lino/modlib/publisher/views.py +61 -11
- lino/modlib/system/models.py +3 -2
- lino/modlib/uploads/__init__.py +1 -0
- lino/modlib/uploads/mixins.py +2 -2
- lino/modlib/uploads/models.py +55 -21
- lino/modlib/uploads/ui.py +1 -0
- lino/modlib/uploads/utils.py +2 -2
- lino/modlib/users/__init__.py +2 -3
- lino/modlib/users/actions.py +12 -17
- lino/modlib/users/fixtures/abc.py +20 -0
- lino/modlib/users/mixins.py +6 -6
- lino/modlib/users/models.py +37 -36
- lino/modlib/weasyprint/__init__.py +25 -14
- lino/modlib/weasyprint/choicelists.py +6 -0
- lino/modlib/weasyprint/config/weasyprint/base.weasy.html +43 -27
- lino/utils/diag.py +5 -3
- lino/utils/html.py +103 -0
- lino/utils/mldbc/mixins.py +2 -2
- lino/utils/soup.py +16 -8
- {lino-25.8.2.dist-info → lino-25.9.0.dist-info}/METADATA +1 -1
- {lino-25.8.2.dist-info → lino-25.9.0.dist-info}/RECORD +135 -95
- lino/modlib/bootstrap3/README.txt +0 -2
- lino/modlib/bootstrap3/__init__.py +0 -73
- lino/modlib/bootstrap3/models.py +0 -30
- lino/modlib/bootstrap3/static/bootstrap-3.3.4/css/bootstrap.css +0 -6584
- lino/modlib/bootstrap3/static/bootstrap-3.3.4/css/bootstrap.css.map +0 -1
- lino/modlib/bootstrap3/static/bootstrap-3.3.4/css/bootstrap.min.css +0 -5
- lino/modlib/bootstrap3/static/bootstrap-3.3.4/js/bootstrap.js +0 -2317
- lino/modlib/bootstrap3/static/bootstrap-3.3.4/js/bootstrap.min.js +0 -7
- /lino/modlib/{bootstrap3 → bootstrap5}/renderer.py +0 -0
- /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/css/bootstrap-theme.css +0 -0
- /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/css/bootstrap-theme.css.map +0 -0
- /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/css/bootstrap-theme.min.css +0 -0
- /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.eot +0 -0
- /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.svg +0 -0
- /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.ttf +0 -0
- /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.woff +0 -0
- /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/fonts/glyphicons-halflings-regular.woff2 +0 -0
- /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/js/bootstrap_lino.js +0 -0
- /lino/modlib/{bootstrap3/static/bootstrap-3.3.4 → bootstrap5/static/bootstrap-5.3.7}/js/npm.js +0 -0
- {lino-25.8.2.dist-info → lino-25.9.0.dist-info}/WHEEL +0 -0
- {lino-25.8.2.dist-info → lino-25.9.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.8.2.dist-info → lino-25.9.0.dist-info}/licenses/COPYING +0 -0
lino/modlib/users/models.py
CHANGED
@@ -476,43 +476,44 @@ if dd.plugins.users.allow_online_registration:
|
|
476
476
|
About.create_account = CreateAccount()
|
477
477
|
|
478
478
|
|
479
|
-
|
480
|
-
def setup_memo_commands(sender=None, **kwargs):
|
481
|
-
# See :doc:`/specs/memo`
|
482
|
-
|
483
|
-
if not sender.is_installed("memo"):
|
484
|
-
return
|
485
|
-
|
486
|
-
mp = sender.plugins.memo.parser
|
487
|
-
mp.add_suggester(
|
488
|
-
"@",
|
489
|
-
sender.models.users.User.objects.filter(username__isnull=False).order_by(
|
490
|
-
"username"
|
491
|
-
),
|
492
|
-
"username",
|
493
|
-
)
|
479
|
+
if dd.is_installed("memo"):
|
494
480
|
|
481
|
+
@dd.receiver(dd.post_startup)
|
482
|
+
def setup_memo_commands(sender=None, **kwargs):
|
483
|
+
# See :doc:`/specs/memo`
|
495
484
|
|
496
|
-
|
497
|
-
|
498
|
-
if not me.is_verified():
|
499
|
-
# sar = rt.models.users.Me.create_request(parent=ar)
|
500
|
-
sar = ar
|
501
|
-
if me.email:
|
502
|
-
# verify_me =
|
503
|
-
msg = format_html(
|
504
|
-
_("Your email address ({email}) is not verified, "
|
505
|
-
"please check your mailbox and {verify} or {resend}."),
|
506
|
-
email=me.email,
|
507
|
-
verify=tostring(sar.instance_action_button(
|
508
|
-
me.verify_me, _("verify now"))),
|
509
|
-
resend=tostring(sar.instance_action_button(
|
510
|
-
me.send_welcome_email, _("re-send our welcome email"))))
|
511
|
-
else:
|
512
|
-
msg = format_html(
|
513
|
-
_("You have no email address, please {edit}."),
|
514
|
-
edit=tostring(sar.obj2html(me, _("edit your user settings"))))
|
515
|
-
yield mark_safe(msg)
|
485
|
+
# if not sender.is_installed("memo"):
|
486
|
+
# return
|
516
487
|
|
488
|
+
mp = sender.plugins.memo.parser
|
489
|
+
mp.add_suggester(
|
490
|
+
"@",
|
491
|
+
sender.models.users.User.objects.filter(
|
492
|
+
username__isnull=False).order_by("username"),
|
493
|
+
"username")
|
494
|
+
|
495
|
+
|
496
|
+
if dd.plugins.users.allow_online_registration:
|
517
497
|
|
518
|
-
|
498
|
+
def welcome_messages(ar):
|
499
|
+
me = ar.get_user()
|
500
|
+
if not me.is_verified():
|
501
|
+
# sar = rt.models.users.Me.create_request(parent=ar)
|
502
|
+
sar = ar
|
503
|
+
if me.email:
|
504
|
+
# verify_me =
|
505
|
+
msg = format_html(
|
506
|
+
_("Your email address ({email}) is not verified, "
|
507
|
+
"please check your mailbox and {verify} or {resend}."),
|
508
|
+
email=me.email,
|
509
|
+
verify=tostring(sar.instance_action_button(
|
510
|
+
me.verify_me, _("verify now"))),
|
511
|
+
resend=tostring(sar.instance_action_button(
|
512
|
+
me.send_welcome_email, _("re-send our welcome email"))))
|
513
|
+
else:
|
514
|
+
msg = format_html(
|
515
|
+
_("You have no email address, please {edit}."),
|
516
|
+
edit=tostring(sar.obj2html(me, _("edit your user settings"))))
|
517
|
+
yield mark_safe(msg)
|
518
|
+
|
519
|
+
dd.add_welcome_handler(welcome_messages)
|
@@ -34,6 +34,8 @@ class Plugin(ad.Plugin):
|
|
34
34
|
top_right_width = None
|
35
35
|
page_background_image = None
|
36
36
|
top_right_image = None
|
37
|
+
bottom_left_image = None
|
38
|
+
bottom_left_width = None
|
37
39
|
header_image = None
|
38
40
|
margin = 10
|
39
41
|
margin_left = 17
|
@@ -53,25 +55,34 @@ class Plugin(ad.Plugin):
|
|
53
55
|
yield 'django-bulma'
|
54
56
|
|
55
57
|
def pre_site_startup(self, site):
|
58
|
+
fcf = site.confdirs.find_config_file
|
56
59
|
for ext in ("jpg", "png"):
|
57
|
-
if self.
|
58
|
-
fn
|
59
|
-
|
60
|
+
if self.bottom_left_image is None:
|
61
|
+
if fn := fcf("bottom-left." + ext, "weasyprint"):
|
62
|
+
self.bottom_left_image = fn
|
63
|
+
if self.top_right_image is None:
|
64
|
+
if fn := fcf("top-right." + ext, "weasyprint"):
|
60
65
|
self.top_right_image = fn
|
61
|
-
|
62
|
-
|
63
|
-
site.logger.warning("imagesize is not installed")
|
64
|
-
continue
|
65
|
-
w, h = imagesize.get(fn)
|
66
|
-
self.top_right_width = self.header_height * w / h
|
67
|
-
fn = site.confdirs.find_config_file("header." + ext, "weasyprint")
|
68
|
-
if fn:
|
66
|
+
if self.header_image is None:
|
67
|
+
if fn := fcf("header." + ext, "weasyprint"):
|
69
68
|
# site.logger.info("Found header_image %s", fn)
|
70
69
|
self.header_image = fn
|
71
70
|
if self.page_background_image is None:
|
72
|
-
fn
|
73
|
-
"page-background." + ext, "weasyprint")
|
74
|
-
if fn:
|
71
|
+
if fn := fcf("page-background." + ext, "weasyprint"):
|
75
72
|
# site.logger.info("Found page_background_image %s", fn)
|
76
73
|
self.page_background_image = fn
|
74
|
+
if self.header_height:
|
75
|
+
if self.top_right_image and not self.top_right_width:
|
76
|
+
# if imagesize is None:
|
77
|
+
# site.logger.warning("imagesize is not installed")
|
78
|
+
# continue
|
79
|
+
w, h = imagesize.get(self.top_right_image)
|
80
|
+
self.top_right_width = self.header_height * w / h
|
81
|
+
if self.footer_height:
|
82
|
+
if self.bottom_left_image and not self.bottom_left_width:
|
83
|
+
# if imagesize is None:
|
84
|
+
# site.logger.warning("imagesize is not installed")
|
85
|
+
# continue
|
86
|
+
w, h = imagesize.get(self.bottom_left_image)
|
87
|
+
self.bottom_left_width = self.footer_height * w / h
|
77
88
|
super().pre_site_startup(site)
|
@@ -14,6 +14,12 @@ except ImportError:
|
|
14
14
|
BULMA_CSS = None
|
15
15
|
|
16
16
|
if dd.plugins.weasyprint.with_bulma:
|
17
|
+
|
18
|
+
# Bulma causes weayprint to issue many warnings, more than 2000 during one
|
19
|
+
# tested doc. So we deactivate them:
|
20
|
+
from weasyprint.logger import LOGGER, logging
|
21
|
+
LOGGER.setLevel(logging.ERROR)
|
22
|
+
|
17
23
|
try:
|
18
24
|
from pathlib import Path
|
19
25
|
import bulma
|
@@ -1,6 +1,7 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
3
|
<head>
|
4
|
+
{%- set WP = dd.plugins.weasyprint %}
|
4
5
|
{%- block head %}
|
5
6
|
<meta charset="UTF-8">
|
6
7
|
<style type="text/css">
|
@@ -58,7 +59,7 @@ p {
|
|
58
59
|
}
|
59
60
|
|
60
61
|
div.recipient {
|
61
|
-
position:relative; left:{{100-
|
62
|
+
position:relative; left:{{100-WP.margin_left}}mm;
|
62
63
|
height:30mm;
|
63
64
|
width:80mm;
|
64
65
|
border: 1px solid lightgray;
|
@@ -80,25 +81,50 @@ div.recipient {
|
|
80
81
|
|
81
82
|
@page {
|
82
83
|
size: {%- block pagesize %}landscape{%- endblock %};
|
83
|
-
margin: {{
|
84
|
-
margin-bottom: {{
|
85
|
-
{%- if True or
|
86
|
-
margin-top: {{
|
84
|
+
margin: {{WP.margin}}mm;
|
85
|
+
margin-bottom: {{WP.margin+WP.footer_height}}mm;
|
86
|
+
{%- if True or WP.top_right_image -%}
|
87
|
+
margin-top: {{WP.margin+WP.header_height}}mm;
|
87
88
|
{%- endif -%}
|
88
|
-
margin-left: {{
|
89
|
-
margin-right: {{
|
90
|
-
{%- if
|
91
|
-
background-image: url(file://{{
|
89
|
+
margin-left: {{WP.margin_left}}mm;
|
90
|
+
margin-right: {{WP.margin_right}}mm;
|
91
|
+
{%- if WP.page_background_image -%}
|
92
|
+
background-image: url(file://{{WP.page_background_image}});
|
92
93
|
background-repeat: no-repeat;
|
93
94
|
background-attachment: fixed;
|
94
95
|
background-size: contain;
|
95
96
|
{%- endif -%}
|
96
97
|
font-family: "Liberation sans", "arial";
|
97
98
|
font-size: 10pt;
|
99
|
+
|
100
|
+
{%- block topright %}
|
101
|
+
{% if WP.top_right_image %}
|
102
|
+
@top-right {
|
103
|
+
height: {{WP.header_height}}mm;
|
104
|
+
width: {{WP.top_right_width}}mm;
|
105
|
+
padding: 0mm;
|
106
|
+
text-align: right;
|
107
|
+
background-image: url(file://{{WP.top_right_image}});
|
108
|
+
background-size: cover;
|
109
|
+
content: ""; // may be empty but must exist, otherwise bg is not rendered
|
110
|
+
// content: url(file://{{WP.top_right_image}});
|
111
|
+
}
|
112
|
+
{% endif %}
|
113
|
+
{%- endblock topright %}
|
98
114
|
{%- block bottomleft %}
|
99
115
|
@bottom-left {
|
116
|
+
{% if WP.bottom_left_image %}
|
117
|
+
height: {{WP.footer_height}}mm;
|
118
|
+
width: {{WP.bottom_left_width}}mm;
|
119
|
+
padding: 0mm;
|
120
|
+
text-align: right;
|
121
|
+
background-image: url(file://{{WP.bottom_left_image}});
|
122
|
+
background-size: cover;
|
123
|
+
content: ""; // may be empty but must exist, otherwise bg is not rendered
|
124
|
+
{% else %}
|
100
125
|
vertical-align: top;
|
101
126
|
content: '{{_("Printed")}} {{fdm(dd.today())}} {{_("at")}} {{now.time().strftime("%H:%M")}}';
|
127
|
+
{% endif %}
|
102
128
|
}
|
103
129
|
{%- endblock bottomleft %}
|
104
130
|
{%- block bottomright %}
|
@@ -107,21 +133,6 @@ div.recipient {
|
|
107
133
|
content: '{{_("Page")}} ' counter(page) ' {{_("of {0}").format("")}} ' counter(pages);
|
108
134
|
}
|
109
135
|
{%- endblock bottomright %}
|
110
|
-
|
111
|
-
{%- block topright %}
|
112
|
-
{% if dd.plugins.weasyprint.top_right_image %}
|
113
|
-
@top-right {
|
114
|
-
height: {{dd.plugins.weasyprint.header_height}}mm;
|
115
|
-
width: {{dd.plugins.weasyprint.top_right_width}}mm;
|
116
|
-
padding: 0mm;
|
117
|
-
text-align: right;
|
118
|
-
background-image: url(file://{{dd.plugins.weasyprint.top_right_image}});
|
119
|
-
background-size: cover;
|
120
|
-
content: ""; // may be empty but must exist, otherwise bg is not rendered
|
121
|
-
// content: url(file://{{dd.plugins.weasyprint.top_right_image}});
|
122
|
-
}
|
123
|
-
{% endif %}
|
124
|
-
{%- endblock topright %}
|
125
136
|
}
|
126
137
|
|
127
138
|
@media print {
|
@@ -142,7 +153,7 @@ div.recipient {
|
|
142
153
|
div.footer_div {
|
143
154
|
position: fixed;
|
144
155
|
// bottom: -5mm;
|
145
|
-
bottom: -{{
|
156
|
+
bottom: -{{WP.footer_height}}mm;
|
146
157
|
width: 100%;
|
147
158
|
// border-top: solid 1pt;
|
148
159
|
padding: 0;
|
@@ -164,9 +175,14 @@ div.recipient {
|
|
164
175
|
</head>
|
165
176
|
<body>
|
166
177
|
{%- block header %}
|
167
|
-
{% if
|
178
|
+
{% if WP.header_image %}
|
168
179
|
<div class="header_div">
|
169
|
-
<img src="file://{{
|
180
|
+
<img src="file://{{WP.header_image}}" style="height:{{WP.header_height}}mm">
|
181
|
+
</div>
|
182
|
+
{% endif %}
|
183
|
+
{% if WP.footer_image %}
|
184
|
+
<div class="footer_div">
|
185
|
+
<img src="file://{{WP.footer_image}}" style="height:{{WP.footer_height}}mm">
|
170
186
|
</div>
|
171
187
|
{% endif %}
|
172
188
|
{%- endblock header %}
|
lino/utils/diag.py
CHANGED
@@ -144,14 +144,14 @@ class Analyzer(object):
|
|
144
144
|
|
145
145
|
return rstgen.ul(items)
|
146
146
|
|
147
|
-
def
|
147
|
+
def show_db_structure(self, sort_fields=False, sort_models=True, verbose_names=False):
|
148
148
|
"""Show a bullet list of all models and their fields.
|
149
149
|
|
150
150
|
Both the list of models and the fields of each model can optionally be
|
151
151
|
sorted alphabetically. The models are sorted by default, the fields not.
|
152
152
|
|
153
153
|
"""
|
154
|
-
self.analyze()
|
154
|
+
# self.analyze()
|
155
155
|
items = []
|
156
156
|
for model in get_models():
|
157
157
|
# names = []
|
@@ -170,7 +170,9 @@ class Analyzer(object):
|
|
170
170
|
|
171
171
|
if sort_models:
|
172
172
|
items = sorted(items)
|
173
|
-
|
173
|
+
print(rstgen.ul(items))
|
174
|
+
|
175
|
+
show_database_structure = show_db_structure
|
174
176
|
|
175
177
|
def show_fields(self, model, field_names=None, languages=None):
|
176
178
|
model = dd.resolve_model(model)
|
lino/utils/html.py
CHANGED
@@ -8,6 +8,7 @@ Some HTML utilities for Lino.
|
|
8
8
|
import types
|
9
9
|
from lxml import etree
|
10
10
|
from etgen.html import E, to_rst, fromstring, iselement, join_elems, forcetext, lines2p
|
11
|
+
from etgen import html as xghtml
|
11
12
|
|
12
13
|
# from etgen.html import tostring as et_tostring
|
13
14
|
from html2text import HTML2Text
|
@@ -15,6 +16,7 @@ from django.utils.html import SafeString, mark_safe, escape, format_html
|
|
15
16
|
# from lino.utils import tostring
|
16
17
|
|
17
18
|
SAFE_EMPTY = mark_safe("")
|
19
|
+
PLAIN_PAGE_LENGTH = 15
|
18
20
|
|
19
21
|
|
20
22
|
def html2text(html, **kwargs):
|
@@ -108,3 +110,104 @@ class Grouper:
|
|
108
110
|
return SAFE_EMPTY
|
109
111
|
self.last_values = self.current_values
|
110
112
|
return self.ar.actor.after_group_change(self, obj)
|
113
|
+
|
114
|
+
|
115
|
+
def table2html(ar, as_main=True):
|
116
|
+
"""Represent the given table request as an HTML table.
|
117
|
+
|
118
|
+
`ar` is the request to be rendered, an instance of
|
119
|
+
:class:`lino.core.requests.ActionRequest`.
|
120
|
+
|
121
|
+
The returned HTML enclosed in a ``<div>`` tag and generated using
|
122
|
+
:mod:`etgen.html`.
|
123
|
+
|
124
|
+
If `as_main` is True, include additional elements such as a paging
|
125
|
+
toolbar. (This argument is currently being ignored.)
|
126
|
+
|
127
|
+
"""
|
128
|
+
# as_main = True
|
129
|
+
t = xghtml.Table()
|
130
|
+
t.attrib.update(**{"class": "table table-striped table-hover"})
|
131
|
+
if ar.limit is None:
|
132
|
+
ar.limit = PLAIN_PAGE_LENGTH
|
133
|
+
pglen = ar.limit
|
134
|
+
if ar.offset is None:
|
135
|
+
page = 1
|
136
|
+
else:
|
137
|
+
"""
|
138
|
+
(assuming pglen is 5)
|
139
|
+
offset page
|
140
|
+
0 1
|
141
|
+
5 2
|
142
|
+
"""
|
143
|
+
page = int(old_div(ar.offset, pglen)) + 1
|
144
|
+
|
145
|
+
ar.dump2html(t, ar.sliced_data_iterator, header_links=as_main)
|
146
|
+
if not as_main:
|
147
|
+
url = ar.get_request_url() or "#" # open in own window
|
148
|
+
return E.div(
|
149
|
+
E.div(
|
150
|
+
E.div(
|
151
|
+
E.a(
|
152
|
+
E.span(**{"class": "glyphicon glyphicon-folder-open"}),
|
153
|
+
href=url,
|
154
|
+
style="margin-left: 4px;",
|
155
|
+
**{"class": "btn btn-default pull-right"},
|
156
|
+
),
|
157
|
+
E.h5(str(ar.get_title()), style="display: inline-block;"),
|
158
|
+
**{"class": "panel-title"},
|
159
|
+
),
|
160
|
+
**{"class": "panel-heading"},
|
161
|
+
),
|
162
|
+
t.as_element(),
|
163
|
+
style="display: inline-block;",
|
164
|
+
**{"class": "panel panel-default"},
|
165
|
+
)
|
166
|
+
|
167
|
+
buttons = []
|
168
|
+
kw = dict()
|
169
|
+
kw = {}
|
170
|
+
if pglen != PLAIN_PAGE_LENGTH:
|
171
|
+
kw[constants.URL_PARAM_LIMIT] = pglen
|
172
|
+
|
173
|
+
if page > 1:
|
174
|
+
kw[constants.URL_PARAM_START] = pglen * (page - 2)
|
175
|
+
prev_url = ar.get_request_url(**kw)
|
176
|
+
kw[constants.URL_PARAM_START] = 0
|
177
|
+
first_url = ar.get_request_url(**kw)
|
178
|
+
else:
|
179
|
+
prev_url = None
|
180
|
+
first_url = None
|
181
|
+
buttons.append(("<<", _("First page"), first_url))
|
182
|
+
buttons.append(("<", _("Previous page"), prev_url))
|
183
|
+
|
184
|
+
next_start = pglen * page
|
185
|
+
if next_start < ar.get_total_count():
|
186
|
+
kw[constants.URL_PARAM_START] = next_start
|
187
|
+
next_url = ar.get_request_url(**kw)
|
188
|
+
last_page = int(old_div((ar.get_total_count() - 1), pglen))
|
189
|
+
kw[constants.URL_PARAM_START] = pglen * last_page
|
190
|
+
last_url = ar.get_request_url(**kw)
|
191
|
+
else:
|
192
|
+
next_url = None
|
193
|
+
last_url = None
|
194
|
+
buttons.append((">", _("Next page"), next_url))
|
195
|
+
buttons.append((">>", _("Last page"), last_url))
|
196
|
+
|
197
|
+
return E.div(buttons2pager(buttons), t.as_element())
|
198
|
+
|
199
|
+
|
200
|
+
def layout2html(ar, elem):
|
201
|
+
wl = ar.bound_action.get_window_layout()
|
202
|
+
if wl is None:
|
203
|
+
raise Exception("{!r} has no window layout".format(ar.bound_action))
|
204
|
+
# ~ print 20120901, wl.main
|
205
|
+
lh = wl.get_layout_handle()
|
206
|
+
|
207
|
+
items = list(lh.main.as_plain_html(ar, elem))
|
208
|
+
# if navigator:
|
209
|
+
# items.insert(0, navigator)
|
210
|
+
# ~ print tostring(E.div())
|
211
|
+
# ~ if len(items) == 0: return ""
|
212
|
+
return E.form(*items)
|
213
|
+
# ~ print 20120901, lh.main.__html__(ar)
|
lino/utils/mldbc/mixins.py
CHANGED
@@ -30,7 +30,7 @@ class BabelNamed(model.Model):
|
|
30
30
|
|
31
31
|
"""
|
32
32
|
|
33
|
-
class Meta
|
33
|
+
class Meta:
|
34
34
|
abstract = True
|
35
35
|
|
36
36
|
name = BabelCharField(max_length=200, verbose_name=_("Designation"))
|
@@ -58,7 +58,7 @@ class BabelDesignated(model.Model):
|
|
58
58
|
|
59
59
|
"""
|
60
60
|
|
61
|
-
class Meta
|
61
|
+
class Meta:
|
62
62
|
abstract = True
|
63
63
|
|
64
64
|
designation = BabelCharField(max_length=200, verbose_name=_("Designation"))
|
lino/utils/soup.py
CHANGED
@@ -22,6 +22,8 @@ from bs4 import BeautifulSoup, NavigableString, Comment, Doctype
|
|
22
22
|
from django.conf import settings
|
23
23
|
|
24
24
|
MORE_INDICATOR = "..."
|
25
|
+
MORE_MARKER = "=MORE="
|
26
|
+
DATA_UPLOAD_ID = "data-upload_id"
|
25
27
|
|
26
28
|
URL_REGEX = re.compile(
|
27
29
|
r'([^"]|^)(https?:\/\/)((www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*))'
|
@@ -251,10 +253,11 @@ ALLOWED_TAGS = frozenset([
|
|
251
253
|
|
252
254
|
GENERALLY_ALLOWED_ATTRS = {"title", "style", "class"}
|
253
255
|
|
256
|
+
|
254
257
|
# Map of allowed attributes by tag. Originally copied from bleach.sanitizer.
|
255
258
|
ALLOWED_ATTRIBUTES = {
|
256
259
|
"a": {"href", "target"} | GENERALLY_ALLOWED_ATTRS,
|
257
|
-
"img": {"src", "alt"} | GENERALLY_ALLOWED_ATTRS,
|
260
|
+
"img": {"src", "alt", "width", "height", DATA_UPLOAD_ID} | GENERALLY_ALLOWED_ATTRS,
|
258
261
|
}
|
259
262
|
|
260
263
|
ALLOWED_ATTRIBUTES["span"] = GENERALLY_ALLOWED_ATTRS | {
|
@@ -281,7 +284,7 @@ def register_sanitizer(func):
|
|
281
284
|
SANITIZERS.append(func)
|
282
285
|
|
283
286
|
|
284
|
-
def sanitized_soup(htmlstr):
|
287
|
+
def sanitized_soup(htmlstr, save=False, ar=None, mentions=None):
|
285
288
|
if not htmlstr.startswith("<"):
|
286
289
|
htmlstr = f"<p>{htmlstr}</p>"
|
287
290
|
htmlstr = url2a(htmlstr)
|
@@ -313,6 +316,9 @@ def sanitized_soup(htmlstr):
|
|
313
316
|
for comment in comments:
|
314
317
|
comment.extract()
|
315
318
|
|
319
|
+
for func in SANITIZERS:
|
320
|
+
func(soup, save=save, ar=ar, mentions=mentions)
|
321
|
+
|
316
322
|
# remove the wrapper tag if it is useless
|
317
323
|
# if len(soup.contents) == 1:
|
318
324
|
# main_tag = soup.contents[0]
|
@@ -322,22 +328,24 @@ def sanitized_soup(htmlstr):
|
|
322
328
|
return soup
|
323
329
|
|
324
330
|
|
325
|
-
def sanitize(htmlstr,
|
331
|
+
def sanitize(htmlstr, **kwargs):
|
332
|
+
# if len(chunks := htmlstr.split(MORE_MARKER, 1)) == 2:
|
333
|
+
# htmlstr = " ".join(chunks)
|
326
334
|
htmlstr = htmlstr.strip()
|
327
335
|
if htmlstr == "":
|
328
336
|
return htmlstr
|
329
|
-
soup = sanitized_soup(htmlstr)
|
330
|
-
for func in SANITIZERS:
|
331
|
-
func(soup, save=save, ar=ar)
|
337
|
+
soup = sanitized_soup(htmlstr, **kwargs)
|
332
338
|
return str(soup).strip()
|
333
339
|
|
334
340
|
|
335
|
-
def truncate_comment(htmlstr, max_length=300):
|
341
|
+
def truncate_comment(htmlstr, max_length=300, **kwargs):
|
336
342
|
# new implementation since 20230713
|
343
|
+
if len(chunks := htmlstr.split(MORE_MARKER, 1)) == 2:
|
344
|
+
htmlstr = chunks[0]
|
337
345
|
htmlstr = htmlstr.strip() # remove leading or trailing newlines
|
338
346
|
if htmlstr == '':
|
339
347
|
return htmlstr
|
340
|
-
soup = sanitized_soup(htmlstr)
|
348
|
+
soup = sanitized_soup(htmlstr, **kwargs)
|
341
349
|
tc = TextCollector(max_length)
|
342
350
|
tc.add_chunk(soup)
|
343
351
|
return tc.text.strip()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: lino
|
3
|
-
Version: 25.
|
3
|
+
Version: 25.9.0
|
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
|