lino 24.10.3__py3-none-any.whl → 24.11.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 (80) hide show
  1. lino/__init__.py +1 -1
  2. lino/api/doctest.py +11 -10
  3. lino/api/rt.py +2 -3
  4. lino/config/admin_main_base.html +2 -2
  5. lino/core/actions.py +2 -4
  6. lino/core/actors.py +70 -35
  7. lino/core/choicelists.py +2 -2
  8. lino/core/dashboard.py +2 -1
  9. lino/core/dbtables.py +15 -15
  10. lino/core/elems.py +8 -4
  11. lino/core/fields.py +12 -3
  12. lino/core/inject.py +9 -2
  13. lino/core/kernel.py +11 -11
  14. lino/core/layouts.py +1 -1
  15. lino/core/model.py +25 -36
  16. lino/core/plugin.py +1 -0
  17. lino/core/renderer.py +21 -21
  18. lino/core/requests.py +94 -83
  19. lino/core/site.py +9 -90
  20. lino/core/store.py +16 -19
  21. lino/core/tables.py +0 -17
  22. lino/core/utils.py +32 -2
  23. lino/core/views.py +2 -1
  24. lino/help_texts.py +10 -5
  25. lino/locale/bn/LC_MESSAGES/django.po +1210 -907
  26. lino/locale/de/LC_MESSAGES/django.po +1760 -1375
  27. lino/locale/django.pot +1136 -906
  28. lino/locale/es/LC_MESSAGES/django.po +1709 -1347
  29. lino/locale/et/LC_MESSAGES/django.po +1206 -906
  30. lino/locale/fr/LC_MESSAGES/django.mo +0 -0
  31. lino/locale/fr/LC_MESSAGES/django.po +1193 -923
  32. lino/locale/nl/LC_MESSAGES/django.po +1247 -942
  33. lino/locale/pt_BR/LC_MESSAGES/django.po +1190 -903
  34. lino/locale/zh_Hant/LC_MESSAGES/django.po +1190 -903
  35. lino/management/commands/show.py +2 -4
  36. lino/mixins/periods.py +15 -7
  37. lino/mixins/polymorphic.py +3 -3
  38. lino/mixins/ref.py +6 -3
  39. lino/modlib/checkdata/__init__.py +3 -3
  40. lino/modlib/comments/choicelists.py +1 -1
  41. lino/modlib/comments/fixtures/demo2.py +4 -1
  42. lino/modlib/comments/mixins.py +9 -10
  43. lino/modlib/comments/models.py +4 -4
  44. lino/modlib/comments/ui.py +5 -0
  45. lino/modlib/extjs/ext_renderer.py +1 -1
  46. lino/modlib/linod/consumers.py +2 -3
  47. lino/modlib/linod/mixins.py +3 -2
  48. lino/modlib/memo/mixins.py +11 -209
  49. lino/modlib/notify/mixins.py +33 -32
  50. lino/modlib/periods/__init__.py +12 -1
  51. lino/modlib/periods/fixtures/std.py +2 -1
  52. lino/modlib/periods/mixins.py +0 -1
  53. lino/modlib/periods/models.py +79 -75
  54. lino/modlib/printing/actions.py +2 -0
  55. lino/modlib/printing/choicelists.py +3 -3
  56. lino/modlib/publisher/ui.py +2 -2
  57. lino/modlib/search/models.py +17 -11
  58. lino/modlib/system/__init__.py +0 -2
  59. lino/modlib/system/choicelists.py +55 -1
  60. lino/modlib/system/fixtures/__init__.py +0 -0
  61. lino/modlib/system/fixtures/std.py +5 -0
  62. lino/modlib/system/models.py +4 -2
  63. lino/modlib/uploads/__init__.py +10 -1
  64. lino/modlib/uploads/choicelists.py +3 -3
  65. lino/modlib/uploads/mixins.py +30 -32
  66. lino/modlib/uploads/models.py +89 -56
  67. lino/modlib/uploads/ui.py +12 -6
  68. lino/modlib/uploads/utils.py +107 -0
  69. lino/modlib/users/models.py +2 -2
  70. lino/modlib/weasyprint/__init__.py +2 -0
  71. lino/utils/__init__.py +14 -9
  72. lino/utils/djangotest.py +2 -1
  73. lino/utils/html.py +32 -1
  74. lino/utils/media.py +2 -3
  75. lino/utils/soup.py +311 -0
  76. {lino-24.10.3.dist-info → lino-24.11.1.dist-info}/METADATA +1 -3
  77. {lino-24.10.3.dist-info → lino-24.11.1.dist-info}/RECORD +80 -76
  78. {lino-24.10.3.dist-info → lino-24.11.1.dist-info}/WHEEL +1 -1
  79. {lino-24.10.3.dist-info → lino-24.11.1.dist-info}/licenses/AUTHORS.rst +0 -0
  80. {lino-24.10.3.dist-info → lino-24.11.1.dist-info}/licenses/COPYING +0 -0
lino/core/model.py CHANGED
@@ -20,6 +20,7 @@ from django.db.models.signals import pre_delete
20
20
  from django.utils.text import format_lazy
21
21
 
22
22
  from lino.utils.html import E, forcetext, tostring, join_elems
23
+ from lino.utils.soup import sanitize
23
24
 
24
25
  from lino.core import fields
25
26
  from lino.core import signals
@@ -36,10 +37,10 @@ from .workflows import ChangeStateAction
36
37
  from .requests import ActionRequest, sliced_data_iterator
37
38
  from .tables import AbstractTable
38
39
 
39
- try:
40
- import bleach
41
- except ImportError:
42
- bleach = None
40
+ # try:
41
+ # import bleach
42
+ # except ImportError:
43
+ # bleach = None
43
44
 
44
45
 
45
46
  class Model(models.Model, fields.TableRow):
@@ -308,8 +309,7 @@ class Model(models.Model, fields.TableRow):
308
309
  if isinstance(f, RichTextField):
309
310
  if f.editable and (
310
311
  f.bleached is True
311
- or f.bleached is None
312
- and settings.SITE.textfield_bleached
312
+ or f.bleached is None and settings.SITE.textfield_bleached
313
313
  ):
314
314
  bleached_fields.append(f)
315
315
  cls._bleached_fields = tuple(bleached_fields)
@@ -428,27 +428,24 @@ class Model(models.Model, fields.TableRow):
428
428
  setattr(self, f.name, new)
429
429
 
430
430
  def fields_to_bleach(self):
431
- if bleach:
432
- for f in self._bleached_fields:
433
- old = getattr(self, f.name)
434
- if old is None:
435
- continue
436
- # print("20190626", self, f, old)
437
- if not old.startswith("<"):
438
- continue
439
- try:
440
- new = bleach.clean(
441
- old,
442
- tags=settings.SITE.bleach_allowed_tags,
443
- attributes=settings.SITE.bleach_allowed_attributes,
444
- strip=True,
445
- )
446
- except TypeError as e:
447
- logger.warning("Could not bleach %r : %s (%s)", old, e, self)
448
- continue
449
- if old != new:
450
- logger.debug("Bleaching %s from %r to %r", f.name, old, new)
451
- yield f, old, new
431
+ for f in self._bleached_fields:
432
+ old = getattr(self, f.name)
433
+ if old is None:
434
+ continue
435
+ new = sanitize(old)
436
+ # try:
437
+ # new = bleach.clean(
438
+ # new,
439
+ # tags=settings.SITE.bleach_allowed_tags,
440
+ # attributes=settings.SITE.bleach_allowed_attributes,
441
+ # strip=True,
442
+ # )
443
+ # except TypeError as e:
444
+ # logger.warning("Could not bleach %r : %s (%s)", old, e, self)
445
+ # continue
446
+ if old != new:
447
+ logger.debug("Bleaching %s from %r to %r", f.name, old, new)
448
+ yield f, old, new
452
449
 
453
450
  updatable_panels = None
454
451
 
@@ -623,15 +620,6 @@ class Model(models.Model, fields.TableRow):
623
620
  def name_column(self, ar):
624
621
  return str(self)
625
622
 
626
- # @fields.displayfield(_("Description"))
627
- # def mobile_item(self, ar):
628
- # if ar is None:
629
- # return ''
630
- # return E.div(*forcetext(self.get_mobile_list_item_elems(ar)))
631
- #
632
- # def get_mobile_list_item_elems(self, ar):
633
- # return [self.as_summary_item(ar)]
634
-
635
623
  @fields.htmlbox()
636
624
  def navigation_panel(self, ar):
637
625
  # if not isinstance(ar, ActionRequest):
@@ -934,6 +922,7 @@ LINO_MODEL_ATTRIBS = (
934
922
  "workflow_buttons",
935
923
  "delete_instance",
936
924
  "setup_parameters",
925
+ "param_defaults",
937
926
  "add_param_filter",
938
927
  "save_new_instance",
939
928
  "save_watched_instance",
lino/core/plugin.py CHANGED
@@ -7,6 +7,7 @@ import inspect
7
7
  from os.path import exists, join, dirname, isdir, abspath
8
8
  from collections.abc import Iterable
9
9
  from urllib.parse import urlencode
10
+ from lino.core.exceptions import ChangedAPI
10
11
 
11
12
 
12
13
  class Plugin:
lino/core/renderer.py CHANGED
@@ -197,7 +197,7 @@ class HtmlRenderer(Renderer):
197
197
  import rstgen
198
198
 
199
199
  if display_mode == constants.DISPLAY_MODE_CARDS:
200
- layout = ar.actor.card_layout or ar.actor.list_layout
200
+ layout = ar.actor.card_layout # or ar.actor.list_layout
201
201
  lh = layout.get_layout_handle()
202
202
  else:
203
203
  lh = ar.bound_action.get_layout_handel()
@@ -262,7 +262,6 @@ class HtmlRenderer(Renderer):
262
262
  ar,
263
263
  nosummary=False,
264
264
  stripped=True,
265
- show_urls=False,
266
265
  header_level=None,
267
266
  display_mode=None,
268
267
  **kwargs
@@ -766,28 +765,27 @@ class TextRenderer(HtmlRenderer):
766
765
  """
767
766
 
768
767
  user = None
769
- show_urls = False
770
768
  link_url = mark_safe("…") # U+2026 Horizontal Ellipsis
771
769
 
772
770
  def __init__(self, *args, **kw):
773
- HtmlRenderer.__init__(self, *args, **kw)
771
+ super().__init__(*args, **kw)
774
772
  self.user = None
775
773
 
776
774
  def get_request_url(self, ar, *args, **kw):
777
- if self.show_urls:
775
+ if ar.show_urls:
778
776
  return super().get_request_url(ar, *args, **kw)
779
777
  return self.link_url
780
778
  # return None
781
779
 
782
780
  def obj2url(self, ar, obj):
783
- if self.show_urls:
781
+ if ar.show_urls:
784
782
  return super().obj2url(ar, obj)
785
783
  return self.link_url
786
784
  # return None
787
785
 
788
- def get_detail_url(self, *args, **kwargs):
789
- if self.show_urls:
790
- return super().get_detail_url(*args, **kwargs)
786
+ def get_detail_url(self, ar, *args, **kwargs):
787
+ if ar.show_urls:
788
+ return super().get_detail_url(ar, *args, **kwargs)
791
789
  return self.link_url
792
790
 
793
791
  # # return str(actor)+"/"+str(pk)
@@ -815,7 +813,7 @@ class TextRenderer(HtmlRenderer):
815
813
  header_links=None,
816
814
  nosummary=False,
817
815
  stripped=True,
818
- show_urls=False,
816
+ show_links=False, # added 20241031
819
817
  display_mode=None,
820
818
  **kwargs
821
819
  ):
@@ -823,9 +821,9 @@ class TextRenderer(HtmlRenderer):
823
821
  Render the given table request as reStructuredText to stdout. See
824
822
  :meth:`ar.show <lino.core.request.BaseRequest.show>`.
825
823
  """
826
- self.show_urls = show_urls
827
824
  if display_mode is None:
828
825
  display_mode = ar.actor.get_display_mode()
826
+ # display_mode = constants.DISPLAY_MODE_GRID
829
827
  if nosummary and display_mode == constants.DISPLAY_MODE_SUMMARY:
830
828
  display_mode = constants.DISPLAY_MODE_GRID
831
829
  else:
@@ -874,6 +872,16 @@ class TextRenderer(HtmlRenderer):
874
872
  yield rstgen.ul(items).strip()
875
873
  return
876
874
 
875
+ # At this point, display_mode is one of story, grid or html.
876
+
877
+ if header_level is not None:
878
+ h = rstgen.header(header_level, ar.get_title())
879
+ if stripped:
880
+ h = h.strip()
881
+ yield h
882
+ # s = h + "\n" + s
883
+ # s = tostring(E.h2(ar.get_title())) + s
884
+
877
885
  fields, headers, widths = ar.get_field_info(column_names)
878
886
 
879
887
  # if str(ar.actor) == "working.WorkedHours":
@@ -884,7 +892,7 @@ class TextRenderer(HtmlRenderer):
884
892
  recno = 0
885
893
  for row in ar.sliced_data_iterator:
886
894
  recno += 1
887
- if show_urls:
895
+ if ar.show_urls or show_links:
888
896
  rows.append([to_rst(x) for x in ar.row2html(recno, fields, row, sums)])
889
897
  else:
890
898
  # only for debugging:
@@ -898,14 +906,6 @@ class TextRenderer(HtmlRenderer):
898
906
  # return a SafeString
899
907
  rows.append([text2rst(x) for x in ar.row2text(fields, row, sums)])
900
908
 
901
- if header_level is not None:
902
- h = rstgen.header(header_level, ar.get_title())
903
- if stripped:
904
- h = h.strip()
905
- yield h
906
- # s = h + "\n" + s
907
- # s = tostring(E.h2(ar.get_title())) + s
908
-
909
909
  # if str(ar.actor) == "working.WorkedHours":
910
910
  # yield "20200306 rows {}".format(rows)
911
911
  if len(rows) == 0:
@@ -1264,7 +1264,7 @@ class JsCacheRenderer(JsRenderer):
1264
1264
  # if res.parameters is not None:
1265
1265
  add(res, self.param_panels, res.params_layout, "%s.ParamsPanel" % res)
1266
1266
  add(res, self.other_panels, res.card_layout, "%s.CardsPanel" % res)
1267
- add(res, self.other_panels, res.list_layout, "%s.ItemsPanel" % res)
1267
+ # add(res, self.other_panels, res.list_layout, "%s.ItemsPanel" % res)
1268
1268
 
1269
1269
  for ba in res.get_actions():
1270
1270
  if ba.action.parameters:
lino/core/requests.py CHANGED
@@ -35,6 +35,7 @@ from asgiref.sync import sync_to_async
35
35
 
36
36
  from lino.utils.html import E, tostring, iselement
37
37
  from lino.utils import AttrDict
38
+ from lino.utils import capture_output
38
39
  from lino.utils import MissingRow
39
40
  from lino.utils.html import html2text
40
41
  from lino.core import constants
@@ -237,8 +238,8 @@ class ValidActionResponses(object):
237
238
 
238
239
 
239
240
  inheritable_attrs = frozenset(
240
- "user subst_user renderer requesting_panel master_instance logger".split()
241
- )
241
+ ["user", "subst_user", "renderer", "requesting_panel", "master_instance",
242
+ "logger", "show_urls"] )
242
243
 
243
244
 
244
245
  def bool2text(x):
@@ -311,9 +312,16 @@ class BaseRequest:
311
312
  no_data_text = _("No data to display")
312
313
  is_on_main_actor = True
313
314
  permalink_uris = False
315
+ show_urls = True
316
+ master = None
314
317
  master_instance = None
315
318
  request = None
316
319
  selected_rows = []
320
+ order_by = None
321
+ filter = None
322
+ gridfilters = None,
323
+ quick_search = None
324
+ extra = None
317
325
  content_type = "application/json"
318
326
  requesting_panel = None
319
327
  xcallback_answers = {}
@@ -326,12 +334,13 @@ class BaseRequest:
326
334
  self,
327
335
  request=None,
328
336
  parent=None,
329
- hash_router=None,
330
337
  is_on_main_actor=True,
331
338
  permalink_uris=None,
332
339
  **kw
333
340
  ):
334
341
  self.response = dict()
342
+ # if str(kw.get('actor', None)) == "cal.EntriesByController":
343
+ # if kw.get('actor', None):
335
344
  if request is not None:
336
345
  assert parent is None
337
346
  self.request = request
@@ -339,8 +348,6 @@ class BaseRequest:
339
348
  kw = self.parse_req(request, self.rqdata, **kw)
340
349
  if permalink_uris is None:
341
350
  permalink_uris = False # todo: which default value?
342
- # if hash_router is None:
343
- # hash_router = False
344
351
  elif parent is not None:
345
352
  # if parent.actor is None:
346
353
  # 20190926 we want to have javascript extjs links in dasboard
@@ -369,15 +376,16 @@ class BaseRequest:
369
376
  # is_on_main_actor = False
370
377
  if permalink_uris is None:
371
378
  permalink_uris = parent.permalink_uris
372
- # if hash_router is None:
373
- # hash_router = parent.hash_router
379
+ # else:
380
+ # kw.setdefault("show_urls", False)
374
381
 
375
382
  self.is_on_main_actor = is_on_main_actor
376
383
  self.permalink_uris = permalink_uris
377
- # self.hash_router = hash_router
378
384
 
379
385
  self.setup(**kw)
380
386
 
387
+ # print("20241101", self.__class__.__name__, self.show_urls)
388
+
381
389
  if self.master is not None and settings.SITE.strict_master_check:
382
390
  if self.master_instance is None:
383
391
  raise exceptions.BadRequest(
@@ -407,10 +415,21 @@ class BaseRequest:
407
415
  renderer=None,
408
416
  xcallback_answers=None,
409
417
  known_values={},
418
+ show_urls=None,
419
+ quick_search=None,
420
+ order_by=None,
421
+ filter=None,
422
+ gridfilters=None,
423
+ extra=None,
410
424
  ):
411
425
  if logger is not None:
412
426
  self.logger = logger
413
427
  self.requesting_panel = requesting_panel
428
+ self.quick_search = quick_search
429
+ self.order_by = order_by
430
+ self.filter = filter
431
+ self.gridfilters = gridfilters
432
+ self.extra = extra
414
433
  if user is None:
415
434
  self.user = settings.SITE.get_anonymous_user()
416
435
  else:
@@ -418,27 +437,31 @@ class BaseRequest:
418
437
  self.current_project = current_project
419
438
  if renderer is None:
420
439
  renderer = settings.SITE.kernel.text_renderer
440
+ # renderer = settings.SITE.kernel.default_renderer
421
441
  self.renderer = renderer
442
+ if show_urls is not None:
443
+ self.show_urls = show_urls
422
444
  self.subst_user = subst_user
423
445
  if xcallback_answers is not None:
424
446
  self.xcallback_answers = xcallback_answers
425
447
 
426
- if master is None and self.actor is not None:
427
- master = self.actor.master
428
- if master_type is not None:
429
- try:
430
- master = ContentType.objects.get(pk=master_type).model_class()
431
- except ContentType.DoesNotExist:
432
- raise exceptions.BadRequest("Invalid master_type {}".format(master_type)) from None
433
- self.master = master
434
-
435
- if self.master is not None and self.master_instance is None:
436
- # master_instance might have been inherited from parent
437
- if master_instance is None:
438
- master_instance = self.get_master_instance(
439
- self.master, master_key, master_type
440
- )
441
- self.master_instance = self.actor.cast_master_instance(master_instance)
448
+ if self.actor is not None:
449
+ if master is None:
450
+ master = self.actor.master
451
+ if master_type is not None and ContentType is not None:
452
+ try:
453
+ master = ContentType.objects.get(pk=master_type).model_class()
454
+ except ContentType.DoesNotExist:
455
+ raise exceptions.BadRequest("Invalid master_type {}".format(master_type)) from None
456
+ self.master = master
457
+
458
+ if self.master is not None and self.master_instance is None:
459
+ # master_instance might have been inherited from parent
460
+ if master_instance is None:
461
+ master_instance = self.get_master_instance(
462
+ self.master, master_key, master_type
463
+ )
464
+ self.master_instance = self.actor.cast_master_instance(master_instance)
442
465
 
443
466
  self.row_meta = dict(meta=True)
444
467
 
@@ -524,36 +547,11 @@ class BaseRequest:
524
547
  master_key=rqdata.get(constants.URL_PARAM_MASTER_PK, None),
525
548
  )
526
549
 
527
- # if settings.SITE.use_filterRow:
528
- # exclude = dict()
529
- # for f in self.ah.store.fields:
530
- # if f.field:
531
- # filterOption = rqdata.get(
532
- # 'filter[%s_filterOption]' % f.field.name)
533
- # if filterOption == 'empty':
534
- # kw[f.field.name + "__isnull"] = True
535
- # elif filterOption == 'notempty':
536
- # kw[f.field.name + "__isnull"] = False
537
- # else:
538
- # filterValue = rqdata.get('filter[%s]' % f.field.name)
539
- # if filterValue:
540
- # if not filterOption:
541
- # filterOption = 'contains'
542
- # if filterOption == 'contains':
543
- # kw[f.field.name + "__icontains"] = filterValue
544
- # elif filterOption == 'doesnotcontain':
545
- # exclude[f.field.name +
546
- # "__icontains"] = filterValue
547
- # else:
548
- # print("unknown filterOption %r" % filterOption)
549
- # if len(exclude):
550
- # kw.update(exclude=exclude)
551
-
552
550
  if settings.SITE.use_gridfilters:
553
- filter = rqdata.get(constants.URL_PARAM_GRIDFILTER, None)
554
- if filter is not None:
555
- filter = json.loads(filter)
556
- kw["gridfilters"] = [constants.dict2kw(flt) for flt in filter]
551
+ v = rqdata.get(constants.URL_PARAM_GRIDFILTER, None)
552
+ if v is not None:
553
+ v = json.loads(v)
554
+ kw["gridfilters"] = [constants.dict2kw(flt) for flt in v]
557
555
 
558
556
  # kw = ActionRequest.parse_req(self, request, rqdata, **kw)
559
557
  if settings.SITE.user_model:
@@ -954,6 +952,8 @@ class BaseRequest:
954
952
  )
955
953
  return
956
954
 
955
+ self.logger.info("Send email '%s' from %s to %s", subject, sender, recipients)
956
+
957
957
  recipients = [a for a in recipients if "@example.com" not in a]
958
958
  if not len(recipients):
959
959
  self.logger.info(
@@ -962,8 +962,6 @@ class BaseRequest:
962
962
  # self.logger.info("Email body would have been %s", body)
963
963
  return
964
964
 
965
- self.logger.info("Send email '%s' from %s to %s", subject, sender, recipients)
966
-
967
965
  kw = {}
968
966
  if body.startswith("<"):
969
967
  kw["html_message"] = body
@@ -1166,6 +1164,9 @@ class BaseRequest:
1166
1164
  def story2rst(self, story, *args, **kwargs):
1167
1165
  return self.renderer.show_story(self, story, *args, **kwargs)
1168
1166
 
1167
+ def shows(self, *args, **kwargs):
1168
+ return capture_output(self.show, *args, **kwargs)
1169
+
1169
1170
  def show(
1170
1171
  self,
1171
1172
  spec=None,
@@ -1175,6 +1176,7 @@ class BaseRequest:
1175
1176
  language=None,
1176
1177
  nosummary=False,
1177
1178
  stripped=True,
1179
+ show_links=False,
1178
1180
  show_urls=False,
1179
1181
  max_width=None,
1180
1182
  header_links=False,
@@ -1194,10 +1196,14 @@ class BaseRequest:
1194
1196
 
1195
1197
  :column_names: overrides default list of columns
1196
1198
 
1197
- :show_urls: show links and other html formatting. Used
1199
+ :show_links: show links and other html formatting. Used
1198
1200
  .e.g. in :ref:`avanti.specs.roles` where we want
1199
1201
  to show whether cells are clickable or not.
1200
1202
 
1203
+ :show_urls: show the real URLs. URLs in doctests are usually replaced by
1204
+ "…" to increase readability. If this is explicitly set to True, Lino
1205
+ prints the full URLs.
1206
+
1201
1207
  :nosummary: if it is a table with :attr:`display_mode
1202
1208
  <lino.core.tables.AbstractTable.display_mode>`
1203
1209
  set to ``((None, DISPLAY_MODE_SUMMARY), )``,
@@ -1253,6 +1259,8 @@ class BaseRequest:
1253
1259
  ar = self.spawn(spec, **kwargs)
1254
1260
  # return self.renderer.show_story(spec, **kwargs)
1255
1261
 
1262
+ ar.show_urls = show_urls
1263
+
1256
1264
  def doit():
1257
1265
  # print 20160530, ar.renderer
1258
1266
  if issubclass(ar.actor, Report):
@@ -1275,7 +1283,7 @@ class BaseRequest:
1275
1283
  nosummary=nosummary,
1276
1284
  stripped=stripped,
1277
1285
  max_width=max_width,
1278
- show_urls=show_urls,
1286
+ show_links=show_links,
1279
1287
  display_mode=display_mode,
1280
1288
  )
1281
1289
  elif isinstance(ar.bound_action.action, actions.ShowDetail):
@@ -1630,9 +1638,9 @@ class ActionRequest(BaseRequest):
1630
1638
  Holds information about an individual web request and provides
1631
1639
  methods like
1632
1640
 
1633
- - :meth:`get_user <lino.core.actions.BaseRequest.get_user>`
1634
- - :meth:`confirm <lino.core.actions.BaseRequest.confirm>`
1635
- - :meth:`spawn <lino.core.actions.BaseRequest.spawn>`
1641
+ - :meth:`get_user <lino.core.requests.BaseRequest.get_user>`
1642
+ - :meth:`confirm <lino.core.requests.BaseRequest.confirm>`
1643
+ - :meth:`spawn <lino.core.requests.BaseRequest.spawn>`
1636
1644
 
1637
1645
  An `ActionRequest` is also a :class:`BaseRequest` and inherits its
1638
1646
  methods.
@@ -1648,11 +1656,8 @@ class ActionRequest(BaseRequest):
1648
1656
  renderer = None
1649
1657
  offset = None
1650
1658
  limit = None
1651
- order_by = None
1652
1659
  master = None
1653
- extra = None
1654
1660
  title = None
1655
- filter = None
1656
1661
  limit = None
1657
1662
  offset = None
1658
1663
 
@@ -1688,25 +1693,14 @@ class ActionRequest(BaseRequest):
1688
1693
  known_values=None,
1689
1694
  param_values=None,
1690
1695
  action_param_values={},
1691
- quick_search=None,
1692
- order_by=None,
1693
1696
  offset=None,
1694
1697
  limit=None,
1695
1698
  title=None,
1696
- filter=None,
1697
- gridfilters=None,
1698
1699
  exclude=None,
1699
1700
  selected_pks=None,
1700
1701
  selected_rows=None,
1701
- extra=None,
1702
1702
  **kw
1703
1703
  ):
1704
- self.quick_search = quick_search
1705
- self.order_by = order_by
1706
- self.filter = filter
1707
- self.gridfilters = gridfilters
1708
- self.extra = extra
1709
-
1710
1704
  if title is not None:
1711
1705
  self.title = title
1712
1706
  if offset is not None:
@@ -1845,7 +1839,7 @@ class ActionRequest(BaseRequest):
1845
1839
  return "{0} {1}".format(self.__class__.__name__, self.bound_action)
1846
1840
 
1847
1841
  def gen_insert_button(
1848
- self, target, button_attrs=dict(style="float: right;"), **values
1842
+ self, target=None, button_attrs=dict(style="float: right;"), **values
1849
1843
  ):
1850
1844
  """
1851
1845
  Generate an insert button using a cached insertable object.
@@ -1860,13 +1854,26 @@ class ActionRequest(BaseRequest):
1860
1854
  The difference is that gen_insert_button is more efficient when you do
1861
1855
  this more than once during a single request.
1862
1856
 
1863
- `target` is the actor into which we want to insert an object.
1864
- `button_label` is the optional button label.
1857
+ `target` is the actor into which we want to insert an object. When this is `None`, Lino uses :attr:`self.actor`.
1858
+ `button_attrs` if given, are forwarded to :meth:`ar2button`.
1865
1859
  `values` is a dict of extra default values to apply to the insertable object.
1866
1860
 
1861
+ The `values` must be atomized by the caller, which is especially
1862
+ important when you want to set a :term:`foreign key` field. So instead
1863
+ of saying::
1864
+
1865
+ gen_insert_button(None, user=u)
1866
+
1867
+ you must say::
1868
+
1869
+ gen_insert_button(None, user=u.pk, userHidden=str(u))
1870
+
1867
1871
  First usage example is in :mod:`lino_xl.lib.calview`.
1872
+ Second usage example is :class:`lino_prima.lib.prima.PupilsAndProjects`.
1868
1873
 
1869
1874
  """
1875
+ if target is None:
1876
+ target = self.actor
1870
1877
  if self._insert_sar is None:
1871
1878
  self._insert_sar = target.insert_action.request_from(self)
1872
1879
  else:
@@ -1877,7 +1884,7 @@ class ActionRequest(BaseRequest):
1877
1884
  # obj = st['data_record']
1878
1885
  # for k, v in values.items():
1879
1886
  # setattr(obj, k, v)
1880
- # print(20200302, st['data_record'])
1887
+ # print(20241018, st['data_record'])
1881
1888
  return self._insert_sar.ar2button(**button_attrs)
1882
1889
 
1883
1890
  def run(self, *args, **kw):
@@ -2030,7 +2037,10 @@ class ActionRequest(BaseRequest):
2030
2037
  if self.create_kw:
2031
2038
  kw.update(self.create_kw)
2032
2039
  if self.known_values:
2033
- kw.update(self.known_values)
2040
+ # kw.update(self.known_values)
2041
+ for k, v in self.known_values.items():
2042
+ if not "__" in k:
2043
+ kw[k] = v
2034
2044
  obj = self.actor.create_instance(self, **kw)
2035
2045
  return obj
2036
2046
 
@@ -2143,8 +2153,8 @@ class ActionRequest(BaseRequest):
2143
2153
  # actor = self.actor
2144
2154
  # return super(ActorRequest, self).spawn(actor, **kw)
2145
2155
 
2146
- def row_as_summary(self, obj, *args, **kwargs):
2147
- return self.actor.row_as_summary(self, obj, *args, **kwargs)
2156
+ def row_as_summary(self, obj, text=None, **kwargs):
2157
+ return self.actor.row_as_summary(self, obj, text, **kwargs)
2148
2158
 
2149
2159
  def row_as_page(self, row, **kwargs):
2150
2160
  return self.actor.row_as_page(self, row, **kwargs)
@@ -2242,6 +2252,7 @@ class ActionRequest(BaseRequest):
2242
2252
  column_names=None,
2243
2253
  header_links=False,
2244
2254
  max_width=None, # ignored
2255
+ show_links=None, # ignored
2245
2256
  hide_sums=None,
2246
2257
  ):
2247
2258
  """
@@ -2431,12 +2442,12 @@ class ActionRequest(BaseRequest):
2431
2442
  if v is None:
2432
2443
  td = E.td(**cellattrs)
2433
2444
  else:
2445
+ td = col.value2html(self, v, **cellattrs)
2446
+ # print("20240506 {} {}".format(col.__class__, tostring(td)))
2434
2447
  nv = col.value2num(v)
2435
2448
  if nv != 0:
2436
2449
  sums[i] += nv
2437
2450
  has_numeric_value = True
2438
- td = col.value2html(self, v, **cellattrs)
2439
- # print("20240506 {} {}".format(col.__class__, tostring(td)))
2440
2451
  col.apply_cell_format(td)
2441
2452
  self.actor.apply_cell_format(self, row, col, recno, td)
2442
2453
  cells.append(td)