lino 25.7.1__py3-none-any.whl → 25.7.3__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 (55) hide show
  1. lino/__init__.py +1 -1
  2. lino/api/doctest.py +41 -12
  3. lino/core/__init__.py +0 -2
  4. lino/core/actions.py +15 -7
  5. lino/core/actors.py +2 -162
  6. lino/core/atomizer.py +9 -8
  7. lino/core/auth/utils.py +9 -1
  8. lino/core/callbacks.py +2 -2
  9. lino/core/elems.py +1 -1
  10. lino/core/fields.py +3 -1
  11. lino/core/kernel.py +14 -18
  12. lino/core/layouts.py +5 -7
  13. lino/core/model.py +12 -3
  14. lino/core/plugin.py +1 -1
  15. lino/core/renderer.py +1 -1
  16. lino/core/requests.py +3 -4
  17. lino/core/site.py +1 -1
  18. lino/core/store.py +3 -3
  19. lino/core/utils.py +20 -17
  20. lino/help_texts.py +10 -7
  21. lino/mixins/__init__.py +3 -2
  22. lino/mixins/{duplicable.py → clonable.py} +45 -50
  23. lino/mixins/dupable.py +2 -2
  24. lino/mixins/registrable.py +7 -5
  25. lino/mixins/sequenced.py +12 -14
  26. lino/modlib/dupable/models.py +2 -2
  27. lino/modlib/extjs/views.py +7 -0
  28. lino/modlib/linod/__init__.py +1 -1
  29. lino/modlib/memo/__init__.py +1 -2
  30. lino/modlib/notify/api.py +5 -0
  31. lino/modlib/office/roles.py +0 -1
  32. lino/modlib/printing/actions.py +2 -6
  33. lino/modlib/printing/choicelists.py +6 -6
  34. lino/modlib/printing/mixins.py +4 -4
  35. lino/modlib/publisher/__init__.py +21 -30
  36. lino/modlib/publisher/models.py +3 -1
  37. lino/modlib/publisher/views.py +4 -11
  38. lino/modlib/summaries/mixins.py +6 -4
  39. lino/modlib/users/actions.py +5 -0
  40. lino/modlib/weasyprint/__init__.py +9 -0
  41. lino/modlib/weasyprint/choicelists.py +14 -9
  42. lino/modlib/weasyprint/config/weasyprint/base.weasy.html +15 -13
  43. lino/sphinxcontrib/__init__.py +1 -1
  44. lino/sphinxcontrib/actordoc.py +1 -1
  45. lino/utils/diag.py +2 -2
  46. lino/utils/instantiator.py +21 -1
  47. {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/METADATA +1 -1
  48. {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/RECORD +51 -55
  49. lino/modlib/forms/__init__.py +0 -51
  50. lino/modlib/forms/models.py +0 -0
  51. lino/modlib/forms/renderer.py +0 -74
  52. lino/modlib/forms/views.py +0 -311
  53. {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/WHEEL +0 -0
  54. {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/licenses/AUTHORS.rst +0 -0
  55. {lino-25.7.1.dist-info → lino-25.7.3.dist-info}/licenses/COPYING +0 -0
@@ -1,311 +0,0 @@
1
- # -*- coding: UTF-8 -*-
2
- # Copyright 2009-2017 Rumma & Ko Ltd
3
- # License: GNU Affero General Public License v3 (see file COPYING for details)
4
- """Views for `lino.modlib.bootstrap3`.
5
-
6
- """
7
- from __future__ import division
8
- from past.utils import old_div
9
-
10
- from lino import logger
11
-
12
- from django import http
13
- from django.conf import settings
14
- from django.views.generic import View
15
- from django.core import exceptions
16
- from django.utils.translation import gettext as _
17
- from django.utils.translation import get_language
18
-
19
- # from django.contrib import auth
20
- from lino.core import auth
21
-
22
- # from lino.api import dd
23
- from lino.core import constants
24
-
25
- # from lino.core import auth
26
- from lino.core.requests import BaseRequest
27
- from lino.core.tables import AbstractTable
28
- from lino.core.views import action_request
29
- from lino.core.utils import navinfo
30
- from lino.utils.html import E, tostring
31
- from etgen import html as xghtml
32
-
33
- PLAIN_PAGE_LENGTH = 15
34
-
35
- MENUS = dict()
36
-
37
-
38
- def http_response(ar, tplname, context):
39
- "Deserves a docstring"
40
- u = ar.get_user()
41
- lang = get_language()
42
- k = (u.user_type, lang)
43
- menu = MENUS.get(k, None)
44
- if menu is None:
45
- menu = settings.SITE.get_site_menu(u.user_type)
46
- bs3 = settings.SITE.plugins.bootstrap3
47
- if False: # 20150803 home button now in base.html
48
- assert bs3.renderer is not None
49
- url = bs3.build_plain_url()
50
- menu.add_url_button(url, label=_("Home"))
51
- e = bs3.renderer.show_menu(ar, menu)
52
- menu = tostring(e)
53
- MENUS[k] = menu
54
- context.update(menu=menu)
55
- context = ar.get_printable_context(**context)
56
- context["ar"] = ar
57
- context["memo"] = ar.parse_memo # MEMO_PARSER.parse
58
- env = settings.SITE.plugins.jinja.renderer.jinja_env
59
- template = env.get_template(tplname)
60
-
61
- response = http.HttpResponse(
62
- template.render(**context), content_type='text/html;charset="utf-8"'
63
- )
64
-
65
- return response
66
-
67
-
68
- def buttons2pager(buttons, title=None):
69
- items = []
70
- if title:
71
- items.append(E.li(E.span(title)))
72
- for symbol, label, url in buttons:
73
- if url is None:
74
- items.append(E.li(E.span(symbol), **{"class": "disabled"}))
75
- else:
76
- items.append(E.li(E.a(symbol, href=url)))
77
- # Bootstrap version 2.x
78
- # return E.div(E.ul(*items), class_='pagination')
79
- return E.ul(*items, **{"class": "pagination pagination-sm"})
80
-
81
-
82
- def table2html(ar, as_main=True):
83
- """Represent the given table request as an HTML table.
84
-
85
- `ar` is the request to be rendered, an instance of
86
- :class:`lino.core.requests.ActionRequest`.
87
-
88
- The returned HTML enclosed in a ``<div>`` tag and generated using
89
- :mod:`etgen.html`.
90
-
91
- If `as_main` is True, include additional elements such as a paging
92
- toolbar. (This argument is currently being ignored.)
93
-
94
- """
95
- as_main = True
96
- t = xghtml.Table()
97
- t.set("class", "table table-striped table-hover")
98
- if ar.limit is None:
99
- ar.limit = PLAIN_PAGE_LENGTH
100
- pglen = ar.limit
101
- if ar.offset is None:
102
- page = 1
103
- else:
104
- """
105
- (assuming pglen is 5)
106
- offset page
107
- 0 1
108
- 5 2
109
- """
110
- page = int(old_div(ar.offset, pglen)) + 1
111
-
112
- ar.dump2html(t, ar.sliced_data_iterator, header_links=as_main)
113
- if not as_main:
114
- url = ar.get_request_url() # open in own window
115
- return E.div(E.a(ar.get_title(), href=url), t.as_element())
116
-
117
- buttons = []
118
- kw = dict()
119
- kw = {}
120
- if pglen != PLAIN_PAGE_LENGTH:
121
- kw[constants.URL_PARAM_LIMIT] = pglen
122
-
123
- if page > 1:
124
- kw[constants.URL_PARAM_START] = pglen * (page - 2)
125
- prev_url = ar.get_request_url(**kw)
126
- kw[constants.URL_PARAM_START] = 0
127
- first_url = ar.get_request_url(**kw)
128
- else:
129
- prev_url = None
130
- first_url = None
131
- buttons.append(("<<", _("First page"), first_url))
132
- buttons.append(("<", _("Previous page"), prev_url))
133
-
134
- next_start = pglen * page
135
- if next_start < ar.get_total_count():
136
- kw[constants.URL_PARAM_START] = next_start
137
- next_url = ar.get_request_url(**kw)
138
- last_page = int(old_div((ar.get_total_count() - 1), pglen))
139
- kw[constants.URL_PARAM_START] = pglen * last_page
140
- last_url = ar.get_request_url(**kw)
141
- else:
142
- next_url = None
143
- last_url = None
144
- buttons.append((">", _("Next page"), next_url))
145
- buttons.append((">>", _("Last page"), last_url))
146
-
147
- return E.div(buttons2pager(buttons), t.as_element())
148
-
149
-
150
- def layout2html(ar, elem):
151
- wl = ar.bound_action.get_window_layout()
152
- # ~ print 20120901, wl.main
153
- lh = wl.get_layout_handle()
154
-
155
- items = list(lh.main.as_plain_html(ar, elem))
156
- # if navigator:
157
- # items.insert(0, navigator)
158
- # ~ print tostring(E.div())
159
- # ~ if len(items) == 0: return ""
160
- return E.form(*items)
161
- # ~ print 20120901, lh.main.__html__(ar)
162
-
163
-
164
- class List(View):
165
- """Render a list of records."""
166
-
167
- def get(self, request, app_label=None, actor=None):
168
- ar = action_request(app_label, actor, request, request.GET, True)
169
- ar.renderer = settings.SITE.plugins.bootstrap3.renderer
170
-
171
- context = dict(
172
- title=ar.get_title(),
173
- heading=ar.get_title(),
174
- )
175
-
176
- if ar.actor is not None and issubclass(ar.actor, AbstractTable):
177
- context.update(main=table2html(ar))
178
- else:
179
- context.update(main=layout2html(ar, None))
180
-
181
- context.update(ar=ar)
182
- return http_response(ar, ar.actor.list_html_template, context)
183
-
184
-
185
- class Element(View):
186
- """Render a single record."""
187
-
188
- def get(self, request, app_label=None, actor=None, pk=None):
189
- ar = action_request(app_label, actor, request, request.GET, False)
190
- ar.renderer = settings.SITE.plugins.bootstrap3.renderer
191
-
192
- navigator = None
193
- if pk and pk != "-99999" and pk != "-99998":
194
- elem = ar.get_row_by_pk(pk)
195
- if elem is None:
196
- raise http.Http404("%s has no row with primary key %r" % (ar.actor, pk))
197
- # ~ raise Exception("20120327 %s.get_row_by_pk(%r)" % (rpt,pk))
198
- if ar.actor.show_detail_navigator:
199
- ni = navinfo(ar.data_iterator, elem)
200
- if ni:
201
- # m = elem.__class__
202
- buttons = []
203
- # ~ buttons.append( ('*',_("Home"), '/' ))
204
-
205
- buttons.append(("<<", _("First page"), ar.pk2url(ni["first"])))
206
- buttons.append(("<", _("Previous page"), ar.pk2url(ni["prev"])))
207
- buttons.append((">", _("Next page"), ar.pk2url(ni["next"])))
208
- buttons.append((">>", _("Last page"), ar.pk2url(ni["last"])))
209
-
210
- navigator = buttons2pager(buttons)
211
- else:
212
- navigator = E.p("No navinfo")
213
- else:
214
- elem = None
215
-
216
- main = layout2html(ar, elem)
217
-
218
- # The `method="html"` argument isn't available in Python 2.6,
219
- # only 2.7. It is useful to avoid side effects in case of
220
- # empty elements: the default method (xml) writes an empty
221
- # E.div() as "<div/>" while in HTML5 it must be "<div></div>"
222
- # (and the ending / is ignored).
223
-
224
- # ~ return tostring(main, method="html")
225
- # ~ return tostring(main)
226
- # return main
227
-
228
- context = dict(
229
- title=ar.get_action_title(),
230
- obj=elem,
231
- form=main,
232
- navigator=navigator,
233
- )
234
- # ~ template = web.jinja_env.get_template('detail.html')
235
- context.update(ar=ar)
236
- return http_response(ar, ar.actor.detail_html_template, context)
237
-
238
-
239
- class Authenticate(View):
240
- def get(self, request, *args, **kw):
241
- action_name = request.GET.get(constants.URL_PARAM_ACTION_NAME)
242
- if action_name == "logout":
243
- username = request.session.pop("username", None)
244
- auth.logout(request)
245
- # request.user = settings.SITE.user_model.get_anonymous_user()
246
- # request.session.pop('password', None)
247
- # ~ username = request.session['username']
248
- # ~ del request.session['password']
249
- target = "/"
250
- return http.HttpResponseRedirect(target)
251
-
252
- # ar = BaseRequest(request)
253
- # ar.success("User %r logged out." % username)
254
- # return ar.renderer.render_action_response(ar)
255
- raise http.Http404()
256
-
257
- def post(self, request, *args, **kw):
258
- username = request.POST.get("username")
259
- password = request.POST.get("password")
260
- user = auth.authenticate(request, username=username, password=password)
261
- auth.login(request, user)
262
- target = "/"
263
- return http.HttpResponseRedirect(target)
264
- # ar = BaseRequest(request)
265
- # mw = auth.get_auth_middleware()
266
- # msg = mw.authenticate(username, password, request)
267
- # if msg:
268
- # request.session.pop('username', None)
269
- # ar.error(msg)
270
- # else:
271
- # request.session['username'] = username
272
- # # request.session['password'] = password
273
- # # ar.user = request....
274
- # ar.success(("Now logged in as %r" % username))
275
- # # print "20150428 Now logged in as %r (%s)" % (username, user)
276
- # return ar.renderer.render_action_response(ar)
277
-
278
-
279
- class Index(View):
280
- """
281
- Render the main page.
282
- """
283
-
284
- def get(self, request, *args, **kw):
285
- # raise Exception("20171122 {} {}".format(
286
- # get_language(), settings.MIDDLEWARE_CLASSES))
287
- ui = settings.SITE.plugins.bootstrap3
288
- # print("20170607", request.user)
289
- # assert ui.renderer is not None
290
- ar = BaseRequest(
291
- # user=user,
292
- request=request,
293
- renderer=ui.renderer,
294
- )
295
- return index_response(ar)
296
-
297
-
298
- def index_response(ar):
299
- ui = settings.SITE.plugins.bootstrap3
300
- main = settings.SITE.get_main_html(ar, front_end=ui)
301
- main = ui.renderer.html_text(main)
302
- context = dict(
303
- title=settings.SITE.title,
304
- main=main,
305
- )
306
- # if settings.SITE.user_model is None:
307
- # user = auth.AnonymousUser.instance()
308
- # else:
309
- # user = request.subst_user or request.user
310
- # context.update(ar=ar)
311
- return http_response(ar, "bootstrap3/index.html", context)
File without changes