lino 24.9.3__py3-none-any.whl → 24.10.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.
Files changed (48) hide show
  1. lino/__init__.py +1 -3
  2. lino/api/doctest.py +18 -1
  3. lino/core/actions.py +15 -8
  4. lino/core/actors.py +83 -9
  5. lino/core/constants.py +20 -4
  6. lino/core/dashboard.py +1 -3
  7. lino/core/dbtables.py +41 -57
  8. lino/core/elems.py +26 -33
  9. lino/core/fields.py +20 -6
  10. lino/core/kernel.py +3 -3
  11. lino/core/model.py +1 -0
  12. lino/core/renderer.py +33 -20
  13. lino/core/requests.py +17 -18
  14. lino/core/site.py +1 -1
  15. lino/core/store.py +11 -11
  16. lino/core/tablerequest.py +5 -5
  17. lino/core/tables.py +39 -25
  18. lino/mixins/dupable.py +1 -1
  19. lino/modlib/checkdata/models.py +1 -1
  20. lino/modlib/comments/mixins.py +2 -2
  21. lino/modlib/comments/ui.py +83 -77
  22. lino/modlib/dupable/models.py +1 -1
  23. lino/modlib/export_excel/models.py +0 -3
  24. lino/modlib/extjs/config/extjs/linoweb.js +1 -1
  25. lino/modlib/extjs/ext_renderer.py +8 -8
  26. lino/modlib/extjs/views.py +6 -16
  27. lino/modlib/help/management/commands/makehelp.py +1 -1
  28. lino/modlib/memo/__init__.py +13 -9
  29. lino/modlib/notify/models.py +1 -1
  30. lino/modlib/printing/actions.py +0 -5
  31. lino/modlib/publisher/ui.py +3 -3
  32. lino/modlib/publisher/views.py +10 -10
  33. lino/modlib/search/models.py +2 -2
  34. lino/modlib/system/mixins.py +0 -10
  35. lino/modlib/uploads/mixins.py +6 -3
  36. lino/modlib/uploads/ui.py +2 -2
  37. lino/modlib/users/mixins.py +5 -5
  38. lino/sphinxcontrib/actordoc.py +1 -1
  39. lino/sphinxcontrib/logo/static/linodocs.css +1 -8
  40. lino/utils/ajax.py +25 -28
  41. lino/utils/choosers.py +1 -1
  42. lino/utils/report.py +1 -1
  43. {lino-24.9.3.dist-info → lino-24.10.0.dist-info}/METADATA +12 -11
  44. {lino-24.9.3.dist-info → lino-24.10.0.dist-info}/RECORD +47 -48
  45. lino/setup_info.py +0 -90
  46. {lino-24.9.3.dist-info → lino-24.10.0.dist-info}/WHEEL +0 -0
  47. {lino-24.9.3.dist-info → lino-24.10.0.dist-info}/licenses/AUTHORS.rst +0 -0
  48. {lino-24.9.3.dist-info → lino-24.10.0.dist-info}/licenses/COPYING +0 -0
@@ -78,9 +78,16 @@ class CommentDetail(dd.DetailLayout):
78
78
  class Comments(dd.Table):
79
79
  required_roles = dd.login_required(CommentsUser)
80
80
  model = "comments.Comment"
81
- display_mode = ((None, constants.DISPLAY_MODE_LIST),)
81
+ default_display_modes = {None: constants.DISPLAY_MODE_LIST}
82
+ # The idea is maybe good, but story mode has no insert button:
83
+ # default_display_modes = {
84
+ # 70: constants.DISPLAY_MODE_LIST,
85
+ # None: constants.DISPLAY_MODE_STORY}
86
+ extra_display_modes = {constants.DISPLAY_MODE_STORY}
87
+ # display mode "html" is not needed for comments
82
88
 
83
89
  params_layout = "start_date end_date observed_event user reply_to"
90
+ column_names = "id modified body_short_preview owner *"
84
91
 
85
92
  insert_layout = dd.InsertLayout(
86
93
  """
@@ -95,63 +102,63 @@ class Comments(dd.Table):
95
102
 
96
103
  detail_layout = "comments.CommentDetail"
97
104
 
98
- card_layout = dd.Panel(
99
- """
100
- # reply_to owner owner_type owner_id
101
- # comment_type
102
- body_short_preview
103
- # private
104
- """,
105
- label=_("Cards"),
106
- )
105
+ # card_layout = dd.Panel(
106
+ # """
107
+ # # reply_to owner owner_type owner_id
108
+ # # comment_type
109
+ # body_short_preview
110
+ # # private
111
+ # """,
112
+ # label=_("Cards"),
113
+ # )
107
114
 
108
115
  @classmethod
109
116
  def get_simple_parameters(cls):
110
- for p in super(Comments, cls).get_simple_parameters():
117
+ for p in super().get_simple_parameters():
111
118
  yield p
112
119
  yield "reply_to"
113
120
 
114
- @classmethod
115
- def get_card_title(cls, ar, obj):
116
- """Overrides the default behaviour"""
117
- return cls.get_comment_header(obj, ar)
118
- # title = _("Created {created} by {user}").format(
119
- # created=naturaltime(obj.created), user=str(obj.user))
120
- # if cls.get_view_permission(ar.get_user().user_type):
121
- # title = tostring(ar.obj2html(obj, title))
122
- # return title
123
-
124
- @classmethod
125
- def get_comment_header(cls, comment, ar):
126
- if (comment.modified - comment.created).total_seconds() < 1:
127
- t = _("Created " + comment.created.strftime("%Y-%m-%d %H:%M"))
128
- else:
129
- t = _("Modified " + comment.modified.strftime("%Y-%m-%d %H:%M"))
130
- ch = ar.obj2htmls(comment, naturaltime(comment.created), title=t)
131
- ch += " " + _("by") + " "
132
- if comment.user is None:
133
- ch += _("system")
134
- else:
135
- ch += ar.obj2htmls(comment.user)
136
-
137
- if cls.insert_action is not None:
138
- sar = cls.insert_action.request_from(ar)
139
- # print(20170217, sar)
140
- sar.known_values = dict(
141
- reply_to=comment, **gfk2lookup(comment.__class__.owner, comment.owner)
142
- )
143
- # if ar.get_user().is_authenticated:
144
- if sar.get_permission():
145
- btn = sar.ar2button(None, _(" Reply "), icon_name=None)
146
- # btn.set("style", "padding-left:10px")
147
- ch += " [" + tostring(btn) + "]"
148
-
149
- # ch.append(' ')
150
- # ch.append(
151
- # E.a(u"⁜", onclick="toggle_visibility('comment-{}');".format(
152
- # comment.id), title=str(_("Hide")), href="#")
153
- # )
154
- return ch
121
+ # @classmethod
122
+ # def get_card_title(cls, ar, obj):
123
+ # """Overrides the default behaviour"""
124
+ # return cls.get_comment_header(obj, ar)
125
+ # # title = _("Created {created} by {user}").format(
126
+ # # created=naturaltime(obj.created), user=str(obj.user))
127
+ # # if cls.get_view_permission(ar.get_user().user_type):
128
+ # # title = tostring(ar.obj2html(obj, title))
129
+ # # return title
130
+
131
+ # @classmethod
132
+ # def get_comment_header(cls, comment, ar):
133
+ # if (comment.modified - comment.created).total_seconds() < 1:
134
+ # t = _("Created " + comment.created.strftime("%Y-%m-%d %H:%M"))
135
+ # else:
136
+ # t = _("Modified " + comment.modified.strftime("%Y-%m-%d %H:%M"))
137
+ # ch = ar.obj2htmls(comment, naturaltime(comment.created), title=t)
138
+ # ch += " " + _("by") + " "
139
+ # if comment.user is None:
140
+ # ch += _("system")
141
+ # else:
142
+ # ch += ar.obj2htmls(comment.user)
143
+ #
144
+ # if cls.insert_action is not None:
145
+ # sar = cls.insert_action.request_from(ar)
146
+ # # print(20170217, sar)
147
+ # sar.known_values = dict(
148
+ # reply_to=comment, **gfk2lookup(comment.__class__.owner, comment.owner)
149
+ # )
150
+ # # if ar.get_user().is_authenticated:
151
+ # if sar.get_permission():
152
+ # btn = sar.ar2button(None, _(" Reply "), icon_name=None)
153
+ # # btn.set("style", "padding-left:10px")
154
+ # ch += " [" + tostring(btn) + "]"
155
+ #
156
+ # # ch.append(' ')
157
+ # # ch.append(
158
+ # # E.a(u"⁜", onclick="toggle_visibility('comment-{}');".format(
159
+ # # comment.id), title=str(_("Hide")), href="#")
160
+ # # )
161
+ # return ch
155
162
 
156
163
 
157
164
  class MyComments(My, Comments):
@@ -174,14 +181,14 @@ class CommentsByX(Comments):
174
181
  class RecentComments(CommentsByX):
175
182
  # required_roles = dd.login_required(CommentsReader)
176
183
  # required_roles = set([CommentsReader])
184
+ # order_by = ["-modified"]
185
+ label = _("Recent comments")
177
186
  allow_create = False
178
187
  column_names = "body_short_preview modified user owner *"
179
188
  stay_in_grid = True
180
189
  live_panel_update = True
181
- # order_by = ["-modified"]
182
- label = _("Recent comments")
183
190
  preview_limit = 10
184
- # display_mode = ((None, constants.DISPLAY_MODE_SUMMARY), )
191
+ # default_display_modes = {None: constants.DISPLAY_MODE_SUMMARY}
185
192
 
186
193
 
187
194
  class CommentsByType(CommentsByX):
@@ -217,26 +224,26 @@ class CommentsByRFC(CommentsByX):
217
224
  kw["reply_to"] = constants.CHOICES_BLANK_FILTER_VALUE
218
225
  return kw
219
226
 
220
- @classmethod
221
- def get_main_card(self, ar):
222
- ticket_obj = ar.master_instance
223
- if ticket_obj is None:
224
- return None
225
- sar = self.request(parent=ar, master_instance=ticket_obj)
226
- html = ticket_obj.get_rfc_description(ar)
227
- sar = self.insert_action.request_from(sar)
228
- if sar.get_permission():
229
- btn = sar.ar2button(None, _("Write comment"), icon_name=None)
230
- html += "<p>" + tostring(btn) + "</p>"
231
-
232
- if html:
233
- return dict(
234
- card_title="Description",
235
- main_card_body=html, # main_card_body is special keyword
236
- id="[main_card]", # needed for map key in react...
237
- )
238
- else:
239
- return None
227
+ # @classmethod
228
+ # def get_main_card(self, ar):
229
+ # ticket_obj = ar.master_instance
230
+ # if ticket_obj is None:
231
+ # return None
232
+ # sar = self.request(parent=ar, master_instance=ticket_obj)
233
+ # html = ticket_obj.get_rfc_description(ar)
234
+ # sar = self.insert_action.request_from(sar)
235
+ # if sar.get_permission():
236
+ # btn = sar.ar2button(None, _("Write comment"), icon_name=None)
237
+ # html += "<p>" + tostring(btn) + "</p>"
238
+ #
239
+ # if html:
240
+ # return dict(
241
+ # card_title="Description",
242
+ # main_card_body=html, # main_card_body is special keyword
243
+ # id="[main_card]", # needed for map key in react...
244
+ # )
245
+ # else:
246
+ # return None
240
247
 
241
248
 
242
249
  class CommentsByMentioned(CommentsByX):
@@ -274,7 +281,6 @@ class RepliesByComment(CommentsByX):
274
281
  live_panel_update = True
275
282
  label = _("Replies")
276
283
  simple_slavegrid_header = True
277
-
278
284
  paginator_template = "PrevPageLink NextPageLink"
279
285
  hide_if_empty = True
280
286
 
@@ -292,7 +298,7 @@ class Reactions(dd.Table):
292
298
 
293
299
  class ReactionsByComment(Reactions):
294
300
  master_key = "comment"
295
- display_mode = ((None, constants.DISPLAY_MODE_SUMMARY),)
301
+ default_display_modes = {None: constants.DISPLAY_MODE_SUMMARY}
296
302
 
297
303
 
298
304
  from lino.modlib.publisher.choicelists import PageFillers
@@ -116,7 +116,7 @@ class SimilarObjects(dd.VirtualTable):
116
116
  """Shows the other objects that are similar to this one."""
117
117
 
118
118
  label = _("Similar objects")
119
- display_mode = ((None, constants.DISPLAY_MODE_SUMMARY),)
119
+ default_display_modes = {None: constants.DISPLAY_MODE_SUMMARY}
120
120
  master = Dupable
121
121
 
122
122
  @classmethod
@@ -128,9 +128,6 @@ class ExportExcelAction(actions.Action):
128
128
  callable_from = "t"
129
129
  required_roles = dd.login_required(DataExporter)
130
130
 
131
- # def is_callable_from(self, caller):
132
- # return isinstance(caller, actions.ShowTable)
133
-
134
131
  def run_from_ui(self, ar, **kw):
135
132
  # Prepare tmp file
136
133
  mf = TmpMediaFile(ar, "xlsx")
@@ -3153,7 +3153,7 @@ Lino.FormPanel = Ext.extend(Lino.FormPanel,{
3153
3153
  }
3154
3154
 
3155
3155
  ,set_current_record : function(record, after) {
3156
- console.log('20160825 set_current_record', record);
3156
+ // console.log('20160825 set_current_record', record);
3157
3157
  if (this.record_selector) {
3158
3158
  this.record_selector.clearValue();
3159
3159
  // e.g. InsertWrapper FormPanel doesn't have a record_selector
@@ -1207,7 +1207,7 @@ class ExtRenderer(JsCacheRenderer):
1207
1207
  if not hasattr(rh, "store"):
1208
1208
  raise AttributeError("20200128 {} has no store".format(rh))
1209
1209
  kw.update(
1210
- ls_store_fields=[js_code(f.as_js(f.name)) for f in rh.store.list_fields]
1210
+ ls_store_fields=[js_code(f.as_js(f.name)) for f in rh.store.grid_fields]
1211
1211
  )
1212
1212
  if rh.store.pk is not None:
1213
1213
  kw.update(ls_id_property=rh.store.pk.name)
@@ -1280,17 +1280,17 @@ class ExtRenderer(JsCacheRenderer):
1280
1280
  yield " this.ls_insert_handler = Lino.%s;" % a.full_name()
1281
1281
 
1282
1282
  yield " var ww = this.containing_window;"
1283
- if not hasattr(rh, "list_layout"):
1284
- raise AttributeError("20200128 {} has no list_layout".format(rh))
1285
- for ln in jsgen.declare_vars(rh.list_layout.main.columns):
1283
+ if not hasattr(rh, "grid_layout"):
1284
+ raise AttributeError("20200128 {} has no grid_layout".format(rh))
1285
+ for ln in jsgen.declare_vars(rh.grid_layout.main.columns):
1286
1286
  yield " " + ln
1287
1287
 
1288
1288
  yield " this.before_row_edit = function(record) {"
1289
- for ln in self.before_row_edit(rh.list_layout.main):
1289
+ for ln in self.before_row_edit(rh.grid_layout.main):
1290
1290
  yield " " + ln
1291
1291
  yield " };"
1292
1292
 
1293
- on_render = self.build_on_render(rh.list_layout.main)
1293
+ on_render = self.build_on_render(rh.grid_layout.main)
1294
1294
  if on_render:
1295
1295
  yield " this.onRender = function(ct, position) {"
1296
1296
  for ln in on_render:
@@ -1303,8 +1303,8 @@ class ExtRenderer(JsCacheRenderer):
1303
1303
 
1304
1304
  yield " this.ls_columns = %s;" % py2js(
1305
1305
  [
1306
- ext_elems.GridColumn(rh.list_layout, i, e)
1307
- for i, e in enumerate(rh.list_layout.main.columns)
1306
+ ext_elems.GridColumn(rh.grid_layout, i, e)
1307
+ for i, e in enumerate(rh.grid_layout.main.columns)
1308
1308
  ]
1309
1309
  )
1310
1310
 
@@ -20,9 +20,6 @@ Summary from <http://en.wikipedia.org/wiki/Restful>:
20
20
  The ID created is included as part of the data returned by this operation.
21
21
  - DELETE : Delete the entire collection.
22
22
 
23
-
24
-
25
-
26
23
  """
27
24
  import json
28
25
  import os
@@ -50,24 +47,17 @@ from lino.core.utils import obj2unicode
50
47
  from lino.utils.html import E, tostring
51
48
  from etgen.html import Document
52
49
 
53
- # E = xghtml.E
54
-
55
50
  from lino.utils import ucsv
56
51
  from lino.utils import dblogger
57
-
58
52
  from lino.core import constants
59
53
  from lino.core import actions
60
54
  from lino.core import fields
61
55
  from lino.core.fields import choices_for_field
62
-
63
56
  from lino.core.views import requested_actor, action_request
64
57
  from lino.core.views import json_response, json_response_kw
65
58
  from lino.core.views import choices_response
66
-
67
59
  from lino.core.requests import BaseRequest, PhantomRow
68
60
 
69
- # from lino.core import callbacks
70
-
71
61
  MAX_ROW_COUNT = 300
72
62
 
73
63
 
@@ -279,9 +269,9 @@ class Restful(View):
279
269
  data = json.loads(data)
280
270
  ar.form2obj_and_save(data, instance, True)
281
271
 
282
- # Ext.ensible needs list_fields, not detail_fields
272
+ # Ext.ensible needs grid_fields, not detail_fields
283
273
  ar.set_response(
284
- rows=[ar.ah.store.row2dict(ar, instance, ar.ah.store.list_fields)]
274
+ rows=[ar.ah.store.row2dict(ar, instance, ar.ah.store.grid_fields)]
285
275
  )
286
276
  return json_response(ar.response)
287
277
 
@@ -298,7 +288,7 @@ class Restful(View):
298
288
  ar = rpt.request(request=request)
299
289
  rh = ar.ah
300
290
  rows = [
301
- rh.store.row2dict(ar, row, rh.store.list_fields)
291
+ rh.store.row2dict(ar, row, rh.store.grid_fields)
302
292
  for row in ar.sliced_data_iterator
303
293
  ]
304
294
  kw = dict(count=ar.get_total_count(), rows=rows)
@@ -320,8 +310,8 @@ class Restful(View):
320
310
  ar = rpt.request(request=request, action=a)
321
311
  ar.renderer = settings.SITE.kernel.extjs_renderer
322
312
  ar.form2obj_and_save(data, elem, False)
323
- # Ext.ensible needs list_fields, not detail_fields
324
- ar.set_response(rows=[rh.store.row2dict(ar, elem, rh.store.list_fields)])
313
+ # Ext.ensible needs grid_fields, not detail_fields
314
+ ar.set_response(rows=[rh.store.row2dict(ar, elem, rh.store.grid_fields)])
325
315
  return json_response(ar.response)
326
316
 
327
317
 
@@ -340,7 +330,7 @@ class ApiElement(View):
340
330
  # if not rpt.get_view_permission(request.user.user_type):
341
331
  # raise PermissionDenied("{} has permission to view {}".format(
342
332
  # request.user.user_type, rpt))
343
- # print("20210530", rpt, request.user.user_type)
333
+ # raise Exception(f"20241001 {rpt} {request.user.user_type}")
344
334
 
345
335
  action_name = request.GET.get(constants.URL_PARAM_ACTION_NAME, None)
346
336
  if action_name:
@@ -481,7 +481,7 @@ class Command(GeneratingCommand):
481
481
  # s += show(_("Columns"), rpt.get_handle().get_columns())
482
482
  s += show(_("Columns"), visible)
483
483
  # s += rubric(_("Columns"))
484
- # s += rstgen.ul([elem2par(e) for e in rpt.list_layout.walk()])
484
+ # s += rstgen.ul([elem2par(e) for e in rpt.grid_layout.walk()])
485
485
  # s += rstgen.ul([elem2par(f) for f in rpt.wildcard_data_elems()])
486
486
 
487
487
  # s += show(_("Other fields"),
@@ -12,8 +12,8 @@ Adds functionality for using memo commands in your text fields.
12
12
 
13
13
  """
14
14
 
15
- from importlib import import_module
16
- from rstgen.utils import py2url_txt
15
+ # from importlib import import_module
16
+ # from rstgen.utils import py2url_txt
17
17
  from lino.api import ad
18
18
  from .parser import Parser, split_name_rest
19
19
  from lino.utils.html import tostring
@@ -76,14 +76,18 @@ class Plugin(ad.Plugin):
76
76
 
77
77
  self.parser.register_command("url", url2html)
78
78
 
79
- def py2html(parser, s, cmdname, mentions, context):
80
- url, txt = py2url_txt(s)
81
- if url:
82
- # lines = inspect.getsourcelines(s)
83
- return '<a href="{0}" target="_blank">{1}</a>'.format(url, txt)
84
- return "<pre>{}</pre>".format(s)
79
+ # 20240920 I disabled the "py" memo command because I don't know anybody
80
+ # who used it (except myself a few times for testing it) and because it
81
+ # requires SETUP_INFO, which has an uncertain future.
85
82
 
86
- self.parser.register_command("py", py2html)
83
+ # def py2html(parser, s, cmdname, mentions, context):
84
+ # url, txt = py2url_txt(s)
85
+ # if url:
86
+ # # lines = inspect.getsourcelines(s)
87
+ # return '<a href="{0}" target="_blank">{1}</a>'.format(url, txt)
88
+ # return "<pre>{}</pre>".format(s)
89
+ #
90
+ # self.parser.register_command("py", py2html)
87
91
 
88
92
  def show2html(ar, s, cmdname, mentions, context):
89
93
  # kwargs = dict(header_level=3) #, nosummary=True)
@@ -383,7 +383,7 @@ class MyMessages(My, Messages):
383
383
  created_order = "-created"
384
384
  order_by = [created_order]
385
385
  # hide_headers = True
386
- display_mode = ((None, constants.DISPLAY_MODE_SUMMARY),)
386
+ default_display_modes = {None: constants.DISPLAY_MODE_SUMMARY}
387
387
 
388
388
  @classmethod
389
389
  def unused_get_table_summary(cls, mi, ar):
@@ -52,11 +52,6 @@ class BasePrintAction(Action):
52
52
  # logger.info("20140401 attach_to_actor() %r", self)
53
53
  return super(BasePrintAction, self).attach_to_actor(actor, name)
54
54
 
55
- # def is_callable_from(self, caller):
56
- # # including ShowEmptyTable which is subclass of
57
- # # ShowDetail. But not callable from ShowInsert.
58
- # return isinstance(caller, (ShowTable, ShowDetail))
59
-
60
55
  def get_print_templates(self, bm, elem):
61
56
  # print("20190506 BasePrintAction.get_print_templates", elem)
62
57
  return elem.get_print_templates(bm, self)
@@ -147,7 +147,7 @@ class Pages(dd.Table):
147
147
  ref
148
148
  #page_type filler
149
149
  """
150
- display_mode = ((None, constants.DISPLAY_MODE_STORY),)
150
+ default_display_modes = {None: constants.DISPLAY_MODE_STORY}
151
151
 
152
152
 
153
153
  class PagesByParent(Pages):
@@ -156,7 +156,7 @@ class PagesByParent(Pages):
156
156
  # ~ column_names = "title user *"
157
157
  order_by = ["seqno"]
158
158
  column_names = "seqno title *"
159
- display_mode = ((None, constants.DISPLAY_MODE_LIST),)
159
+ default_display_modes = {None: constants.DISPLAY_MODE_LIST}
160
160
 
161
161
 
162
162
  # PublisherViews.add_item_lazy("p", Pages)
@@ -169,7 +169,7 @@ class TranslationsByPage(Pages):
169
169
  master_key = "translated_from"
170
170
  label = _("Translations")
171
171
  column_names = "ref title language id *"
172
- display_mode = ((None, constants.DISPLAY_MODE_SUMMARY),)
172
+ default_display_modes = {None: constants.DISPLAY_MODE_SUMMARY}
173
173
 
174
174
  @classmethod
175
175
  def row_as_summary(cls, ar, obj, **kwargs):
@@ -9,6 +9,7 @@ from django.views.generic import View
9
9
  from lino.api import dd
10
10
  from lino.core import auth
11
11
  from lino.core.requests import BaseRequest, ActionRequest
12
+ from django.core.exceptions import ObjectDoesNotExist
12
13
 
13
14
  from django.shortcuts import redirect
14
15
  from django.views.decorators.csrf import ensure_csrf_cookie
@@ -21,8 +22,8 @@ class Element(View):
21
22
 
22
23
  def get(self, request, pk=None):
23
24
  # print("20220927 a get()")
24
- if pk is None:
25
- return http.HttpResponseNotFound()
25
+ # if pk is None:
26
+ # return http.HttpResponseNotFound()
26
27
  # rnd = settings.SITE.kernel.default_renderer
27
28
  rnd = settings.SITE.plugins.publisher.renderer
28
29
 
@@ -33,15 +34,14 @@ class Element(View):
33
34
  # if rnd.front_end.media_name == 'react':
34
35
  # kw.update(hash_router=True)
35
36
 
36
- # kw.update(selected_pks=[pk])
37
+ kw.update(selected_pks=[pk])
37
38
 
38
- # ar = self.publisher_view.table_class.request(request=request, **kw)
39
- ar = self.table_class.request(request=request, **kw)
40
- # ar = self.actor.request(request=request, **kw)
41
- obj = ar.get_row_by_pk(pk)
42
- if obj is None:
43
- return http.HttpResponseNotFound()
44
- ar.selected_rows = [obj]
39
+ try:
40
+ ar = self.table_class.request(request=request, **kw)
41
+ except ObjectDoesNotExist as e:
42
+ # print("20240911", e)
43
+ return http.HttpResponseNotFound(f"No row #{pk} in {self.table_class}")
44
+ obj = ar.selected_rows[0]
45
45
  return obj.get_publisher_response(ar)
46
46
 
47
47
 
@@ -25,7 +25,7 @@ class SiteSearchBase(dd.VirtualTable):
25
25
  column_names = "description matches"
26
26
  private_apps = frozenset(["sessions", "contenttypes", "users"])
27
27
 
28
- display_mode = ((None, constants.DISPLAY_MODE_STORY),)
28
+ default_display_modes = {None: constants.DISPLAY_MODE_STORY}
29
29
 
30
30
  card_layout = """description"""
31
31
  list_layout = """
@@ -136,7 +136,7 @@ class SiteSearchBase(dd.VirtualTable):
136
136
 
137
137
 
138
138
  class SiteSearch(SiteSearchBase):
139
- display_mode = ((None, constants.DISPLAY_MODE_LIST),)
139
+ default_display_modes = {None: constants.DISPLAY_MODE_LIST}
140
140
 
141
141
  @classmethod
142
142
  def get_data_rows(cls, ar):
@@ -107,11 +107,6 @@ class Lockable(EditSafe):
107
107
  resolve_fields_list(cls, "lockable_fields")
108
108
  cls.lockable_fields = set([f.name for f in cls.lockable_fields])
109
109
 
110
- # @dd.action(_("Edit"), sort_index=100, callable_from='d')
111
- # def acquire_lock(self, ar):
112
- # self.lock_row(ar)
113
- # ar.success(refresh=True)
114
-
115
110
  def before_ui_edit(self, ar, **kwargs):
116
111
  self.lock_row(ar)
117
112
  ar.success(refresh=True)
@@ -131,11 +126,6 @@ class Lockable(EditSafe):
131
126
  )
132
127
  self._disabled_fields = None # clear cache
133
128
 
134
- # @dd.action(_("Abort"), sort_index=100, auto_save=None, callable_from='d')
135
- # def release_lock(self, ar):
136
- # self.unlock_row(ar)
137
- # ar.success(refresh=True)
138
-
139
129
  def on_ui_abort(self, ar, **kwargs):
140
130
  self.unlock_row(ar)
141
131
  ar.success(refresh=True)
@@ -1,5 +1,5 @@
1
1
  # -*- coding: UTF-8 -*-
2
- # Copyright 2008-2023 Rumma & Ko Ltd
2
+ # Copyright 2008-2024 Rumma & Ko Ltd
3
3
  # License: GNU Affero General Public License v3 (see file COPYING for details)
4
4
 
5
5
  import os
@@ -13,11 +13,12 @@ from django.core.files.storage import default_storage
13
13
  from django.core.exceptions import ValidationError, FieldError
14
14
  from django.template.defaultfilters import filesizeformat
15
15
 
16
- from lino.modlib.uploads import UPLOADS_ROOT
16
+ from rstgen.sphinxconf.sigal_image import parse_image_spec
17
+ from lino.core import constants
17
18
  from lino.utils import DATE_TO_DIR_TPL
18
19
  from lino.utils.html import E
19
- from rstgen.sphinxconf.sigal_image import parse_image_spec
20
20
  from lino.api import dd, rt, _
21
+ from lino.modlib.uploads import UPLOADS_ROOT
21
22
  from lino.modlib.memo.mixins import MemoReferrable
22
23
 
23
24
  # from lino.mixins.sequenced import Sequenced
@@ -133,6 +134,8 @@ class UploadBase(MemoReferrable, GalleryViewable):
133
134
  class Meta(object):
134
135
  abstract = True
135
136
 
137
+ extra_display_modes = {constants.DISPLAY_MODE_GALLERY}
138
+
136
139
  file = models.FileField(_("File"), blank=True, upload_to=upload_to_tpl)
137
140
  mimetype = models.CharField(
138
141
  _("MIME type"), blank=True, max_length=255, editable=False)
lino/modlib/uploads/ui.py CHANGED
@@ -33,7 +33,7 @@ class Volumes(dd.Table):
33
33
  ref root_dir
34
34
  description
35
35
  """
36
-
36
+
37
37
  detail_layout = """
38
38
  ref root_dir
39
39
  description
@@ -146,7 +146,7 @@ class AreaUploads(Uploads):
146
146
  # required_roles = dd.login_required((OfficeUser, OfficeOperator))
147
147
  required_roles = dd.login_required(UploadsReader)
148
148
  stay_in_grid = True
149
- display_mode = ((None, constants.DISPLAY_MODE_SUMMARY),)
149
+ default_display_modes = {None: constants.DISPLAY_MODE_SUMMARY}
150
150
 
151
151
  # 20180119
152
152
  # @classmethod
@@ -65,11 +65,11 @@ class Authored(Printable):
65
65
  return ba.action.readonly
66
66
  return True
67
67
 
68
- @classmethod
69
- def on_analyze(cls, site):
70
- if hasattr(cls, "manager_level_field"):
71
- raise ChangedAPI("{0} has a manager_level_field".format(cls))
72
- super().on_analyze(site)
68
+ # @classmethod
69
+ # def on_analyze(cls, site):
70
+ # if hasattr(cls, "manager_level_field"):
71
+ # raise ChangedAPI("{0} has a manager_level_field".format(cls))
72
+ # super().on_analyze(site)
73
73
 
74
74
  # no longer needed after 20170826
75
75
  # @classmethod
@@ -199,7 +199,7 @@ def get_actor_description(self):
199
199
  if self.help_text:
200
200
  body += unindent(force_str(self.help_text).strip()) + "\n\n"
201
201
 
202
- # ~ ll = self.get_handle().list_layout
202
+ # ~ ll = self.get_handle().grid_layout
203
203
  # ~ if ll is not None:
204
204
  # ~ body += fields_table([ e.field for e in ll.main.columns] )
205
205
 
@@ -41,7 +41,7 @@ table.scrollwide td {
41
41
  }
42
42
 
43
43
  /* centered image captions would be nice. doesn't work.
44
- e.g. http://lino-framework.org/tutorials/layouts.html
44
+ e.g. https://lino-framework.org/tutorials/layouts.html
45
45
  */
46
46
  p.caption {
47
47
  width: 100%;
@@ -61,10 +61,3 @@ div.body {
61
61
  color: #3E4349;
62
62
  padding: 30px 30px;
63
63
  }
64
-
65
- img.pi-button {
66
- width: 1.1em;
67
- height: 1.1em;
68
- background-color: LightSkyBlue;
69
- border: 3px solid LightSkyBlue;
70
- }