lino 25.8.3__py3-none-any.whl → 25.9.1__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.
Files changed (63) hide show
  1. lino/__init__.py +1 -1
  2. lino/api/dd.py +0 -1
  3. lino/core/__init__.py +0 -1
  4. lino/core/actions.py +1 -1
  5. lino/core/actors.py +8 -0
  6. lino/core/elems.py +1 -1
  7. lino/core/fields.py +4 -1
  8. lino/core/model.py +2 -11
  9. lino/core/requests.py +8 -7
  10. lino/core/site.py +0 -79
  11. lino/core/user_types.py +1 -10
  12. lino/help_texts.py +1 -5
  13. lino/management/commands/dump2py.py +4 -3
  14. lino/management/commands/initdb.py +0 -3
  15. lino/modlib/__init__.py +0 -1
  16. lino/modlib/bootstrap5/README.txt +1 -1
  17. lino/modlib/bootstrap5/__init__.py +34 -38
  18. lino/modlib/bootstrap5/config/bootstrap5/base.html +4 -0
  19. lino/modlib/bootstrap5/models.py +23 -23
  20. lino/modlib/bootstrap5/views.py +2 -107
  21. lino/modlib/checkdata/choicelists.py +1 -1
  22. lino/modlib/comments/fixtures/demo2.py +1 -0
  23. lino/modlib/comments/ui.py +7 -7
  24. lino/modlib/extjs/__init__.py +2 -4
  25. lino/modlib/extjs/config/extjs/index.html +1 -1
  26. lino/modlib/extjs/ext_renderer.py +1 -7
  27. lino/modlib/extjs/views.py +2 -0
  28. lino/modlib/help/models.py +1 -12
  29. lino/modlib/linod/__init__.py +2 -2
  30. lino/modlib/linod/mixins.py +3 -2
  31. lino/modlib/memo/__init__.py +10 -9
  32. lino/modlib/memo/mixins.py +38 -21
  33. lino/modlib/memo/models.py +10 -7
  34. lino/modlib/memo/parser.py +3 -1
  35. lino/modlib/notify/models.py +6 -9
  36. lino/modlib/publisher/__init__.py +12 -7
  37. lino/modlib/publisher/choicelists.py +9 -64
  38. lino/modlib/publisher/config/publisher/page.pub.html +73 -7
  39. lino/modlib/publisher/fixtures/std.py +14 -1
  40. lino/modlib/publisher/mixins.py +41 -12
  41. lino/modlib/publisher/models.py +74 -75
  42. lino/modlib/publisher/renderer.py +28 -12
  43. lino/modlib/publisher/ui.py +35 -35
  44. lino/modlib/publisher/views.py +59 -24
  45. lino/modlib/system/models.py +3 -2
  46. lino/modlib/uploads/__init__.py +1 -0
  47. lino/modlib/uploads/mixins.py +2 -2
  48. lino/modlib/uploads/models.py +55 -21
  49. lino/modlib/uploads/ui.py +1 -0
  50. lino/modlib/uploads/utils.py +2 -2
  51. lino/modlib/users/__init__.py +2 -3
  52. lino/modlib/users/actions.py +12 -17
  53. lino/modlib/users/models.py +37 -36
  54. lino/modlib/weasyprint/choicelists.py +6 -0
  55. lino/utils/diag.py +5 -3
  56. lino/utils/html.py +103 -0
  57. lino/utils/mldbc/mixins.py +2 -2
  58. lino/utils/soup.py +16 -8
  59. {lino-25.8.3.dist-info → lino-25.9.1.dist-info}/METADATA +1 -2
  60. {lino-25.8.3.dist-info → lino-25.9.1.dist-info}/RECORD +63 -63
  61. {lino-25.8.3.dist-info → lino-25.9.1.dist-info}/WHEEL +0 -0
  62. {lino-25.8.3.dist-info → lino-25.9.1.dist-info}/licenses/AUTHORS.rst +0 -0
  63. {lino-25.8.3.dist-info → lino-25.9.1.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)}} | {{obj.get_next_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
- <a href="/{{site.kernel.editing_front_end.url_prefix}}/">{{_("Admin")}}</a>
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
  &mdash;
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 index_node, home_children = obj.home_and_children(ar) %}
45
- <a class="navbar-brand" href="{{ar.obj2url(index_node)}}">{{ index_node.title }}</a>
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 home_children %}
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
- kwargs = dict(language=lng.django_code, special_page=sp)
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)
@@ -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, PageFillers, SpecialPages
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
- root_page = dd.ForeignKey(
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, rt.models.publisher.Page.objects.filter(parent=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, PrivacyRelevant):
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)
@@ -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
- # from django.utils.translation import get_language
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, PageFillers, SpecialPages
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 = ["ref", "language"]
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 self.ref or super().__str__()
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 as_paragraph(self, ar):
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("{} &mdash; {}", 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(" &raquo; ".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.ref != 'index':
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
- yield "\n\n"
190
- if hlevel == 1:
191
- yield self.filler.get_dynamic_story(ar, self)
192
- else:
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.root_page is None and self.parent is not None:
238
- self.root_page = self.parent.root_page
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, root):
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.root_page != root:
247
- self.root_page = root
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
- if self.group.ref is not None:
290
- parts.append(self.group.ref)
291
- if self.ref:
292
- if self.ref != "index":
293
- parts.append(self.group.ref)
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.modlib.bootstrap5.renderer import Renderer
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
- return self.front_end.buildurl(**kwargs)
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
- # return super().obj2url(ar, obj, **kwargs)
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)