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
@@ -1,33 +1,29 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
2
|
# Copyright 2009-2020 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
|
-
"""Views for `lino.modlib.
|
4
|
+
"""Views for `lino.modlib.bootstrap5`.
|
5
5
|
|
6
6
|
"""
|
7
|
-
from past.utils import old_div
|
8
7
|
|
9
8
|
from django import http
|
10
9
|
from django.conf import settings
|
11
10
|
from django.views.generic import View
|
12
|
-
from django.core import exceptions
|
13
11
|
from django.utils.translation import gettext as _
|
14
12
|
from django.utils.translation import get_language
|
15
13
|
from django.utils.decorators import method_decorator
|
16
14
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
17
|
-
|
18
15
|
# from django.contrib import auth
|
19
16
|
from lino.core import auth
|
20
|
-
|
21
17
|
# from lino.api import dd
|
22
18
|
from lino.core import constants
|
23
|
-
|
24
19
|
# from lino.core import auth
|
25
20
|
from lino.core.requests import BaseRequest
|
26
21
|
from lino.core.tables import AbstractTable
|
27
22
|
from lino.core.views import action_request
|
28
23
|
from lino.core.utils import navinfo
|
29
24
|
from lino.utils.html import E, tostring
|
30
|
-
|
25
|
+
|
26
|
+
raise Exception("No longer used since 20250826")
|
31
27
|
|
32
28
|
PLAIN_PAGE_LENGTH = 15
|
33
29
|
|
@@ -42,12 +38,12 @@ def http_response(ar, tplname, context):
|
|
42
38
|
menu = MENUS.get(k, None)
|
43
39
|
if menu is None:
|
44
40
|
menu = settings.SITE.get_site_menu(u.user_type)
|
45
|
-
|
41
|
+
bs5 = settings.SITE.plugins.bootstrap5
|
46
42
|
if False: # 20150803 home button now in base.html
|
47
|
-
assert
|
48
|
-
url =
|
43
|
+
assert bs5.renderer is not None
|
44
|
+
url = bs5.build_plain_url()
|
49
45
|
menu.add_url_button(url, label=_("Home"))
|
50
|
-
e =
|
46
|
+
e = bs5.renderer.show_menu(ar, menu)
|
51
47
|
menu = tostring(e)
|
52
48
|
MENUS[k] = menu
|
53
49
|
context.update(menu=menu)
|
@@ -78,113 +74,12 @@ def buttons2pager(buttons, title=None):
|
|
78
74
|
return E.ul(*items, **{"class": "pagination pagination-sm"})
|
79
75
|
|
80
76
|
|
81
|
-
def table2html(ar, as_main=True):
|
82
|
-
"""Represent the given table request as an HTML table.
|
83
|
-
|
84
|
-
`ar` is the request to be rendered, an instance of
|
85
|
-
:class:`lino.core.requests.ActionRequest`.
|
86
|
-
|
87
|
-
The returned HTML enclosed in a ``<div>`` tag and generated using
|
88
|
-
:mod:`etgen.html`.
|
89
|
-
|
90
|
-
If `as_main` is True, include additional elements such as a paging
|
91
|
-
toolbar. (This argument is currently being ignored.)
|
92
|
-
|
93
|
-
"""
|
94
|
-
# as_main = True
|
95
|
-
t = xghtml.Table()
|
96
|
-
t.attrib.update(**{"class": "table table-striped table-hover"})
|
97
|
-
if ar.limit is None:
|
98
|
-
ar.limit = PLAIN_PAGE_LENGTH
|
99
|
-
pglen = ar.limit
|
100
|
-
if ar.offset is None:
|
101
|
-
page = 1
|
102
|
-
else:
|
103
|
-
"""
|
104
|
-
(assuming pglen is 5)
|
105
|
-
offset page
|
106
|
-
0 1
|
107
|
-
5 2
|
108
|
-
"""
|
109
|
-
page = int(old_div(ar.offset, pglen)) + 1
|
110
|
-
|
111
|
-
ar.dump2html(t, ar.sliced_data_iterator, header_links=as_main)
|
112
|
-
if not as_main:
|
113
|
-
url = ar.get_request_url() or "#" # open in own window
|
114
|
-
return E.div(
|
115
|
-
E.div(
|
116
|
-
E.div(
|
117
|
-
E.a(
|
118
|
-
E.span(**{"class": "glyphicon glyphicon-folder-open"}),
|
119
|
-
href=url,
|
120
|
-
style="margin-left: 4px;",
|
121
|
-
**{"class": "btn btn-default pull-right"},
|
122
|
-
),
|
123
|
-
E.h5(str(ar.get_title()), style="display: inline-block;"),
|
124
|
-
**{"class": "panel-title"},
|
125
|
-
),
|
126
|
-
**{"class": "panel-heading"},
|
127
|
-
),
|
128
|
-
t.as_element(),
|
129
|
-
style="display: inline-block;",
|
130
|
-
**{"class": "panel panel-default"},
|
131
|
-
)
|
132
|
-
|
133
|
-
buttons = []
|
134
|
-
kw = dict()
|
135
|
-
kw = {}
|
136
|
-
if pglen != PLAIN_PAGE_LENGTH:
|
137
|
-
kw[constants.URL_PARAM_LIMIT] = pglen
|
138
|
-
|
139
|
-
if page > 1:
|
140
|
-
kw[constants.URL_PARAM_START] = pglen * (page - 2)
|
141
|
-
prev_url = ar.get_request_url(**kw)
|
142
|
-
kw[constants.URL_PARAM_START] = 0
|
143
|
-
first_url = ar.get_request_url(**kw)
|
144
|
-
else:
|
145
|
-
prev_url = None
|
146
|
-
first_url = None
|
147
|
-
buttons.append(("<<", _("First page"), first_url))
|
148
|
-
buttons.append(("<", _("Previous page"), prev_url))
|
149
|
-
|
150
|
-
next_start = pglen * page
|
151
|
-
if next_start < ar.get_total_count():
|
152
|
-
kw[constants.URL_PARAM_START] = next_start
|
153
|
-
next_url = ar.get_request_url(**kw)
|
154
|
-
last_page = int(old_div((ar.get_total_count() - 1), pglen))
|
155
|
-
kw[constants.URL_PARAM_START] = pglen * last_page
|
156
|
-
last_url = ar.get_request_url(**kw)
|
157
|
-
else:
|
158
|
-
next_url = None
|
159
|
-
last_url = None
|
160
|
-
buttons.append((">", _("Next page"), next_url))
|
161
|
-
buttons.append((">>", _("Last page"), last_url))
|
162
|
-
|
163
|
-
return E.div(buttons2pager(buttons), t.as_element())
|
164
|
-
|
165
|
-
|
166
|
-
def layout2html(ar, elem):
|
167
|
-
wl = ar.bound_action.get_window_layout()
|
168
|
-
if wl is None:
|
169
|
-
raise Exception("{!r} has no window layout".format(ar.bound_action))
|
170
|
-
# ~ print 20120901, wl.main
|
171
|
-
lh = wl.get_layout_handle()
|
172
|
-
|
173
|
-
items = list(lh.main.as_plain_html(ar, elem))
|
174
|
-
# if navigator:
|
175
|
-
# items.insert(0, navigator)
|
176
|
-
# ~ print tostring(E.div())
|
177
|
-
# ~ if len(items) == 0: return ""
|
178
|
-
return E.form(*items)
|
179
|
-
# ~ print 20120901, lh.main.__html__(ar)
|
180
|
-
|
181
|
-
|
182
77
|
class List(View):
|
183
78
|
"""Render a list of records."""
|
184
79
|
|
185
80
|
def get(self, request, app_label=None, actor=None):
|
186
81
|
ar = action_request(app_label, actor, request, request.GET, True)
|
187
|
-
ar.renderer = settings.SITE.plugins.
|
82
|
+
ar.renderer = settings.SITE.plugins.bootstrap5.renderer
|
188
83
|
|
189
84
|
context = dict(
|
190
85
|
title=ar.get_title(),
|
@@ -207,7 +102,7 @@ class Element(View):
|
|
207
102
|
def get(self, request, app_label=None, actor=None, pk=None):
|
208
103
|
# print(request, app_label, actor, pk)
|
209
104
|
ar = action_request(app_label, actor, request, request.GET, False)
|
210
|
-
ar.renderer = settings.SITE.plugins.
|
105
|
+
ar.renderer = settings.SITE.plugins.bootstrap5.renderer
|
211
106
|
|
212
107
|
navigator = None
|
213
108
|
if pk and pk != "-99999" and pk != "-99998":
|
@@ -317,7 +212,7 @@ class Index(View):
|
|
317
212
|
def get(self, request, *args, **kw):
|
318
213
|
# raise Exception("20171122 {} {}".format(
|
319
214
|
# get_language(), settings.MIDDLEWARE_CLASSES))
|
320
|
-
ui = settings.SITE.plugins.
|
215
|
+
ui = settings.SITE.plugins.bootstrap5
|
321
216
|
# print("20170607", request.user)
|
322
217
|
# assert ui.renderer is not None
|
323
218
|
ar = BaseRequest(
|
@@ -329,7 +224,7 @@ class Index(View):
|
|
329
224
|
|
330
225
|
|
331
226
|
def index_response(ar):
|
332
|
-
ui = settings.SITE.plugins.
|
227
|
+
ui = settings.SITE.plugins.bootstrap5
|
333
228
|
main = settings.SITE.get_main_html(ar, front_end=ui)
|
334
229
|
main = ui.renderer.html_text(main)
|
335
230
|
context = dict(
|
@@ -341,4 +236,4 @@ def index_response(ar):
|
|
341
236
|
# else:
|
342
237
|
# user = request.subst_user or request.user
|
343
238
|
# context.update(ar=ar)
|
344
|
-
return http_response(ar, "
|
239
|
+
return http_response(ar, "bootstrap5/index.html", context)
|
lino/modlib/comments/mixins.py
CHANGED
@@ -102,9 +102,6 @@ class Commentable(MemoReferrable):
|
|
102
102
|
def get_rfc_description(self, ar):
|
103
103
|
return ""
|
104
104
|
|
105
|
-
# def get_comment_group(self):
|
106
|
-
# return None
|
107
|
-
|
108
105
|
if dd.is_installed("comments"):
|
109
106
|
|
110
107
|
def save_new_instance(self, ar):
|
@@ -123,12 +120,8 @@ class Commentable(MemoReferrable):
|
|
123
120
|
return _("Created new {model} {obj}.").format(
|
124
121
|
model=self.__class__._meta.verbose_name, obj=self)
|
125
122
|
|
126
|
-
# @classmethod
|
127
|
-
# def add_comments_filter(cls, qs, ar):
|
128
|
-
# return qs
|
129
|
-
|
130
123
|
def get_comment_group(self):
|
131
|
-
|
124
|
+
return None # dd.plugins.groups.get_default_group()
|
132
125
|
|
133
126
|
def on_create_comment(self, comment, ar):
|
134
127
|
pass
|
lino/modlib/comments/models.py
CHANGED
@@ -285,8 +285,10 @@ class Comment(
|
|
285
285
|
self.owner.on_create_comment(self, ar)
|
286
286
|
|
287
287
|
def get_default_group(self):
|
288
|
+
# implements PrivacyRelevant
|
288
289
|
if self.owner:
|
289
290
|
return self.owner.get_comment_group()
|
291
|
+
return super().get_default_group()
|
290
292
|
|
291
293
|
def after_ui_save(self, ar, cw):
|
292
294
|
super().after_ui_save(ar, cw)
|
lino/modlib/comments/ui.py
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
# Copyright 2013-2023 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
|
-
from lino.modlib.publisher.choicelists import PageFillers
|
6
5
|
from django.contrib.humanize.templatetags.humanize import naturaltime
|
7
6
|
from django.contrib.contenttypes.models import ContentType
|
8
7
|
from django.db import models
|
@@ -66,18 +65,22 @@ class CommentDetail(dd.DetailLayout):
|
|
66
65
|
|
67
66
|
more = dd.Panel(
|
68
67
|
"""
|
69
|
-
|
68
|
+
more1 more2
|
70
69
|
""",
|
71
70
|
label=_("More"),
|
72
71
|
)
|
73
72
|
|
74
|
-
|
73
|
+
more1 = """
|
75
74
|
id user group
|
76
75
|
owner_type owner_id
|
77
|
-
created modified
|
78
76
|
comment_type
|
79
77
|
ReactionsByComment
|
80
78
|
"""
|
79
|
+
more2 = """
|
80
|
+
created modified
|
81
|
+
memo.MentionsByTarget
|
82
|
+
memo.MentionsByOwner
|
83
|
+
"""
|
81
84
|
|
82
85
|
|
83
86
|
class Comments(dd.Table):
|
@@ -314,6 +317,3 @@ class Reactions(dd.Table):
|
|
314
317
|
class ReactionsByComment(Reactions):
|
315
318
|
master_key = "comment"
|
316
319
|
default_display_modes = {None: constants.DISPLAY_MODE_SUMMARY}
|
317
|
-
|
318
|
-
|
319
|
-
PageFillers.add_item(RecentComments)
|
lino/modlib/extjs/__init__.py
CHANGED
@@ -28,7 +28,8 @@ from django.utils.translation import gettext_lazy as _
|
|
28
28
|
class Plugin(Plugin):
|
29
29
|
"""Extends :class:`lino.core.plugin.Plugin`."""
|
30
30
|
|
31
|
-
needs_plugins = ["lino.modlib.
|
31
|
+
# needs_plugins = ["lino.modlib.bootstrap5"]
|
32
|
+
needs_plugins = ["lino.modlib.jinja"]
|
32
33
|
|
33
34
|
enter_submits_form = False
|
34
35
|
"""Whether the :kbd:`ENTER` key (or :kbd:`CTRL+ENTER` when in a
|
@@ -216,9 +217,6 @@ class Plugin(Plugin):
|
|
216
217
|
from django.urls import re_path as url
|
217
218
|
from . import views
|
218
219
|
|
219
|
-
# if self.site.developer_site_cache:
|
220
|
-
# self.renderer.build_site_cache()
|
221
|
-
|
222
220
|
rx = "^"
|
223
221
|
|
224
222
|
urlpatterns = [
|
@@ -130,7 +130,7 @@
|
|
130
130
|
{{ ln }}{% endfor -%}
|
131
131
|
{%- endfor -%}
|
132
132
|
{# Main Lino js code #}
|
133
|
-
{{ javascript(site.
|
133
|
+
{{ javascript(site.build_media_url(*ext_renderer.lino_js_parts())) }}
|
134
134
|
{# javascript(site.buildurl('linolib.js')) #}
|
135
135
|
{# ###### OnReady JS code ###### #}
|
136
136
|
<script type="text/javascript">
|
@@ -420,10 +420,7 @@ class ExtRenderer(JsCacheRenderer):
|
|
420
420
|
user = request.subst_user
|
421
421
|
|
422
422
|
def getit():
|
423
|
-
if
|
424
|
-
settings.SITE.developer_site_cache
|
425
|
-
and not settings.SITE.never_build_site_cache
|
426
|
-
):
|
423
|
+
if settings.SITE.developer_site_cache:
|
427
424
|
self.build_js_cache(False)
|
428
425
|
|
429
426
|
# Render template
|
@@ -1478,9 +1475,6 @@ class ExtRenderer(JsCacheRenderer):
|
|
1478
1475
|
)
|
1479
1476
|
yield "LANGUAGE_CHOICES = %s;" % py2js(list(settings.SITE.LANGUAGE_CHOICES))
|
1480
1477
|
yield "MEDIA_URL = %s;" % py2js(settings.SITE.build_media_url())
|
1481
|
-
# if settings.SITE.never_build_site_cache:
|
1482
|
-
# yield "GEN_TIMESTAMP = '%s';" % settings.SITE.kernel.lino_version
|
1483
|
-
# else:
|
1484
1478
|
yield "GEN_TIMESTAMP = %s;" % py2js(settings.SITE.kernel.lino_version)
|
1485
1479
|
|
1486
1480
|
return "\n".join(fn())
|
lino/modlib/extjs/views.py
CHANGED
@@ -597,11 +597,13 @@ class ApiList(View):
|
|
597
597
|
|
598
598
|
if True:
|
599
599
|
kw.update(title=str(ar.get_title()))
|
600
|
+
# kw.update(title=str(ar.get_breadcrumbs()))
|
600
601
|
else:
|
601
602
|
# 20190704 work in progress.
|
602
603
|
# add open_in_own_window button after title of slave panel
|
603
604
|
kw.update(
|
604
605
|
title=str(ar.get_title())
|
606
|
+
# title=str(ar.get_breadcrumbs())
|
605
607
|
+ " "
|
606
608
|
+ tostring(ar.open_in_own_window_button())
|
607
609
|
)
|
lino/modlib/help/models.py
CHANGED
@@ -28,17 +28,6 @@ class OpenHelpWindow(dd.Action):
|
|
28
28
|
help_text = _("Open Help Window")
|
29
29
|
show_in_plain = True
|
30
30
|
|
31
|
-
# def js_handler(self, actor):
|
32
|
-
# parts = ['cache', 'help']
|
33
|
-
# if get_language() != settings.SITE.DEFAULT_LANGUAGE.django_code:
|
34
|
-
# parts.append(get_language())
|
35
|
-
# parts.append(str(actor) + ".html")
|
36
|
-
# url = settings.SITE.build_site_cache_url(*parts)
|
37
|
-
# # return "let _ = window.open('%s');" % url
|
38
|
-
# # return "() => window.open('%s')" % url
|
39
|
-
# return "function (){window.open('%s')}" % url
|
40
|
-
# # return "window.open('%s')" % url
|
41
|
-
|
42
31
|
def run_from_ui(self, ar, **kwargs):
|
43
32
|
# print("20210612")
|
44
33
|
parts = ["cache", "help"]
|
@@ -46,7 +35,7 @@ class OpenHelpWindow(dd.Action):
|
|
46
35
|
parts.append(get_language())
|
47
36
|
parts.append(str(ar.actor) + ".html")
|
48
37
|
# parts.append("index.html")
|
49
|
-
url = settings.SITE.
|
38
|
+
url = settings.SITE.build_media_url(*parts)
|
50
39
|
ar.set_response(success=True)
|
51
40
|
ar.success(open_url=url)
|
52
41
|
|
lino/modlib/jinja/renderer.py
CHANGED
lino/modlib/linod/mixins.py
CHANGED
@@ -179,10 +179,11 @@ class Runnable(Sequenced, RecurrenceSet):
|
|
179
179
|
await ar.adebug("Terminated %s with warning %s", self, str(e))
|
180
180
|
self.message = out.getvalue()
|
181
181
|
except Exception as e:
|
182
|
+
tb = "".join(traceback.format_exception(e))
|
182
183
|
self.message = out.getvalue()
|
183
|
-
self.message += "\n" +
|
184
|
+
self.message += "\n" + tb
|
184
185
|
self.disabled = True
|
185
|
-
await ar.awarning("Disabled %s after exception
|
186
|
+
await ar.awarning("Disabled %s after exception:\n%s", self, tb)
|
186
187
|
# ar.warning("Disabled %s after exception %s", astr(self), e)
|
187
188
|
# now = await sync_to_async(dd.now)()
|
188
189
|
self.last_end_time = timezone.now()
|
lino/modlib/memo/__init__.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2008-
|
1
|
+
# Copyright 2008-2025 Rumma & Ko Ltd
|
2
2
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
3
3
|
"""See :doc:`/specs/memo`.
|
4
4
|
|
@@ -15,6 +15,7 @@ Adds functionality for using memo commands in your text fields.
|
|
15
15
|
# from importlib import import_module
|
16
16
|
# from rstgen.utils import py2url_txt
|
17
17
|
from lino.api import ad
|
18
|
+
from lino.core import constants
|
18
19
|
from .parser import Parser, split_name_rest
|
19
20
|
from lino.utils.html import tostring
|
20
21
|
|
@@ -46,7 +47,7 @@ class Plugin(ad.Plugin):
|
|
46
47
|
front_end = None
|
47
48
|
# front_end = 'extjs'
|
48
49
|
# front_end = 'lino_react.react'
|
49
|
-
# front_end = '
|
50
|
+
# front_end = 'bootstrap5'
|
50
51
|
"""The front end to use when writing previews.
|
51
52
|
|
52
53
|
If this is `None`, Lino will use the default :term:`front end`
|
@@ -94,20 +95,20 @@ class Plugin(ad.Plugin):
|
|
94
95
|
kwargs = dict() # , nosummary=True)
|
95
96
|
dv = self.site.models.resolve(s)
|
96
97
|
sar = dv.create_request(parent=ar, limit=dv.preview_limit)
|
97
|
-
|
98
|
-
#
|
99
|
-
|
100
|
-
|
101
|
-
|
98
|
+
return sar.show(**kwargs)
|
99
|
+
# kwargs.update(display_mode=constants.DISPLAY_MODE_STORY)
|
100
|
+
# return sar.show_story(**kwargs)
|
101
|
+
# rv = ""
|
102
|
+
# # rv += "20230325 [show {}]".format(dv)
|
103
|
+
# for e in sar.renderer.table2story(sar, **kwargs):
|
104
|
+
# rv += tostring(e)
|
105
|
+
# return rv
|
102
106
|
|
103
107
|
self.parser.register_command("show", show2html)
|
104
108
|
|
105
109
|
if False:
|
106
110
|
# letting website users execute arbitrary code is a security risk
|
107
111
|
def eval2html(ar, s, cmdname, mentions, context):
|
108
|
-
from django.conf import settings # context of exec command
|
109
|
-
|
110
|
-
sar = ar.spawn_request(renderer=settings.SITE.kernel.html_renderer)
|
111
112
|
return eval(compile(s, cmdname, "eval"))
|
112
113
|
|
113
114
|
self.parser.register_command("eval", eval2html)
|
@@ -147,7 +148,6 @@ class Plugin(ad.Plugin):
|
|
147
148
|
# break
|
148
149
|
|
149
150
|
def get_patterns(self):
|
150
|
-
# from django.conf.urls import url
|
151
151
|
from django.urls import re_path as url
|
152
152
|
from . import views
|
153
153
|
|
lino/modlib/memo/mixins.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
|
-
# Copyright 2016-
|
2
|
+
# Copyright 2016-2025 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
|
+
# import difflib
|
6
|
+
# import sys
|
5
7
|
from lxml.html import fragments_fromstring
|
6
8
|
import lxml
|
7
9
|
|
@@ -19,6 +21,7 @@ from lino.core.fields import RichTextField, PreviewTextField
|
|
19
21
|
from lino.utils.html import E, tostring, mark_safe
|
20
22
|
from lino.utils.restify import restify
|
21
23
|
from lino.utils.soup import truncate_comment
|
24
|
+
from lino.utils.soup import MORE_MARKER
|
22
25
|
from lino.utils.mldbc.fields import BabelTextField
|
23
26
|
from lino.modlib.checkdata.choicelists import Checker
|
24
27
|
from lino.api import rt, dd, _
|
@@ -130,6 +133,7 @@ class MemoReferrable(dd.Model):
|
|
130
133
|
|
131
134
|
# class BasePreviewable(MentionGenerator):
|
132
135
|
class BasePreviewable(dd.Model):
|
136
|
+
|
133
137
|
class Meta:
|
134
138
|
abstract = True
|
135
139
|
|
@@ -138,12 +142,13 @@ class BasePreviewable(dd.Model):
|
|
138
142
|
def get_preview_length(self):
|
139
143
|
return settings.SITE.plugins.memo.short_preview_length
|
140
144
|
|
145
|
+
# def after_ui_save(self, ar, watcher):
|
141
146
|
def save(self, *args, **kwargs):
|
142
147
|
"""Updates the preview fields and the list of mentioned objects."""
|
143
|
-
pf = self.previewable_field
|
144
148
|
mentions = set()
|
149
|
+
pf = self.previewable_field
|
145
150
|
txt = self.get_previewable_text(settings.SITE.DEFAULT_LANGUAGE)
|
146
|
-
short, full = self.parse_previews(txt, None, mentions)
|
151
|
+
short, full = self.parse_previews(txt, None, mentions, True)
|
147
152
|
# if "choose one or the other" in short:
|
148
153
|
# raise Exception("20230928 {} {}".format(len(short), short))
|
149
154
|
# print("20231023 b", short)
|
@@ -154,23 +159,30 @@ class BasePreviewable(dd.Model):
|
|
154
159
|
src = self.get_previewable_text(lng)
|
155
160
|
# src = getattr(self, pf + lng.suffix)
|
156
161
|
with translation.override(lng.django_code):
|
157
|
-
short, full = self.parse_previews(src, None, mentions)
|
162
|
+
short, full = self.parse_previews(src, None, mentions, True)
|
158
163
|
setattr(self, pf + "_short_preview" + lng.suffix, short)
|
159
164
|
setattr(self, pf + "_full_preview" + lng.suffix, full)
|
165
|
+
|
166
|
+
# self.save() # yes this causes a second save()
|
160
167
|
super().save(*args, **kwargs)
|
168
|
+
# super().after_ui_save(ar, watcher)
|
161
169
|
self.synchronize_mentions(mentions)
|
162
170
|
|
163
171
|
def get_previewable_text(self, lng):
|
164
172
|
return getattr(self, self.previewable_field + lng.suffix)
|
165
173
|
|
166
|
-
def parse_previews(
|
174
|
+
def parse_previews(
|
175
|
+
self, source, ar=None, mentions=None, save=False, **context):
|
167
176
|
context.update(self=self)
|
168
177
|
full = settings.SITE.plugins.memo.parser.parse(
|
169
|
-
source, ar=ar,
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
178
|
+
source, ar=ar, mentions=mentions, context=context)
|
179
|
+
short = truncate_comment(
|
180
|
+
full, self.get_preview_length(),
|
181
|
+
ar=ar, save=save, mentions=mentions)
|
182
|
+
if len(chunks := full.split(MORE_MARKER, 1)) == 2:
|
183
|
+
full = " ".join(chunks)
|
184
|
+
if settings.SITE.plugins.memo.use_markup:
|
185
|
+
if not full.startswith("<"):
|
174
186
|
full = markdown.markdown(full, **MARKDOWNCFG)
|
175
187
|
return (short, full)
|
176
188
|
|
@@ -201,7 +213,7 @@ class BasePreviewable(dd.Model):
|
|
201
213
|
for e in lxml.html.fragments_fromstring(self.body_short_preview):
|
202
214
|
yield e
|
203
215
|
except Exception as e:
|
204
|
-
yield "{} [{}]"
|
216
|
+
yield f"{self.body_short_preview} [{e}]"
|
205
217
|
|
206
218
|
|
207
219
|
class Previewable(BasePreviewable):
|
@@ -218,20 +230,21 @@ class Previewable(BasePreviewable):
|
|
218
230
|
|
219
231
|
def get_body_parsed(self, ar, short=False):
|
220
232
|
if ar.renderer is settings.SITE.kernel.editing_front_end.renderer:
|
221
|
-
return
|
233
|
+
return mark_safe(
|
234
|
+
self.body_short_preview if short else self.body_full_preview)
|
222
235
|
# raise Exception("{} is not {}".format(
|
223
236
|
# ar.renderer, settings.SITE.kernel.editing_front_end.renderer))
|
224
237
|
src = self.body
|
225
|
-
s, f = self.parse_previews(src, ar,
|
226
|
-
return s if short else f
|
238
|
+
s, f = self.parse_previews(src, ar, None, False)
|
239
|
+
return mark_safe(s if short else f)
|
227
240
|
|
228
241
|
def as_paragraph(self, ar):
|
229
242
|
s = super().as_paragraph(ar)
|
230
243
|
# s = format_html("<b>{}</b> : {}", .format(ar.add_detail_link(self, str(self)))
|
231
244
|
# s = ar.obj2htmls(self)
|
232
245
|
s = format_html(
|
233
|
-
"<b>{}</b> : {}",
|
234
|
-
|
246
|
+
"<b>{}</b> : {}",
|
247
|
+
s, mark_safe(self.body_short_preview) or _("(no preview)"))
|
235
248
|
return s
|
236
249
|
|
237
250
|
|
@@ -267,13 +280,16 @@ class PreviewableChecker(Checker):
|
|
267
280
|
pf = obj.previewable_field
|
268
281
|
# src = getattr(obj, pf+suffix)
|
269
282
|
expected_mentions = set()
|
270
|
-
short, full = obj.parse_previews(src, None, expected_mentions)
|
283
|
+
short, full = obj.parse_previews(src, None, expected_mentions, False)
|
271
284
|
is_broken = False
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
):
|
285
|
+
stored_short = getattr(obj, pf + "_short_preview" + lng.suffix)
|
286
|
+
stored_full = getattr(obj, pf + "_full_preview" + lng.suffix)
|
287
|
+
if stored_short != short or stored_full != full:
|
276
288
|
yield (True, _("Preview differs from source."))
|
289
|
+
# print(f"20250908 found_full is {stored_full}, to expected: {full}")
|
290
|
+
# # print("20250908 found_full to expected_full:")
|
291
|
+
# sys.stdout.writelines(difflib.unified_diff(
|
292
|
+
# stored_full.splitlines(), full.splitlines()))
|
277
293
|
is_broken = True
|
278
294
|
found_mentions = set([obj.target for obj in obj.get_saved_mentions()])
|
279
295
|
if expected_mentions != found_mentions:
|
@@ -284,6 +300,7 @@ class PreviewableChecker(Checker):
|
|
284
300
|
# setattr(obj, pf+'_full_preview'+suffix, full)
|
285
301
|
obj.full_clean()
|
286
302
|
obj.save()
|
303
|
+
# obj.after_ui_save(ar, None)
|
287
304
|
# self.synchronize_mentions(mentions)
|
288
305
|
|
289
306
|
def get_checkdata_problems(self, ar, obj, fix=False):
|
lino/modlib/memo/models.py
CHANGED
@@ -23,7 +23,8 @@ target_label = _("Target")
|
|
23
23
|
|
24
24
|
|
25
25
|
class Mention(Controllable):
|
26
|
-
|
26
|
+
|
27
|
+
class Meta:
|
27
28
|
app_label = "memo"
|
28
29
|
abstract = dd.is_abstract_model(__name__, "Mention")
|
29
30
|
verbose_name = _("Mention")
|
@@ -85,12 +86,14 @@ class Mentions(dd.Table):
|
|
85
86
|
# id comment owner created
|
86
87
|
# """
|
87
88
|
|
88
|
-
|
89
|
-
|
90
|
-
#
|
91
|
-
#
|
92
|
-
|
93
|
-
|
89
|
+
|
90
|
+
class MentionsByOwner(Mentions):
|
91
|
+
# Not much used because when you are on the owner, you can see the mentions
|
92
|
+
# in the memo text
|
93
|
+
label = _("Mentions")
|
94
|
+
master_key = "owner"
|
95
|
+
column_names = "target *"
|
96
|
+
default_display_modes = {None: constants.DISPLAY_MODE_SUMMARY}
|
94
97
|
|
95
98
|
|
96
99
|
class MentionsByTarget(Mentions):
|
lino/modlib/memo/parser.py
CHANGED
@@ -19,7 +19,7 @@ from django.conf import settings
|
|
19
19
|
from lino import logger
|
20
20
|
|
21
21
|
import re
|
22
|
-
import
|
22
|
+
import traceback
|
23
23
|
from typing import Callable, Any
|
24
24
|
|
25
25
|
from bs4 import BeautifulSoup, MarkupResemblesLocatorWarning
|
@@ -313,6 +313,8 @@ All remaining arguments are used as the text of the link.
|
|
313
313
|
mo.end(),
|
314
314
|
)
|
315
315
|
# logger.debug(msg)
|
316
|
+
# print(msg, ":")
|
317
|
+
# traceback.print_exc()
|
316
318
|
return msg
|
317
319
|
|
318
320
|
def parse_suggestions(self, src):
|