lino 25.4.2__py3-none-any.whl → 25.4.4__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/core/kernel.py +33 -7
- lino/core/renderer.py +3 -3
- lino/core/site.py +10 -36
- lino/help_texts.py +3 -3
- lino/management/commands/demotest.py +16 -22
- lino/mixins/__init__.py +5 -5
- lino/mixins/dupable.py +2 -4
- lino/mixins/registrable.py +5 -2
- lino/modlib/about/models.py +2 -2
- lino/modlib/checkdata/choicelists.py +4 -4
- lino/modlib/checkdata/models.py +11 -3
- lino/modlib/comments/fixtures/demo2.py +4 -0
- lino/modlib/comments/models.py +1 -1
- lino/modlib/dupable/mixins.py +3 -5
- lino/modlib/extjs/ext_renderer.py +1 -1
- lino/modlib/extjs/views.py +1 -1
- lino/modlib/help/fixtures/demo2.py +3 -2
- lino/modlib/jinja/mixins.py +18 -5
- lino/modlib/linod/models.py +1 -1
- lino/modlib/linod/routing.py +49 -46
- lino/modlib/memo/mixins.py +3 -2
- lino/modlib/notify/api.py +33 -14
- lino/modlib/notify/mixins.py +4 -3
- lino/modlib/printing/mixins.py +1 -1
- lino/modlib/publisher/choicelists.py +41 -20
- lino/modlib/publisher/config/publisher/page.pub.html +24 -0
- lino/modlib/publisher/fixtures/demo2.py +12 -0
- lino/modlib/publisher/fixtures/std.py +2 -1
- lino/modlib/publisher/fixtures/synodalworld.py +17 -0
- lino/modlib/publisher/mixins.py +7 -0
- lino/modlib/publisher/models.py +77 -7
- lino/modlib/publisher/ui.py +5 -5
- lino/modlib/publisher/views.py +9 -2
- lino/modlib/system/models.py +1 -1
- lino/modlib/uploads/models.py +2 -2
- lino/static/bootstrap.css +1 -1
- lino/utils/dbfreader.py +64 -32
- lino/utils/dbhash.py +3 -2
- {lino-25.4.2.dist-info → lino-25.4.4.dist-info}/METADATA +1 -1
- {lino-25.4.2.dist-info → lino-25.4.4.dist-info}/RECORD +44 -43
- lino/management/commands/monitor.py +0 -160
- {lino-25.4.2.dist-info → lino-25.4.4.dist-info}/WHEEL +0 -0
- {lino-25.4.2.dist-info → lino-25.4.4.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.4.2.dist-info → lino-25.4.4.dist-info}/licenses/COPYING +0 -0
lino/modlib/linod/routing.py
CHANGED
@@ -1,67 +1,70 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
|
-
# Copyright 2022 Rumma & Ko Ltd
|
2
|
+
# Copyright 2022-2025 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
|
-
from django.
|
5
|
+
from django.conf import settings
|
6
6
|
from django.core.asgi import get_asgi_application
|
7
|
-
from django.utils.functional import LazyObject
|
8
7
|
|
9
|
-
|
10
|
-
from channels.db import database_sync_to_async
|
11
|
-
from channels.sessions import SessionMiddlewareStack
|
12
|
-
from channels.routing import ProtocolTypeRouter, URLRouter, ChannelNameRouter
|
8
|
+
if settings.SITE.plugins.linod.use_channels:
|
13
9
|
|
14
|
-
from
|
15
|
-
from
|
10
|
+
from django.urls import re_path
|
11
|
+
from django.utils.functional import LazyObject
|
12
|
+
from channels.middleware import BaseMiddleware
|
13
|
+
from channels.db import database_sync_to_async
|
14
|
+
from channels.sessions import SessionMiddlewareStack
|
15
|
+
from channels.routing import ProtocolTypeRouter, URLRouter, ChannelNameRouter
|
16
16
|
|
17
|
-
from .
|
18
|
-
from .consumers import
|
17
|
+
from lino.core.auth import get_user
|
18
|
+
from lino.modlib.notify.consumers import ClientConsumer
|
19
19
|
|
20
|
+
from .utils import CHANNEL_NAME
|
21
|
+
from .consumers import LinodConsumer
|
20
22
|
|
21
|
-
class UserLazyObject(LazyObject):
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
class UserLazyObject(LazyObject):
|
24
|
+
"""
|
25
|
+
Throw a more useful error message when scope['user'] is accessed before it's resolved
|
26
|
+
"""
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
+
def _setup(self):
|
29
|
+
raise ValueError("Accessing scope user before it is ready.")
|
28
30
|
|
31
|
+
async def _get_user(scope):
|
32
|
+
class Wrapper:
|
33
|
+
def __init__(self, session):
|
34
|
+
self.session = session
|
29
35
|
|
30
|
-
|
31
|
-
|
32
|
-
def __init__(self, session):
|
33
|
-
self.session = session
|
36
|
+
r = Wrapper(scope["session"])
|
37
|
+
return await database_sync_to_async(get_user)(r)
|
34
38
|
|
35
|
-
|
36
|
-
|
39
|
+
class AuthMiddleware(BaseMiddleware):
|
40
|
+
def populate_scope(self, scope):
|
41
|
+
# Make sure we have a session
|
42
|
+
if "session" not in scope:
|
43
|
+
raise ValueError("AuthMiddleware cannot find session in scope.")
|
44
|
+
# Add it to the scope if it's not there already
|
45
|
+
if "user" not in scope:
|
46
|
+
scope["user"] = UserLazyObject()
|
37
47
|
|
48
|
+
async def resolve_scope(self, scope):
|
49
|
+
scope["user"]._wrapped = await _get_user(scope)
|
38
50
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
raise ValueError("AuthMiddleware cannot find session in scope.")
|
44
|
-
# Add it to the scope if it's not there already
|
45
|
-
if "user" not in scope:
|
46
|
-
scope["user"] = UserLazyObject()
|
51
|
+
async def __call__(self, scope, receive=None, send=None):
|
52
|
+
self.populate_scope(scope)
|
53
|
+
await self.resolve_scope(scope)
|
54
|
+
return await self.inner(scope, receive, send)
|
47
55
|
|
48
|
-
|
49
|
-
scope["user"]._wrapped = await _get_user(scope)
|
56
|
+
routes = [re_path(r"^WS/$", ClientConsumer.as_asgi())]
|
50
57
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
58
|
+
protocol_mapping = dict(
|
59
|
+
websocket=SessionMiddlewareStack(AuthMiddleware(URLRouter(routes))),
|
60
|
+
channel=ChannelNameRouter({CHANNEL_NAME: LinodConsumer.as_asgi()}),
|
61
|
+
http=get_asgi_application(),
|
62
|
+
)
|
55
63
|
|
64
|
+
application = ProtocolTypeRouter(protocol_mapping)
|
56
65
|
|
57
|
-
|
66
|
+
# raise Exception("20240424")
|
58
67
|
|
59
|
-
|
60
|
-
websocket=SessionMiddlewareStack(AuthMiddleware(URLRouter(routes))),
|
61
|
-
channel=ChannelNameRouter({CHANNEL_NAME: LinodConsumer.as_asgi()}),
|
62
|
-
http=get_asgi_application(),
|
63
|
-
)
|
68
|
+
else:
|
64
69
|
|
65
|
-
application =
|
66
|
-
|
67
|
-
# raise Exception("20240424")
|
70
|
+
application = get_asgi_application()
|
lino/modlib/memo/mixins.py
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
5
|
from lxml.html import fragments_fromstring
|
6
|
-
from lino.utils.html import E, tostring, mark_safe
|
7
6
|
import lxml
|
8
7
|
|
9
8
|
try:
|
@@ -19,6 +18,7 @@ from django.utils.html import format_html
|
|
19
18
|
from lino.core.gfks import gfk2lookup
|
20
19
|
from lino.core.model import Model
|
21
20
|
from lino.core.fields import fields_list, RichTextField, PreviewTextField
|
21
|
+
from lino.utils.html import E, tostring, mark_safe
|
22
22
|
from lino.utils.restify import restify
|
23
23
|
from lino.utils.soup import truncate_comment
|
24
24
|
from lino.utils.mldbc.fields import BabelTextField
|
@@ -33,6 +33,7 @@ def django_truncate_comment(html_str):
|
|
33
33
|
settings.SITE.plugins.memo.short_preview_length, html=True
|
34
34
|
)
|
35
35
|
|
36
|
+
|
36
37
|
MARKDOWNCFG = dict(
|
37
38
|
extensions=["toc"], extension_configs=dict(toc=dict(toc_depth=3, permalink=True))
|
38
39
|
)
|
@@ -293,7 +294,7 @@ class PreviewableChecker(Checker):
|
|
293
294
|
obj.save()
|
294
295
|
# self.synchronize_mentions(mentions)
|
295
296
|
|
296
|
-
def get_checkdata_problems(self, obj, fix=False):
|
297
|
+
def get_checkdata_problems(self, ar, obj, fix=False):
|
297
298
|
for x in self._get_checkdata_problems(settings.SITE.DEFAULT_LANGUAGE, obj, fix):
|
298
299
|
yield x
|
299
300
|
if isinstance(obj, BabelPreviewable):
|
lino/modlib/notify/api.py
CHANGED
@@ -15,6 +15,8 @@ LIVE_PANEL_UPDATE = "PANEL_UPDATE"
|
|
15
15
|
|
16
16
|
NOTIFICATION_TYPES = [NOTIFICATION, CHAT, LIVE_PANEL_UPDATE]
|
17
17
|
|
18
|
+
DONT_CATCH = True
|
19
|
+
|
18
20
|
|
19
21
|
# def send_panel_update(actorIDs: list[str], pk: int):
|
20
22
|
def send_panel_update(data):
|
@@ -27,16 +29,24 @@ def send_panel_update(data):
|
|
27
29
|
from channels.layers import get_channel_layer
|
28
30
|
from asgiref.sync import async_to_sync
|
29
31
|
|
32
|
+
if settings.SITE.loading_from_dump:
|
33
|
+
return
|
34
|
+
|
30
35
|
channel_layer = get_channel_layer()
|
31
36
|
|
32
37
|
data.update(type=LIVE_PANEL_UPDATE)
|
33
38
|
|
34
|
-
|
39
|
+
if DONT_CATCH:
|
35
40
|
async_to_sync(channel_layer.send)(
|
36
41
|
CHANNEL_NAME, {"type": "send.panel.update", "text": json.dumps(data)}
|
37
42
|
)
|
38
|
-
|
39
|
-
|
43
|
+
else:
|
44
|
+
try:
|
45
|
+
async_to_sync(channel_layer.send)(
|
46
|
+
CHANNEL_NAME, {"type": "send.panel.update", "text": json.dumps(data)}
|
47
|
+
)
|
48
|
+
except Exception as e:
|
49
|
+
logger.exception(e)
|
40
50
|
|
41
51
|
|
42
52
|
def send_notification(
|
@@ -54,11 +64,9 @@ def send_notification(
|
|
54
64
|
OK button of their desktop notification.
|
55
65
|
|
56
66
|
"""
|
57
|
-
if user is None:
|
67
|
+
if user is None or settings.SITE.loading_from_dump:
|
58
68
|
return
|
59
69
|
|
60
|
-
created = created.strftime("%a %d %b %Y %H:%M")
|
61
|
-
|
62
70
|
if dd.get_plugin_setting("linod", "use_channels"):
|
63
71
|
# importing channels at module level would cause certain things to fail
|
64
72
|
# when channels isn't installed, e.g. `manage.py prep` in `lino_book.projects.workflows`.
|
@@ -73,7 +81,7 @@ def send_notification(
|
|
73
81
|
subject=subject,
|
74
82
|
id=primary_key,
|
75
83
|
body=body,
|
76
|
-
created=created,
|
84
|
+
created=created.strftime("%a %d %b %Y %H:%M"),
|
77
85
|
action_url=action_url,
|
78
86
|
)
|
79
87
|
|
@@ -86,6 +94,7 @@ def send_notification(
|
|
86
94
|
},
|
87
95
|
) # data
|
88
96
|
except Exception as e:
|
97
|
+
raise # 20250415
|
89
98
|
logger.exception(e)
|
90
99
|
|
91
100
|
if dd.plugins.notify.use_push_api:
|
@@ -95,7 +104,7 @@ def send_notification(
|
|
95
104
|
body=body,
|
96
105
|
action_title=action_title,
|
97
106
|
)
|
98
|
-
|
107
|
+
if DONT_CATCH:
|
99
108
|
async_to_sync(channel_layer.send)(
|
100
109
|
CHANNEL_NAME,
|
101
110
|
{
|
@@ -104,9 +113,19 @@ def send_notification(
|
|
104
113
|
"user_id": user.id if user is not None else None,
|
105
114
|
},
|
106
115
|
)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
116
|
+
else:
|
117
|
+
try:
|
118
|
+
async_to_sync(channel_layer.send)(
|
119
|
+
CHANNEL_NAME,
|
120
|
+
{
|
121
|
+
"type": "send.push",
|
122
|
+
"data": data,
|
123
|
+
"user_id": user.id if user is not None else None,
|
124
|
+
},
|
125
|
+
)
|
126
|
+
except (
|
127
|
+
ChannelFull
|
128
|
+
) as e: # happens on older Pythons and can cause many tracebacks
|
129
|
+
# logger.exception(e)
|
130
|
+
# logger.warning(str(e))
|
131
|
+
pass
|
lino/modlib/notify/mixins.py
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
# Copyright 2016-
|
1
|
+
# Copyright 2016-2025 Rumma & Ko Ltd
|
2
2
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
3
3
|
|
4
4
|
from lino.utils.html import E, tostring, format_html, mark_safe
|
5
|
+
from lino.core.utils import full_model_name as fmn
|
5
6
|
from lino.api import dd, rt, _
|
6
7
|
|
7
8
|
# PUBLIC_GROUP = "all_users_channel"
|
@@ -11,13 +12,13 @@ def get_updatables(instance, ar=None):
|
|
11
12
|
data = {
|
12
13
|
"actorIDs": instance.updatable_panels,
|
13
14
|
"pk": instance.pk,
|
14
|
-
"model": f"{instance
|
15
|
+
"model": f"{fmn(instance)}",
|
15
16
|
"mk": None, "master_model": None
|
16
17
|
}
|
17
18
|
if ar is None:
|
18
19
|
return data
|
19
20
|
if mi := ar.master_instance:
|
20
|
-
data.update(mk=mi.pk, master_model=f"{mi
|
21
|
+
data.update(mk=mi.pk, master_model=f"{fmn(mi)}")
|
21
22
|
return data
|
22
23
|
|
23
24
|
|
lino/modlib/printing/mixins.py
CHANGED
@@ -260,7 +260,7 @@ class CachedPrintableChecker(Checker):
|
|
260
260
|
model = CachedPrintable
|
261
261
|
verbose_name = _("Check for missing target files")
|
262
262
|
|
263
|
-
def get_checkdata_problems(self, obj, fix=False):
|
263
|
+
def get_checkdata_problems(self, ar, obj, fix=False):
|
264
264
|
if obj.build_time is not None:
|
265
265
|
t = obj.get_cache_mtime()
|
266
266
|
if t is None:
|
@@ -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
|
import os
|
@@ -8,7 +8,7 @@ from copy import copy
|
|
8
8
|
from django.db import models
|
9
9
|
from django.conf import settings
|
10
10
|
from django.utils import translation
|
11
|
-
from django.utils.text import format_lazy
|
11
|
+
# from django.utils.text import format_lazy
|
12
12
|
|
13
13
|
from lino.api import dd, rt, _
|
14
14
|
from lino.utils.html import E, tostring, join_elems
|
@@ -181,19 +181,40 @@ class SpecialPage(dd.Choice):
|
|
181
181
|
# pointing_field_name = 'publisher.Page.special_page'
|
182
182
|
# show_values = True
|
183
183
|
|
184
|
-
def __init__(self,
|
185
|
-
self.
|
186
|
-
|
184
|
+
def __init__(self, name, text=None, parent=None, **kwargs):
|
185
|
+
self.parent_value = parent
|
186
|
+
self._default_values = dict()
|
187
|
+
for k in ("ref", "title", "filler", "body"):
|
187
188
|
if k in kwargs:
|
188
|
-
self.
|
189
|
-
super().__init__(
|
190
|
-
if
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
189
|
+
self._default_values[k] = kwargs.pop(k)
|
190
|
+
super().__init__(name, text, name, **kwargs)
|
191
|
+
# if (filler := self.default_values.get('filler', None)):
|
192
|
+
# if "title" not in self.default_values:
|
193
|
+
# self.default_values["title"] = filler.data_view.get_actor_label()
|
194
|
+
# else:
|
195
|
+
# if "title" not in self.default_values:
|
196
|
+
# self.default_values["title"] = self.text
|
197
|
+
|
198
|
+
def on_page_created(self, obj):
|
199
|
+
for k, v in self._default_values.items():
|
200
|
+
setattr(obj, k, v)
|
201
|
+
if obj.filler and not obj.title:
|
202
|
+
obj.title = obj.filler.data_view.get_actor_label()
|
203
|
+
if not obj.title:
|
204
|
+
obj.title = self.text or "20250422"
|
205
|
+
if self.parent_value:
|
206
|
+
psp = self.choicelist.get_by_value(self.parent_value)
|
207
|
+
obj.parent = psp.get_object()
|
208
|
+
|
209
|
+
# def get_object(self, ar):
|
210
|
+
def get_object(self):
|
211
|
+
language = translation.get_language()
|
212
|
+
# if len(settings.SITE.languages) == 1:
|
213
|
+
# language = translation.get_language()
|
214
|
+
# else:
|
215
|
+
# language = ar.request.LANGUAGE_CODE
|
216
|
+
# return rt.models.publisher.Page.objects.get(ref=self.defaul_values['ref'], language=language)
|
217
|
+
return rt.models.publisher.Page.objects.get(special_page=self, language=language)
|
197
218
|
|
198
219
|
|
199
220
|
class SpecialPages(dd.ChoiceList):
|
@@ -227,9 +248,9 @@ class SpecialPages(dd.ChoiceList):
|
|
227
248
|
|
228
249
|
add = SpecialPages.add_item
|
229
250
|
|
230
|
-
add("
|
231
|
-
add("
|
232
|
-
add("
|
233
|
-
add("
|
234
|
-
add("
|
235
|
-
add("
|
251
|
+
add("home", _("Home"), ref="index", body=_("Welcome to our great website."))
|
252
|
+
add("terms", _("Terms and conditions"))
|
253
|
+
add("privacy", _("Privacy policy"))
|
254
|
+
add("cookies", _("Cookie settings"))
|
255
|
+
add("copyright", _("Copyright"))
|
256
|
+
add("about", _("About us"))
|
@@ -32,6 +32,30 @@
|
|
32
32
|
</div>
|
33
33
|
{% endblock %}
|
34
34
|
|
35
|
+
{% block navbar %}
|
36
|
+
<nav class="navbar navbar-default" role="navigation">
|
37
|
+
<div class="container-fluid">
|
38
|
+
{% set index_node, home_children = obj.home_and_children(ar) %}
|
39
|
+
<div class="navbar-header">
|
40
|
+
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-navbar-collapsible-content" aria-expanded="false">
|
41
|
+
<span class="sr-only">Toggle navigation</span>
|
42
|
+
<span class="icon-bar"></span>
|
43
|
+
<span class="icon-bar"></span>
|
44
|
+
<span class="icon-bar"></span>
|
45
|
+
</button>
|
46
|
+
<a class="navbar-brand" href="{{ar.get_home_url()}}">{{ index_node.title }}</a>
|
47
|
+
</div>
|
48
|
+
<div class="collapse navbar-collapse" id="bs-navbar-collapsible-content">
|
49
|
+
<ul class="nav navbar-nav">
|
50
|
+
{% for child in home_children %}
|
51
|
+
<li>{{tostring(ar.obj2html(child))}}</li>
|
52
|
+
{% endfor %}
|
53
|
+
</ul>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
</nav>
|
57
|
+
{% endblock %}
|
58
|
+
|
35
59
|
{% block content %}
|
36
60
|
<div class="row-fluid">
|
37
61
|
{% for chunk in obj.as_page(ar) %}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
from asgiref.sync import async_to_sync
|
2
|
+
from django.conf import settings
|
3
|
+
from lino.modlib.linod.choicelists import Procedures
|
4
|
+
from lino.api import rt
|
5
|
+
|
6
|
+
|
7
|
+
def objects():
|
8
|
+
yield None
|
9
|
+
ar = rt.login("robin")
|
10
|
+
if True: # settings.SITE.use_linod:
|
11
|
+
Procedures.update_publisher_pages.run(ar)
|
12
|
+
# async_to_sync(Procedures.update_publisher_pages.run)(ar)
|
@@ -18,10 +18,11 @@ def objects():
|
|
18
18
|
with translation.override(lng.django_code):
|
19
19
|
kwargs = dict(language=lng.django_code, special_page=sp)
|
20
20
|
kwargs.update(publishing_state="published")
|
21
|
-
kwargs.update(sp.default_values)
|
21
|
+
# kwargs.update(sp.default_values)
|
22
22
|
if lng.suffix:
|
23
23
|
kwargs.update(translated_from=translated_from)
|
24
24
|
obj = Page(**kwargs)
|
25
|
+
sp.on_page_created(obj)
|
25
26
|
yield obj
|
26
27
|
if not lng.suffix:
|
27
28
|
translated_from = obj
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from lino.api import rt, _
|
2
|
+
|
3
|
+
home_children = [
|
4
|
+
(_("Mission"), None, []),
|
5
|
+
(_("Maxim"), None, []),
|
6
|
+
(_("Propaganda"), None, []),
|
7
|
+
(_("About us"), None, [
|
8
|
+
(_("Team"), None, []),
|
9
|
+
(_("History"), None, []),
|
10
|
+
(_("Contact"), None, []),
|
11
|
+
(_("Terms & conditions"), None, []),
|
12
|
+
]),
|
13
|
+
]
|
14
|
+
|
15
|
+
|
16
|
+
def objects():
|
17
|
+
return rt.models.publisher.make_demo_pages(home_children)
|
lino/modlib/publisher/mixins.py
CHANGED
@@ -7,6 +7,7 @@ from lino.utils.html import tostring
|
|
7
7
|
from lino.api import dd, rt, _
|
8
8
|
|
9
9
|
from django import http
|
10
|
+
from django.db import models
|
10
11
|
from django.conf import settings
|
11
12
|
from django.utils.translation import get_language
|
12
13
|
from lino.core.renderer import add_user_language
|
@@ -124,6 +125,11 @@ class Publishable(Printable):
|
|
124
125
|
# # context = dict(obj=self, request=request, language=get_language())
|
125
126
|
# return template.render(**context)
|
126
127
|
|
128
|
+
def home_and_children(self, ar):
|
129
|
+
home = rt.models.publisher.SpecialPages.home.get_object()
|
130
|
+
return home, rt.models.publisher.Page.objects.filter(parent=home)
|
131
|
+
# return dv.model.objects.filter(models.Q(parent=index_node) | models.Q(ref='index'), language=language)
|
132
|
+
|
127
133
|
def get_publisher_response(self, ar):
|
128
134
|
if not self.is_public():
|
129
135
|
return http.HttpResponseNotFound(
|
@@ -147,6 +153,7 @@ class PublishableContent(Publishable):
|
|
147
153
|
language = dd.LanguageField()
|
148
154
|
publishing_state = PublishingStates.field(default="draft")
|
149
155
|
filler = PageFillers.field(blank=True, null=True)
|
156
|
+
main_image = dd.ForeignKey('uploads.Upload', blank=True, null=True, verbose_name=_("Main image"))
|
150
157
|
|
151
158
|
def get_print_language(self):
|
152
159
|
return self.language
|
lino/modlib/publisher/models.py
CHANGED
@@ -2,12 +2,6 @@
|
|
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 *
|
6
|
-
from lino.api import rt, dd
|
7
|
-
|
8
|
-
from .choicelists import PublishingStates, PageFillers, SpecialPages
|
9
|
-
from .mixins import Publishable
|
10
|
-
|
11
5
|
from html import escape
|
12
6
|
from django.db import models
|
13
7
|
from django.http import HttpResponseRedirect
|
@@ -15,6 +9,11 @@ from django.conf import settings
|
|
15
9
|
from django.utils import translation
|
16
10
|
from django.utils.translation import pgettext_lazy
|
17
11
|
|
12
|
+
from lorem import get_paragraph
|
13
|
+
from django.utils import translation
|
14
|
+
from django.conf import settings
|
15
|
+
|
16
|
+
|
18
17
|
# from django.utils.translation import get_language
|
19
18
|
from django.utils.html import mark_safe
|
20
19
|
|
@@ -38,6 +37,10 @@ from lino.mixins.polymorphic import Polymorphic
|
|
38
37
|
from lino_xl.lib.topics.mixins import Taggable
|
39
38
|
# from .utils import render_node
|
40
39
|
|
40
|
+
from lino.api import rt, dd
|
41
|
+
from .choicelists import PublishingStates, PageFillers, SpecialPages
|
42
|
+
from .mixins import Publishable
|
43
|
+
from .ui import *
|
41
44
|
|
42
45
|
# class Node(Referrable, Hierarchical, Sequenced, Previewable, Publishable, Commentable):
|
43
46
|
# Polymorphic,
|
@@ -105,6 +108,17 @@ class Page(
|
|
105
108
|
# def as_story_item(self, ar, **kwargs):
|
106
109
|
# return "".join(self.as_page(ar, **kwargs))
|
107
110
|
|
111
|
+
def as_paragraph(self, ar):
|
112
|
+
title = E.b(escape(self.title))
|
113
|
+
url = ar.obj2url(self)
|
114
|
+
if url is not None:
|
115
|
+
title = E.a(title, href=url, style="text-decoration: none; color: black;")
|
116
|
+
body = self.get_body_parsed(ar, short=True)
|
117
|
+
if body:
|
118
|
+
body = " - " + body
|
119
|
+
item = E.li(title, body)
|
120
|
+
return tostring(item)
|
121
|
+
|
108
122
|
def toc_html(self, ar, max_depth=1):
|
109
123
|
def li(obj):
|
110
124
|
# return "<li>{}</li>".format(obj.memo2html(ar, str(obj)))
|
@@ -131,7 +145,7 @@ class Page(
|
|
131
145
|
else:
|
132
146
|
title = "<b>{}</b> — ".format(escape(self.title))
|
133
147
|
title += self.get_body_parsed(ar, short=True)
|
134
|
-
title = "<li>{}</
|
148
|
+
title = "<li>{}</li>".format(title)
|
135
149
|
# edit_url = ar.renderer.obj2url(ar, self)
|
136
150
|
# url = self.publisher_url(ar)
|
137
151
|
# print("20231029", ar.renderer)
|
@@ -146,6 +160,16 @@ class Page(
|
|
146
160
|
|
147
161
|
# if not self.is_public():
|
148
162
|
# return
|
163
|
+
if hlevel == 1 and self.main_image:
|
164
|
+
yield f"""
|
165
|
+
<div class="row">
|
166
|
+
<div class="center-block">
|
167
|
+
<a href="#" class="thumbnail">
|
168
|
+
<img src="{self.main_image.get_media_file().get_image_url()}">
|
169
|
+
</a>
|
170
|
+
</div>
|
171
|
+
</div>
|
172
|
+
"""
|
149
173
|
|
150
174
|
if display_mode in ("detail",):
|
151
175
|
info = self.get_node_info(ar)
|
@@ -401,3 +425,49 @@ def update_publisher_pages(ar):
|
|
401
425
|
prev = obj
|
402
426
|
count += 1
|
403
427
|
ar.logger.info("%d pages have been updated.", count)
|
428
|
+
|
429
|
+
|
430
|
+
def make_demo_pages(pages_desc):
|
431
|
+
# Translation = rt.models.pages.Translation
|
432
|
+
# for lc in settings.SITE.LANGUAGE_CHOICES:
|
433
|
+
# language = lc[0]
|
434
|
+
# kwargs = dict(language=language, ref='index')
|
435
|
+
# with translation.override(language):
|
436
|
+
|
437
|
+
parent_nodes = []
|
438
|
+
for lng in settings.SITE.languages:
|
439
|
+
counter = {None: 0}
|
440
|
+
# count = 0
|
441
|
+
home_page = Page.objects.get(
|
442
|
+
special_page=SpecialPages.home, language=lng.django_code)
|
443
|
+
|
444
|
+
with translation.override(lng.django_code):
|
445
|
+
|
446
|
+
def make_pages(pages, parent=None):
|
447
|
+
for page in pages:
|
448
|
+
if len(page) != 3:
|
449
|
+
raise Exception(f"Oops {page}")
|
450
|
+
title, body, children = page
|
451
|
+
kwargs = dict(title=title)
|
452
|
+
if body is None:
|
453
|
+
kwargs.update(body=get_paragraph())
|
454
|
+
else:
|
455
|
+
kwargs.update(body=body)
|
456
|
+
if parent is None:
|
457
|
+
# kwargs.update(ref='index')
|
458
|
+
continue # home page is created by SpecialPages
|
459
|
+
if lng.suffix:
|
460
|
+
kwargs.update(
|
461
|
+
translated_from=parent_nodes[counter[None]])
|
462
|
+
kwargs.update(language=lng.django_code)
|
463
|
+
if dd.is_installed("publisher"):
|
464
|
+
kwargs.update(publishing_state='published')
|
465
|
+
obj = Page(parent=parent, **kwargs)
|
466
|
+
yield obj
|
467
|
+
if not lng.suffix:
|
468
|
+
parent_nodes.append(obj)
|
469
|
+
counter[None] += 1
|
470
|
+
# print("20230324", title, kwargs)
|
471
|
+
yield make_pages(children, obj)
|
472
|
+
|
473
|
+
yield make_pages(pages_desc, parent=home_page)
|
lino/modlib/publisher/ui.py
CHANGED
@@ -92,7 +92,7 @@ else:
|
|
92
92
|
|
93
93
|
|
94
94
|
class PageDetail(dd.DetailLayout):
|
95
|
-
main = "first_panel
|
95
|
+
main = "general first_panel more"
|
96
96
|
|
97
97
|
first_panel = dd.Panel(
|
98
98
|
"""
|
@@ -129,11 +129,11 @@ class PageDetail(dd.DetailLayout):
|
|
129
129
|
# """
|
130
130
|
|
131
131
|
right_panel = """
|
132
|
-
ref
|
132
|
+
ref language
|
133
133
|
parent seqno
|
134
|
-
child_node_depth
|
134
|
+
child_node_depth main_image
|
135
135
|
#page_type filler
|
136
|
-
|
136
|
+
publishing_state special_page
|
137
137
|
publisher.TranslationsByPage
|
138
138
|
"""
|
139
139
|
|
@@ -147,7 +147,7 @@ class Pages(dd.Table):
|
|
147
147
|
ref
|
148
148
|
#page_type filler
|
149
149
|
"""
|
150
|
-
default_display_modes = {None: constants.
|
150
|
+
default_display_modes = {None: constants.DISPLAY_MODE_LIST}
|
151
151
|
|
152
152
|
|
153
153
|
class PagesByParent(Pages):
|
lino/modlib/publisher/views.py
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
from django.conf import settings
|
6
6
|
from django import http
|
7
7
|
from django.views.generic import View
|
8
|
+
from django.utils import translation
|
8
9
|
|
9
10
|
from lino.api import dd
|
10
11
|
from lino.core import auth
|
@@ -52,7 +53,13 @@ class Index(View):
|
|
52
53
|
def get(self, request, pk=1):
|
53
54
|
rnd = settings.SITE.plugins.publisher.renderer
|
54
55
|
dv = settings.SITE.models.publisher.Pages
|
55
|
-
|
56
|
+
if len(settings.SITE.languages) == 1:
|
57
|
+
# language = settings.SITE.languages[0].django_code
|
58
|
+
language = translation.get_language()
|
59
|
+
else:
|
60
|
+
language = request.LANGUAGE_CODE
|
61
|
+
index_node = dv.model.objects.get(ref="index", language=language)
|
56
62
|
# print("20231025", index_node)
|
57
|
-
ar = dv.create_request(request=request, renderer=rnd,
|
63
|
+
ar = dv.create_request(request=request, renderer=rnd,
|
64
|
+
selected_rows=[index_node])
|
58
65
|
return index_node.get_publisher_response(ar)
|
lino/modlib/system/models.py
CHANGED
@@ -326,7 +326,7 @@ class BleachChecker(Checker):
|
|
326
326
|
if len(m._bleached_fields):
|
327
327
|
yield m
|
328
328
|
|
329
|
-
def get_checkdata_problems(self, obj, fix=False):
|
329
|
+
def get_checkdata_problems(self, ar, obj, fix=False):
|
330
330
|
t = tuple(obj.fields_to_bleach(save=False))
|
331
331
|
if len(t):
|
332
332
|
fldnames = ", ".join([f.name for f, old, new in t])
|