lino 25.8.3__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/core/__init__.py +0 -1
- lino/core/actions.py +1 -1
- lino/core/actors.py +8 -0
- lino/core/elems.py +1 -1
- lino/core/fields.py +4 -1
- lino/core/model.py +2 -11
- lino/core/requests.py +8 -7
- lino/core/site.py +0 -79
- lino/core/user_types.py +1 -10
- lino/help_texts.py +1 -5
- lino/management/commands/initdb.py +0 -3
- lino/modlib/__init__.py +0 -1
- lino/modlib/bootstrap5/README.txt +1 -1
- lino/modlib/bootstrap5/__init__.py +34 -38
- lino/modlib/bootstrap5/config/bootstrap5/base.html +4 -0
- lino/modlib/bootstrap5/models.py +23 -23
- lino/modlib/bootstrap5/views.py +2 -107
- lino/modlib/checkdata/choicelists.py +1 -1
- lino/modlib/comments/fixtures/demo2.py +1 -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/linod/mixins.py +3 -2
- lino/modlib/memo/__init__.py +10 -9
- 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/publisher/__init__.py +12 -7
- lino/modlib/publisher/choicelists.py +9 -64
- lino/modlib/publisher/config/publisher/page.pub.html +73 -7
- lino/modlib/publisher/fixtures/std.py +14 -1
- lino/modlib/publisher/mixins.py +41 -12
- lino/modlib/publisher/models.py +74 -75
- lino/modlib/publisher/renderer.py +28 -12
- lino/modlib/publisher/ui.py +35 -35
- lino/modlib/publisher/views.py +59 -24
- 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/models.py +37 -36
- lino/modlib/weasyprint/choicelists.py +6 -0
- 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.3.dist-info → lino-25.9.0.dist-info}/METADATA +1 -1
- {lino-25.8.3.dist-info → lino-25.9.0.dist-info}/RECORD +61 -61
- {lino-25.8.3.dist-info → lino-25.9.0.dist-info}/WHEEL +0 -0
- {lino-25.8.3.dist-info → lino-25.9.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.8.3.dist-info → lino-25.9.0.dist-info}/licenses/COPYING +0 -0
@@ -1,10 +1,10 @@
|
|
1
1
|
{% extends "bootstrap5/base.html" %}
|
2
|
-
|
2
|
+
{% block title %}{{site.title or site.verbose_name}}{% endblock %}
|
3
3
|
{% block header %}
|
4
4
|
<div class="container-fluid lino-bs-header">
|
5
5
|
{% if ar -%}
|
6
6
|
{% if isinstance(obj, rt.models.publisher.Page) -%}
|
7
|
-
{{obj.get_prev_link(ar)}}
|
7
|
+
{{obj.get_prev_link(ar)}} {{obj.get_next_link(ar)}} |
|
8
8
|
{% endif -%}
|
9
9
|
{% if dd.is_installed('react') %}
|
10
10
|
{% set ar_react = obj.get_default_table().request(parent=ar, permalink_uris=True, renderer=dd.plugins.react.renderer) %}
|
@@ -18,9 +18,10 @@
|
|
18
18
|
{{_("Home")}}
|
19
19
|
{% endif -%}
|
20
20
|
{% if site.kernel.editing_front_end.url_prefix -%}
|
21
|
-
|
21
|
+
| <a href="/{{site.kernel.editing_front_end.url_prefix}}/">{{_("Admin")}}</a>
|
22
22
|
{% endif -%}
|
23
23
|
{% if len(site.languages) > 1 -%}
|
24
|
+
|
|
24
25
|
{% for lang in site.languages -%}
|
25
26
|
{% if lang.django_code == requested_language -%}
|
26
27
|
{{lang.django_code}}
|
@@ -29,26 +30,91 @@
|
|
29
30
|
{% endif -%}
|
30
31
|
{% endfor -%}
|
31
32
|
{% endif -%}
|
33
|
+
|
|
34
|
+
{% set user = ar.get_user() %}
|
35
|
+
{% if user.is_anonymous %}
|
36
|
+
<a href="" data-bs-toggle="modal" data-bs-target="#signinModal">
|
37
|
+
{{str(user)}}
|
38
|
+
</a>
|
39
|
+
{% else %}
|
40
|
+
{{str(user)}} <a href="/logout">{{_("Sign out")}}</a>
|
41
|
+
{% endif %}
|
32
42
|
{%- if site.kernel.admin_ui -%}
|
33
43
|
—
|
34
44
|
<a href="{{site.kernel.admin_ui.build_plain_url()}}/">{{site.kernel.admin_ui.ui_label}}</a>
|
35
45
|
{%- endif -%}
|
36
46
|
{% endif -%}
|
37
47
|
|
48
|
+
<div class="modal fade" id="signinModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
49
|
+
<div class="modal-dialog modal-dialog-centered">
|
50
|
+
<div class="modal-content">
|
51
|
+
<div class="modal-header">
|
52
|
+
<h1 class="modal-title fs-5" id="exampleModalLabel">{{_("Sign in form")}}</h1>
|
53
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
54
|
+
</div>
|
55
|
+
<div class="modal-body">
|
56
|
+
<form id="signinForm" action="/login" method="post">
|
57
|
+
<div id="signinError" class="alert alert-danger d-none" role="alert">
|
58
|
+
{{_("Incorrect username or password.")}}
|
59
|
+
</div>
|
60
|
+
<div class="mb-3">
|
61
|
+
<label for="signinInputUsername" class="form-label">{{_("Username")}}</label>
|
62
|
+
<input type="text" name="username" class="form-control" id="signinInputUsername" aria-describedby="usernameHelp" required="true">
|
63
|
+
<!-- <div id="usernameHelp" class="form-text">Enter your username.</div>-->
|
64
|
+
</div>
|
65
|
+
<div class="mb-3">
|
66
|
+
<label for="signinInputPassword" class="form-label">{{_("Password")}}</label>
|
67
|
+
<input type="password" name="password" class="form-control" id="signinInputPassword" required="true">
|
68
|
+
</div>
|
69
|
+
</form>
|
70
|
+
</div>
|
71
|
+
<div class="modal-footer">
|
72
|
+
<button form="signinForm" type="submit" class="btn btn-primary">{{_("Sign in")}}</button>
|
73
|
+
</div>
|
74
|
+
</div>
|
75
|
+
</div>
|
76
|
+
</div>
|
77
|
+
|
78
|
+
<script>
|
79
|
+
const form = document.getElementById("signinForm");
|
80
|
+
form.addEventListener("submit", (event) => {
|
81
|
+
event.preventDefault();
|
82
|
+
const formData = new FormData(form);
|
83
|
+
fetch(form.action, {
|
84
|
+
method: form.method,
|
85
|
+
body: formData
|
86
|
+
}).then((resp) => {
|
87
|
+
if (resp.status >= 400) {
|
88
|
+
const eel = document.getElementById("signinError");
|
89
|
+
eel.innerText = "Some unknown error occured, please contact site administrator";
|
90
|
+
eel.classList.remove("d-none")
|
91
|
+
return {success: false}
|
92
|
+
}
|
93
|
+
return resp.json()
|
94
|
+
}).then((resp) => {
|
95
|
+
if (resp.success) window.location.reload()
|
96
|
+
else {
|
97
|
+
const eel = document.getElementById("signinError");
|
98
|
+
eel.classList.remove("d-none")
|
99
|
+
}
|
100
|
+
})
|
101
|
+
});
|
102
|
+
</script>
|
103
|
+
|
38
104
|
</div>
|
39
105
|
{% endblock %}
|
40
106
|
|
41
107
|
{% block navbar %}
|
42
108
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
43
109
|
<div class="container-fluid">
|
44
|
-
{% set
|
45
|
-
<a class="navbar-brand" href="{{ar.obj2url(
|
110
|
+
{% set homepage, children = obj.home_and_children(ar) %}
|
111
|
+
<a class="navbar-brand" href="{{ar.obj2url(homepage)}}"><span class="pi pi-home"/></a>
|
46
112
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#bs-navbar-collapsible-content" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
47
113
|
<span class="navbar-toggler-icon"></span>
|
48
114
|
</button>
|
49
115
|
<div class="collapse navbar-collapse" id="bs-navbar-collapsible-content">
|
50
116
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
51
|
-
{% for child in
|
117
|
+
{% for child in children %}
|
52
118
|
<li class="nav-item">
|
53
119
|
<a class="nav-link" href="{{ar.obj2url(child)}}">{{str(child)}}</a>
|
54
120
|
</li>
|
@@ -60,7 +126,7 @@
|
|
60
126
|
{% endblock %}
|
61
127
|
|
62
128
|
{% block content %}
|
63
|
-
<div class="row-fluid">
|
129
|
+
<div class="row-fluid ql-editor">
|
64
130
|
{% for chunk in obj.as_page(ar) %}
|
65
131
|
{{ chunk }}
|
66
132
|
{% endfor %}
|
@@ -11,13 +11,26 @@ from lino.api import rt
|
|
11
11
|
|
12
12
|
|
13
13
|
def objects():
|
14
|
+
Tree = rt.models.publisher.Tree
|
14
15
|
Page = rt.models.publisher.Page
|
16
|
+
index = Tree(ref='index')
|
17
|
+
yield index
|
18
|
+
# lng2tree = dict()
|
19
|
+
# for lng in settings.SITE.languages:
|
20
|
+
# with translation.override(lng.django_code):
|
21
|
+
# kwargs = dict(language=lng.django_code, ref='index')
|
22
|
+
# obj = Tree(**kwargs)
|
23
|
+
# yield obj
|
24
|
+
# lng2tree[lng.django_code] = obj
|
25
|
+
|
15
26
|
for sp in SpecialPages.get_list_items():
|
16
27
|
translated_from = None
|
17
28
|
for lng in settings.SITE.languages:
|
18
29
|
with translation.override(lng.django_code):
|
19
|
-
|
30
|
+
# tree = lng2tree[lng.django_code]
|
31
|
+
kwargs = dict(publisher_tree=index, special_page=sp)
|
20
32
|
kwargs.update(publishing_state="published")
|
33
|
+
kwargs.update(language=lng.django_code)
|
21
34
|
# kwargs.update(sp.default_values)
|
22
35
|
if lng.suffix:
|
23
36
|
kwargs.update(translated_from=translated_from)
|
lino/modlib/publisher/mixins.py
CHANGED
@@ -12,9 +12,8 @@ from django import http
|
|
12
12
|
# from lino.utils import buildurl
|
13
13
|
from lino.utils.html import mark_safe
|
14
14
|
from lino.modlib.printing.mixins import Printable
|
15
|
-
from lino.modlib.users.mixins import PrivacyRelevant
|
16
15
|
from lino.api import dd, rt, _
|
17
|
-
from .choicelists import PublishingStates,
|
16
|
+
from .choicelists import PublishingStates, SpecialPages
|
18
17
|
# from .choicelists import PublisherViews, PublishingStates
|
19
18
|
|
20
19
|
|
@@ -46,9 +45,7 @@ class Publishable(Printable):
|
|
46
45
|
publisher_template = "publisher/page.pub.html"
|
47
46
|
_lino_publisher_location = None
|
48
47
|
|
49
|
-
|
50
|
-
"publisher.Page", null=True, blank=True,
|
51
|
-
verbose_name=_("Root page"), related_name='+')
|
48
|
+
publisher_tree = dd.ForeignKey("publisher.Tree", null=True, blank=True)
|
52
49
|
|
53
50
|
@dd.htmlbox()
|
54
51
|
def full_page(self, ar):
|
@@ -83,18 +80,21 @@ class Publishable(Printable):
|
|
83
80
|
# context.update(content=html)
|
84
81
|
tpl = dd.plugins.jinja.renderer.jinja_env.get_template(self.publisher_template)
|
85
82
|
return http.HttpResponse(
|
86
|
-
tpl.render(**context), content_type='text/html;charset="utf-8"'
|
87
|
-
)
|
83
|
+
tpl.render(**context), content_type='text/html;charset="utf-8"')
|
88
84
|
|
89
85
|
def home_and_children(self, ar):
|
90
|
-
home = self.root_page
|
86
|
+
# home = self.publisher_tree.root_page
|
87
|
+
Page = rt.models.publisher.Page
|
88
|
+
qs = Page.objects.filter(
|
89
|
+
publisher_tree=self.publisher_tree, parent__isnull=True)
|
90
|
+
home = qs.first()
|
91
91
|
if home is None:
|
92
92
|
home = SpecialPages.home.get_object()
|
93
|
-
return home,
|
93
|
+
return home, Page.objects.filter(parent=home)
|
94
94
|
# return dv.model.objects.filter(models.Q(parent=index_node) | models.Q(ref='index'), language=language)
|
95
95
|
|
96
96
|
|
97
|
-
class PublishableContent(Publishable
|
97
|
+
class PublishableContent(Publishable):
|
98
98
|
|
99
99
|
class Meta:
|
100
100
|
abstract = True
|
@@ -102,9 +102,12 @@ class PublishableContent(Publishable, PrivacyRelevant):
|
|
102
102
|
|
103
103
|
language = dd.LanguageField()
|
104
104
|
publishing_state = PublishingStates.field(default="draft")
|
105
|
-
filler = PageFillers.field(blank=True, null=True)
|
106
105
|
main_image = dd.ForeignKey('uploads.Upload', blank=True,
|
107
106
|
null=True, verbose_name=_("Main image"))
|
107
|
+
translated_from = dd.ForeignKey(
|
108
|
+
"self", verbose_name=_("Translated from"),
|
109
|
+
null=True, blank=True,
|
110
|
+
related_name="translated_to")
|
108
111
|
|
109
112
|
def get_print_language(self):
|
110
113
|
return self.language
|
@@ -118,6 +121,32 @@ class PublishableContent(Publishable, PrivacyRelevant):
|
|
118
121
|
super().on_duplicate(ar, master)
|
119
122
|
|
120
123
|
def is_public(self):
|
121
|
-
if self.private:
|
124
|
+
if self.publisher_tree.private:
|
122
125
|
return False
|
123
126
|
return self.publishing_state.is_public
|
127
|
+
|
128
|
+
def get_publisher_response(self, ar):
|
129
|
+
if ar and ar.request and self.language != ar.request.LANGUAGE_CODE:
|
130
|
+
rqlang = ar.request.LANGUAGE_CODE
|
131
|
+
# tt = rt.models.pages.Translation.objects.filter(
|
132
|
+
# parent=self, language=ar.request.LANGUAGE_CODE).first()
|
133
|
+
obj = None
|
134
|
+
if self.translated_from_id and self.translated_from.language == rqlang:
|
135
|
+
obj = self.translated_from
|
136
|
+
else:
|
137
|
+
sources = set([self.id])
|
138
|
+
p = self.translated_from
|
139
|
+
while p is not None:
|
140
|
+
sources.add(p.id)
|
141
|
+
p = p.translated_from
|
142
|
+
qs = self.__class__.objects.filter(
|
143
|
+
language=rqlang, translated_from__in=sources)
|
144
|
+
obj = qs.first()
|
145
|
+
# obj = self.translated_to.filter(language=rqlang).first()
|
146
|
+
# print("20231027 redirect to translation", tt.language, ar.request.LANGUAGE_CODE)
|
147
|
+
if obj is not None:
|
148
|
+
# print("20231028", self.language, "!=", ar.request.LANGUAGE_CODE, tt)
|
149
|
+
ar.selected_rows = [obj]
|
150
|
+
url = ar.get_request_url()
|
151
|
+
return http.HttpResponseRedirect(url)
|
152
|
+
return super().get_publisher_response(ar)
|
lino/modlib/publisher/models.py
CHANGED
@@ -5,28 +5,57 @@
|
|
5
5
|
from html import escape
|
6
6
|
from lorem import get_paragraph
|
7
7
|
from django.db import models
|
8
|
-
from django.http import HttpResponseRedirect
|
9
8
|
from django.conf import settings
|
10
9
|
from django.utils import translation
|
11
|
-
|
10
|
+
from django.utils.translation import get_language
|
12
11
|
from lino.api import dd, rt, _
|
13
12
|
# from lino.utils import mti
|
14
|
-
from lino.utils.html import E, tostring
|
13
|
+
from lino.utils.html import E, tostring, format_html
|
14
|
+
from lino.utils.instantiator import get_or_create
|
15
15
|
# from lino.core.renderer import add_user_language
|
16
16
|
# from lino.utils.mldbc.fields import LanguageField
|
17
|
-
from lino.mixins import Hierarchical, Sequenced
|
17
|
+
from lino.mixins import Hierarchical, Sequenced, Referrable
|
18
18
|
# from lino.modlib.summaries.mixins import Summarized
|
19
19
|
from lino.modlib.publisher.mixins import Publishable, PublishableContent
|
20
20
|
from lino.modlib.comments.mixins import Commentable
|
21
21
|
from lino.modlib.linod.choicelists import schedule_daily
|
22
22
|
from lino.modlib.memo.mixins import Previewable
|
23
|
+
from lino.modlib.users.mixins import PrivacyRelevant
|
23
24
|
from lino_xl.lib.topics.mixins import Taggable
|
24
25
|
|
25
|
-
from .choicelists import PublishingStates,
|
26
|
+
from .choicelists import PublishingStates, SpecialPages
|
26
27
|
from .mixins import Publishable
|
27
28
|
from .ui import *
|
28
29
|
|
29
30
|
|
31
|
+
class Tree(PrivacyRelevant, Referrable):
|
32
|
+
class Meta:
|
33
|
+
verbose_name = _("Tree")
|
34
|
+
verbose_name_plural = _("Trees")
|
35
|
+
abstract = dd.is_abstract_model(__name__, "Tree")
|
36
|
+
# unique_together = ["ref", "language"]
|
37
|
+
|
38
|
+
# ref = dd.CharField(_("Reference"), max_length=200, blank=True, null=True)
|
39
|
+
# root_page = dd.ForeignKey(
|
40
|
+
# "publisher.Page", null=True, blank=True,
|
41
|
+
# verbose_name=_("Root page"), related_name='+')
|
42
|
+
|
43
|
+
@dd.virtualfield(dd.ForeignKey(
|
44
|
+
'publisher.Page', verbose_name=_("Root page")))
|
45
|
+
def root_page(self, ar=None):
|
46
|
+
return self.get_root_page(get_language())
|
47
|
+
|
48
|
+
def get_root_page(self, language):
|
49
|
+
if self.pk is not None:
|
50
|
+
qs = Page.objects.filter(
|
51
|
+
parent__isnull=True, publisher_tree=self, language=language)
|
52
|
+
return qs.first()
|
53
|
+
# try:
|
54
|
+
# return Page.objects.get(parent=None, publisher_tree=self)
|
55
|
+
# except Page.DoesNotExist:
|
56
|
+
# return None
|
57
|
+
|
58
|
+
|
30
59
|
class Page(
|
31
60
|
Hierarchical, Sequenced, Previewable, Commentable, PublishableContent,
|
32
61
|
Taggable
|
@@ -39,31 +68,21 @@ class Page(
|
|
39
68
|
# unique_together = ["group", "ref", "language"]
|
40
69
|
# else:
|
41
70
|
# unique_together = ["ref", "language"]
|
42
|
-
unique_together = ["
|
71
|
+
# unique_together = ["publisher_tree", "language"]
|
43
72
|
|
44
73
|
memo_command = "page"
|
45
74
|
allow_cascaded_delete = ['parent']
|
46
75
|
|
47
|
-
ref = dd.CharField(_("Reference"), max_length=200, blank=True, null=True)
|
48
76
|
title = dd.CharField(_("Title"), max_length=250, blank=True)
|
49
77
|
child_node_depth = models.IntegerField(default=1)
|
50
|
-
# page_type = PageTypes.field(blank=True, null=True)
|
51
78
|
special_page = SpecialPages.field(blank=True)
|
52
79
|
|
53
|
-
translated_from = dd.ForeignKey(
|
54
|
-
"publisher.Page",
|
55
|
-
verbose_name=_("Translated from"),
|
56
|
-
null=True,
|
57
|
-
blank=True,
|
58
|
-
related_name="translated_to",
|
59
|
-
)
|
60
|
-
|
61
80
|
previous_page = dd.ForeignKey(
|
62
81
|
"self", null=True, blank=True, editable=False,
|
63
82
|
verbose_name=_("Previous page"), related_name='+')
|
64
83
|
|
65
84
|
def __str__(self):
|
66
|
-
return self.title or
|
85
|
+
return self.title or super().__str__()
|
67
86
|
|
68
87
|
# def on_create(self, ar):
|
69
88
|
# self.page_type = self.get_page_type()
|
@@ -81,11 +100,7 @@ class Page(
|
|
81
100
|
return ""
|
82
101
|
|
83
102
|
def is_public(self):
|
84
|
-
return not self.private
|
85
|
-
|
86
|
-
# def full_clean(self):
|
87
|
-
# self.page_type = self.mti_child().get_page_type()
|
88
|
-
# super().full_clean()
|
103
|
+
return not self.publisher_tree.private
|
89
104
|
|
90
105
|
def mti_child(self):
|
91
106
|
# if self.page_type:
|
@@ -104,7 +119,7 @@ class Page(
|
|
104
119
|
# def as_story_item(self, ar, **kwargs):
|
105
120
|
# return "".join(self.as_page(ar, **kwargs))
|
106
121
|
|
107
|
-
def
|
122
|
+
def old_as_paragraph(self, ar):
|
108
123
|
title = E.b(escape(self.title))
|
109
124
|
url = ar.obj2url(self)
|
110
125
|
if url is not None:
|
@@ -115,6 +130,17 @@ class Page(
|
|
115
130
|
item = E.li(title, body)
|
116
131
|
return tostring(item)
|
117
132
|
|
133
|
+
def as_paragraph(self, ar):
|
134
|
+
title = format_html("<b>{}</b>", self.title)
|
135
|
+
if (url := ar.obj2url(self)) is not None:
|
136
|
+
title = format_html(
|
137
|
+
'<a href="{url}" style="text-decoration:none;color:black;">{title}</a>',
|
138
|
+
title=title, url=url)
|
139
|
+
body = self.get_body_parsed(ar, short=True)
|
140
|
+
if body:
|
141
|
+
return format_html("{} — {}", title, body)
|
142
|
+
return title
|
143
|
+
|
118
144
|
def toc_html(self, ar, max_depth=1):
|
119
145
|
def li(obj):
|
120
146
|
# return "<li>{}</li>".format(obj.memo2html(ar, str(obj)))
|
@@ -131,8 +157,7 @@ class Page(
|
|
131
157
|
if len(breadcrumbs) > 1:
|
132
158
|
breadcrumbs = [
|
133
159
|
"""<a href="{0}">{1}</a>""".format(
|
134
|
-
ar.obj2url(p.mti_child()), p.title
|
135
|
-
)
|
160
|
+
ar.obj2url(p.mti_child()), p.title)
|
136
161
|
for p in breadcrumbs[:-1]
|
137
162
|
]
|
138
163
|
yield "<p>{}</p>".format(" » ".join(breadcrumbs))
|
@@ -168,7 +193,7 @@ class Page(
|
|
168
193
|
|
169
194
|
# if display_mode in ("detail", "story"):
|
170
195
|
if display_mode == "detail":
|
171
|
-
if hlevel == 1 and not dd.plugins.memo.use_markup and self.
|
196
|
+
if hlevel == 1 and not dd.plugins.memo.use_markup and self.parent_id:
|
172
197
|
yield self.toc_html(ar)
|
173
198
|
|
174
199
|
if hlevel == 1 and self.main_image:
|
@@ -185,12 +210,11 @@ class Page(
|
|
185
210
|
# yield self.body_full_preview
|
186
211
|
yield self.get_body_parsed(ar, short=False)
|
187
212
|
|
188
|
-
if self.filler:
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
yield self.filler.get_dynamic_paragraph(ar, self)
|
213
|
+
# if self.filler:
|
214
|
+
# if hlevel == 1:
|
215
|
+
# yield self.filler.get_dynamic_story(ar, self)
|
216
|
+
# else:
|
217
|
+
# yield self.filler.get_dynamic_paragraph(ar, self)
|
194
218
|
|
195
219
|
# if dd.plugins.memo.use_markup:
|
196
220
|
# return
|
@@ -234,17 +258,17 @@ class Page(
|
|
234
258
|
return "".join(ar.row_as_page(self))
|
235
259
|
|
236
260
|
def full_clean(self):
|
237
|
-
if self.
|
238
|
-
self.
|
261
|
+
if self.publisher_tree is None and self.parent is not None:
|
262
|
+
self.publisher_tree = self.parent.publisher_tree
|
239
263
|
super().full_clean()
|
240
264
|
|
241
|
-
def update_page(self, prev,
|
265
|
+
def update_page(self, prev, tree):
|
242
266
|
save = False
|
243
267
|
if self.previous_page != prev:
|
244
268
|
self.previous_page = prev
|
245
269
|
save = True
|
246
|
-
if self.
|
247
|
-
self.
|
270
|
+
if self.publisher_tree != tree:
|
271
|
+
self.publisher_tree = tree
|
248
272
|
save = True
|
249
273
|
if save:
|
250
274
|
self.save()
|
@@ -285,40 +309,14 @@ class Page(
|
|
285
309
|
|
286
310
|
def get_absolute_url(self, **kwargs):
|
287
311
|
parts = []
|
288
|
-
if self.group is not None:
|
289
|
-
|
290
|
-
|
291
|
-
if self.ref:
|
292
|
-
if self.ref != "index":
|
293
|
-
parts.append(self.
|
312
|
+
# if self.group is not None:
|
313
|
+
# if self.group.ref is not None:
|
314
|
+
# parts.append(self.group.ref)
|
315
|
+
if self.publisher_tree.ref:
|
316
|
+
if self.publisher_tree.ref != "index":
|
317
|
+
parts.append(self.publisher_tree.ref)
|
294
318
|
return dd.plugins.publisher.build_plain_url(*parts, **kwargs)
|
295
319
|
|
296
|
-
def get_publisher_response(self, ar):
|
297
|
-
if ar and ar.request and self.language != ar.request.LANGUAGE_CODE:
|
298
|
-
rqlang = ar.request.LANGUAGE_CODE
|
299
|
-
# tt = rt.models.pages.Translation.objects.filter(
|
300
|
-
# parent=self, language=ar.request.LANGUAGE_CODE).first()
|
301
|
-
obj = None
|
302
|
-
if self.translated_from_id and self.translated_from.language == rqlang:
|
303
|
-
obj = self.translated_from
|
304
|
-
else:
|
305
|
-
sources = set([self.id])
|
306
|
-
p = self.translated_from
|
307
|
-
while p is not None:
|
308
|
-
sources.add(p.id)
|
309
|
-
p = p.translated_from
|
310
|
-
qs = self.__class__.objects.filter(
|
311
|
-
language=rqlang, translated_from__in=sources)
|
312
|
-
obj = qs.first()
|
313
|
-
# obj = self.translated_to.filter(language=rqlang).first()
|
314
|
-
# print("20231027 redirect to translation", tt.language, ar.request.LANGUAGE_CODE)
|
315
|
-
if obj is not None:
|
316
|
-
# print("20231028", self.language, "!=", ar.request.LANGUAGE_CODE, tt)
|
317
|
-
ar.selected_rows = [obj]
|
318
|
-
url = ar.get_request_url()
|
319
|
-
return HttpResponseRedirect(url)
|
320
|
-
return super().get_publisher_response(ar)
|
321
|
-
|
322
320
|
|
323
321
|
if dd.plugins.memo.use_markup:
|
324
322
|
dd.update_field(Page, "body", format="plain")
|
@@ -337,13 +335,15 @@ def update_publisher_pages(ar):
|
|
337
335
|
for root in Page.objects.filter(parent__isnull=True):
|
338
336
|
prev = None
|
339
337
|
for obj in root.walk():
|
340
|
-
obj.update_page(prev, root)
|
338
|
+
obj.update_page(prev, root.publisher_tree)
|
341
339
|
prev = obj
|
342
340
|
count += 1
|
343
341
|
ar.logger.info("%d pages have been updated.", count)
|
344
342
|
|
345
343
|
|
346
344
|
def make_demo_pages(pages_desc, root_ref, group=None):
|
345
|
+
Tree = rt.models.publisher.Tree
|
346
|
+
tree = get_or_create(Tree, ref=root_ref, group=group)
|
347
347
|
# Translation = rt.models.pages.Translation
|
348
348
|
# for lc in settings.SITE.LANGUAGE_CHOICES:
|
349
349
|
# language = lc[0]
|
@@ -365,19 +365,18 @@ def make_demo_pages(pages_desc, root_ref, group=None):
|
|
365
365
|
if len(page) != 3:
|
366
366
|
raise Exception(f"Oops {page}")
|
367
367
|
title, body, children = page
|
368
|
-
kwargs = dict(title=title, group=group)
|
368
|
+
# kwargs = dict(title=title, group=group)
|
369
|
+
kwargs = dict(title=title, publisher_tree=tree,
|
370
|
+
language=lng.django_code)
|
369
371
|
if body is None:
|
370
372
|
kwargs.update(body=get_paragraph())
|
371
373
|
else:
|
372
374
|
kwargs.update(body=body)
|
373
|
-
if parent is None:
|
374
|
-
kwargs.update(ref=root_ref)
|
375
|
-
else:
|
375
|
+
if parent is not None:
|
376
376
|
kwargs.update(parent=parent)
|
377
377
|
if lng.suffix:
|
378
378
|
kwargs.update(
|
379
379
|
translated_from=parent_nodes[counter[None]])
|
380
|
-
kwargs.update(language=lng.django_code)
|
381
380
|
if dd.is_installed("publisher"):
|
382
381
|
kwargs.update(publishing_state='published')
|
383
382
|
obj = Page(**kwargs)
|
@@ -1,29 +1,37 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
|
-
# Copyright 2023 Rumma & Ko Ltd
|
2
|
+
# Copyright 2023-2025 Rumma & Ko Ltd
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
|
-
from lino import logger
|
6
5
|
from lino.core.renderer import add_user_language
|
7
|
-
from lino.
|
6
|
+
from lino.core.renderer import HtmlRenderer
|
8
7
|
|
8
|
+
from lino.modlib.publisher.mixins import Publishable
|
9
|
+
|
10
|
+
|
11
|
+
class Renderer(HtmlRenderer):
|
12
|
+
|
13
|
+
tableattrs = {"class": "table table-hover table-striped table-condensed"}
|
14
|
+
cellattrs = dict(align="left", valign="top")
|
15
|
+
|
16
|
+
can_auth = False
|
9
17
|
|
10
|
-
class Renderer(Renderer):
|
11
18
|
def __init__(self, front_end):
|
12
19
|
super().__init__(front_end)
|
13
20
|
dr = front_end.site.kernel.default_renderer
|
14
21
|
for k in ("row_action_button", "get_detail_url"):
|
15
22
|
setattr(self, k, getattr(dr, k))
|
16
23
|
|
17
|
-
def get_request_url(self, ar, *args, **kwargs):
|
18
|
-
obj = ar.selected_rows[0]
|
19
|
-
return self.obj2url(ar, obj, **kwargs)
|
20
|
-
# return obj.publisher_url(ar, **kwargs)
|
21
|
-
|
22
24
|
def obj2url(self, ar, obj, **kwargs):
|
25
|
+
if not isinstance(obj, Publishable):
|
26
|
+
return super().obj2url(ar, obj, **kwargs)
|
23
27
|
# if ar.actor is None or not isinstance(obj, ar.actor.model):
|
24
28
|
add_user_language(kwargs, ar)
|
25
|
-
if isinstance(obj, self.front_end.site.models.publisher.Page) and obj.ref == 'index':
|
26
|
-
|
29
|
+
# if isinstance(obj, self.front_end.site.models.publisher.Page) and obj.ref == 'index':
|
30
|
+
if isinstance(obj, self.front_end.site.models.publisher.Page) and obj.parent is None:
|
31
|
+
if obj.publisher_tree.ref is not None:
|
32
|
+
if obj.publisher_tree.ref == 'index':
|
33
|
+
return self.front_end.buildurl(**kwargs)
|
34
|
+
return self.front_end.buildurl(obj.publisher_tree.ref, **kwargs)
|
27
35
|
# if obj.ref:
|
28
36
|
# return self.front_end.buildurl(obj.ref, **kwargs)
|
29
37
|
loc = obj.__class__._lino_publisher_location
|
@@ -43,4 +51,12 @@ class Renderer(Renderer):
|
|
43
51
|
# publisher view,
|
44
52
|
return None
|
45
53
|
return self.front_end.site.kernel.default_renderer.obj2url(ar, obj, **kwargs)
|
46
|
-
|
54
|
+
|
55
|
+
def get_home_url(self, ar, *args, **kw):
|
56
|
+
add_user_language(kw, ar)
|
57
|
+
return self.front_end.build_plain_url(*args, **kw)
|
58
|
+
|
59
|
+
def get_request_url(self, ar, *args, **kwargs):
|
60
|
+
obj = ar.selected_rows[0]
|
61
|
+
return self.obj2url(ar, obj, **kwargs)
|
62
|
+
# return obj.publisher_url(ar, **kwargs)
|