lino 25.1.5__py3-none-any.whl → 25.2.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 (41) hide show
  1. lino/__init__.py +1 -1
  2. lino/core/actions.py +10 -3
  3. lino/core/actors.py +9 -3
  4. lino/core/callbacks.py +3 -2
  5. lino/core/fields.py +4 -0
  6. lino/core/keyboard.py +7 -2
  7. lino/core/model.py +1 -0
  8. lino/core/requests.py +7 -7
  9. lino/core/site.py +1 -2
  10. lino/core/store.py +2 -2
  11. lino/help_texts.py +4 -1
  12. lino/locale/bn/LC_MESSAGES/django.po +782 -710
  13. lino/locale/de/LC_MESSAGES/django.mo +0 -0
  14. lino/locale/de/LC_MESSAGES/django.po +1259 -1280
  15. lino/locale/django.pot +751 -702
  16. lino/locale/es/LC_MESSAGES/django.po +777 -708
  17. lino/locale/et/LC_MESSAGES/django.po +784 -709
  18. lino/locale/fr/LC_MESSAGES/django.po +1339 -1191
  19. lino/locale/nl/LC_MESSAGES/django.po +787 -712
  20. lino/locale/pt_BR/LC_MESSAGES/django.po +769 -700
  21. lino/locale/zh_Hant/LC_MESSAGES/django.po +769 -700
  22. lino/management/commands/demotest.py +7 -3
  23. lino/mixins/__init__.py +1 -1
  24. lino/modlib/checkdata/choicelists.py +5 -4
  25. lino/modlib/checkdata/models.py +9 -8
  26. lino/modlib/comments/fixtures/demo2.py +4 -2
  27. lino/modlib/help/models.py +5 -0
  28. lino/modlib/jinja/__init__.py +0 -4
  29. lino/modlib/memo/__init__.py +1 -1
  30. lino/modlib/periods/mixins.py +1 -25
  31. lino/modlib/periods/models.py +42 -9
  32. lino/modlib/system/choicelists.py +12 -11
  33. lino/utils/config.py +2 -0
  34. lino/utils/dbfreader.py +18 -24
  35. lino/utils/dpy.py +15 -3
  36. lino/utils/soup.py +136 -103
  37. {lino-25.1.5.dist-info → lino-25.2.0.dist-info}/METADATA +1 -1
  38. {lino-25.1.5.dist-info → lino-25.2.0.dist-info}/RECORD +41 -41
  39. {lino-25.1.5.dist-info → lino-25.2.0.dist-info}/WHEEL +0 -0
  40. {lino-25.1.5.dist-info → lino-25.2.0.dist-info}/licenses/AUTHORS.rst +0 -0
  41. {lino-25.1.5.dist-info → lino-25.2.0.dist-info}/licenses/COPYING +0 -0
lino/__init__.py CHANGED
@@ -26,7 +26,7 @@ defines no models, some template files, a series of :term:`django-admin commands
26
26
 
27
27
  """
28
28
 
29
- __version__ = '25.1.5'
29
+ __version__ = '25.2.0'
30
30
 
31
31
  # import setuptools # avoid UserWarning "Distutils was imported before Setuptools"?
32
32
 
lino/core/actions.py CHANGED
@@ -1069,6 +1069,9 @@ class CreateRow(Action):
1069
1069
 
1070
1070
  # select_rows = False
1071
1071
  def run_from_ui(self, ar, **kwargs):
1072
+ if (msg := ar.actor.model.disable_create(ar)) is not None:
1073
+ ar.error(msg)
1074
+ return
1072
1075
  elem = ar.create_instance_from_request(**kwargs)
1073
1076
  self.save_new_instance(ar, elem)
1074
1077
 
@@ -1088,6 +1091,7 @@ class CreateRow(Action):
1088
1091
  # if ar.actor.stay_in_grid and ar.requesting_panel:
1089
1092
  if ar.actor.stay_in_grid:
1090
1093
  # do not open a detail window on the new instance
1094
+ ar.set_response(refresh_all=True)
1091
1095
  return
1092
1096
 
1093
1097
  ar.goto_instance(elem)
@@ -1122,7 +1126,7 @@ class CreateRow(Action):
1122
1126
 
1123
1127
  ar.goto_instance(elems[0])
1124
1128
 
1125
- # No need to ask refresh_all since closing the window will
1129
+ # No need to ask refresh_all since closing the detail window will
1126
1130
  # automatically refresh the underlying window.
1127
1131
 
1128
1132
 
@@ -1139,6 +1143,9 @@ class SubmitInsert(CreateRow):
1139
1143
  # button actions would try to refer the requesting panel which
1140
1144
  # is going to be closed (this disturbs at least in ticket
1141
1145
  # #219)
1146
+ if (msg := ar.actor.model.disable_create(ar)) is not None:
1147
+ ar.error(msg)
1148
+ return
1142
1149
  ar.requesting_panel = None
1143
1150
 
1144
1151
  if ar.actor.handle_uploaded_files is not None:
@@ -1155,11 +1162,11 @@ class SubmitInsert(CreateRow):
1155
1162
  # Multiple uploads possible, note plural method names.
1156
1163
  elems = ar.create_instances_from_request(**kwargs)
1157
1164
  self.save_new_instances(ar, elems)
1158
- ar.set_response(close_window=True)
1159
1165
  else:
1160
1166
  elem = ar.create_instance_from_request(**kwargs)
1161
1167
  self.save_new_instance(ar, elem)
1162
- ar.set_response(close_window=True)
1168
+
1169
+ ar.set_response(close_window=True)
1163
1170
  # if settings.SITE.is_installed("react"):
1164
1171
  # ar.goto_instance(elem)
1165
1172
 
lino/core/actors.py CHANGED
@@ -975,10 +975,16 @@ class Actor(actions.Parametrizable, Permittable, metaclass=ActorMetaClass):
975
975
 
976
976
  @classmethod
977
977
  def get_create_permission(self, ar):
978
- if not settings.SITE.user_types_module:
979
- return True
980
- if ar.get_user().user_type.readonly:
978
+ if not self.allow_create:
979
+ return False
980
+ if settings.SITE.readonly:
981
981
  return False
982
+ if settings.SITE.user_types_module:
983
+ user = ar.get_user()
984
+ if user.user_type.readonly:
985
+ return False
986
+ if self.hide_editing(user.user_type):
987
+ return False
982
988
  return True
983
989
 
984
990
  @classmethod
lino/core/callbacks.py CHANGED
@@ -52,9 +52,10 @@ class Callback(object):
52
52
  self.choices_dict = {}
53
53
  self.ar = ar
54
54
 
55
- str_msg = tostring(message).encode()
55
+ str_msg = f"{ar.get_user().username}:{tostring(message)}"
56
+
56
57
  # self.uid = uid if uid is not None else str(md5(str_msg).digest())
57
- self.uid = uid if uid is not None else md5(str_msg).hexdigest()
58
+ self.uid = uid if uid is not None else md5(str_msg.encode()).hexdigest()
58
59
  # 20240407 .digest() returned bytestrings, yielding strings of style
59
60
  # "b'8\x04\xeb\xf53\x9f\xf2\x90\x82l\x81~s\xe8\x90\xa6'"
60
61
 
lino/core/fields.py CHANGED
@@ -1299,6 +1299,10 @@ class TableRow(object):
1299
1299
  # return v
1300
1300
  # raise Exception("Oops, {} on {} is {}".format(name, cls, v))
1301
1301
 
1302
+ @classmethod
1303
+ def disable_create(self, ar):
1304
+ return None
1305
+
1302
1306
  def get_detail_action(self, ar):
1303
1307
  """Return the (bound) detail action to use for showing this object in
1304
1308
  a detail window. Return `None` when no detail form exists or
lino/core/keyboard.py CHANGED
@@ -6,6 +6,12 @@ This defines the :class:`Hotkey` class and some keystrokes.
6
6
 
7
7
  The system is not yet heavily used.
8
8
 
9
+ React uses the attributes `ctrl`, `shift`, `alt` and `code`.
10
+
11
+ For the `code`, see:
12
+ https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code
13
+
14
+
9
15
  """
10
16
 
11
17
 
@@ -23,7 +29,7 @@ class Hotkey(object):
23
29
  if key:
24
30
  self.key = key.upper()
25
31
  self.keycode = ord(self.key)
26
- for k, v in list(kw.items()):
32
+ for k, v in kw.items():
27
33
  setattr(self, k, v)
28
34
 
29
35
  self.__dict__.update(
@@ -35,7 +41,6 @@ class Hotkey(object):
35
41
  alt=self.alt,
36
42
  )
37
43
 
38
-
39
44
  # ExtJS src/core/EventManager-more.js
40
45
  RETURN = Hotkey(keycode=13, code="Enter")
41
46
  ESCAPE = Hotkey(keycode=27, code="Escape")
lino/core/model.py CHANGED
@@ -969,6 +969,7 @@ LINO_MODEL_ATTRIBS = (
969
969
  "on_create",
970
970
  "error2str",
971
971
  "print_subclasses_graph",
972
+ "disable_create",
972
973
  "grid_post",
973
974
  "submit_insert",
974
975
  "delete_veto_message",
lino/core/requests.py CHANGED
@@ -2061,20 +2061,20 @@ class ActionRequest(BaseRequest):
2061
2061
  return
2062
2062
  # if self.actor.insert_layout is not None: # and not self.actor.stay_in_grid \
2063
2063
  # return
2064
- if (
2065
- self.create_kw is None
2066
- or self.actor.hide_editing(self.get_user().user_type)
2067
- or not self.actor.allow_create
2068
- ):
2064
+ if self.create_kw is None:
2065
+ # print("20250127 self.create_kw is None")
2069
2066
  return
2070
2067
  if not self.actor.get_create_permission(self):
2071
2068
  return
2069
+ # raise Exception("20250127 create_phantom_rows")
2070
+ if self.actor.model is not None:
2071
+ if self.actor.model.disable_create(self) is not None:
2072
+ return
2072
2073
  yield PhantomRow(self, **kw)
2073
2074
 
2074
2075
  def create_instance(self, **kw):
2075
2076
  """
2076
- Create a row (a model instance if this is a database table) using
2077
- the specified keyword arguments.
2077
+ Create a database row using the specified keyword arguments.
2078
2078
  """
2079
2079
  if self.create_kw:
2080
2080
  kw.update(self.create_kw)
lino/core/site.py CHANGED
@@ -96,8 +96,7 @@ def to_locale(language):
96
96
  + language[p + 2 :].lower()
97
97
  )
98
98
  return language[:p].lower() + "_" + language[p + 1 :].upper()
99
- else:
100
- return language.lower()
99
+ return language.lower()
101
100
 
102
101
 
103
102
  def class2str(cl):
lino/core/store.py CHANGED
@@ -576,11 +576,11 @@ class DisabledFieldsStoreField(SpecialStoreField):
576
576
 
577
577
  """
578
578
 
579
- name = str("disabled_fields")
579
+ name = "disabled_fields"
580
580
 
581
581
  def __init__(self, store):
582
582
  # from lino.core.gfks import GenericForeignKey
583
- SpecialStoreField.__init__(self, store)
583
+ super().__init__(store)
584
584
  self.always_disabled = set()
585
585
  for f in self.store.all_fields:
586
586
  if f.field is not None:
lino/help_texts.py CHANGED
@@ -8,7 +8,7 @@ help_texts = {
8
8
  'lino.mixins.Contactable' : _("""Mixin for models that represent somebody who can be contacted by email."""),
9
9
  'lino.mixins.Contactable.get_as_user' : _("""Return the user object representing this contactable."""),
10
10
  'lino.mixins.Phonable' : _("""Mixin for models that represent somebody who can be contacted by phone."""),
11
- 'lino.mixins.Modified' : _("""Adds a a timestamp field which holds the last modification time of every individual database object."""),
11
+ 'lino.mixins.Modified' : _("""Adds a a timestamp field that holds the last modification time of every individual database object."""),
12
12
  'lino.mixins.Modified.modified' : _("""The time when this database object was last modified."""),
13
13
  'lino.mixins.Modified.auto_touch' : _("""Whether to touch objects automatically when saving them."""),
14
14
  'lino.mixins.Created' : _("""Adds a timestamp field which holds the creation time of every individual database object."""),
@@ -174,6 +174,7 @@ help_texts = {
174
174
  'lino.modlib.uploads.Plugin' : _("""See /dev/plugins."""),
175
175
  'lino.modlib.uploads.Plugin.remove_orphaned_files' : _("""Whether checkdata –fix should automatically delete orphaned files in the uploads folder."""),
176
176
  'lino.modlib.uploads.Plugin.with_thumbnails' : _("""Whether to use PIL, the Python Imaging Library."""),
177
+ 'lino.modlib.uploads.Plugin.with_volumes' : _("""Whether to use library files (volumes)."""),
177
178
  'lino.modlib.weasyprint.Plugin' : _("""See /dev/plugins."""),
178
179
  'lino.modlib.weasyprint.Plugin.header_height' : _("""Height of header in mm. Set to None if you want no header."""),
179
180
  'lino.modlib.weasyprint.Plugin.footer_height' : _("""Height of footer in mm. Set to None if you want no header."""),
@@ -314,6 +315,7 @@ help_texts = {
314
315
  'lino.utils.ucsv.UTF8Recoder' : _("""Iterator that reads an encoded stream and reencodes the input to UTF-8"""),
315
316
  'lino.utils.ucsv.UnicodeReader' : _("""A CSV reader which will iterate over lines in the CSV file “f”, which is encoded in the given encoding."""),
316
317
  'lino.utils.ucsv.UnicodeWriter' : _("""A CSV writer which will write rows to CSV file “f”, which is encoded in the given encoding."""),
318
+ 'lino.core.model.Model.disable_create' : _("""Return a veto message if you want to refuse creating rows on this model in the given action request even when permission has been given."""),
317
319
  'lino.core.model.Model.on_create' : _("""Override this to set default values that depend on the request."""),
318
320
  'lino.core.model.Model.after_ui_create' : _("""Hook to define custom behaviour to run when a user has created a new instance of this model."""),
319
321
  'lino.core.model.Model.submit_insert' : _("""The SubmitInsert action to be executed when the when the users submits an insert window."""),
@@ -353,6 +355,7 @@ help_texts = {
353
355
  'lino.modlib.checkdata.Checkers' : _("""The list of data checkers known by this application."""),
354
356
  'lino.modlib.checkdata.Checker' : _("""Base class for all data checkers."""),
355
357
  'lino.modlib.checkdata.Checker.model' : _("""The model to be checked. If this is a string, Lino will resolve it at startup."""),
358
+ 'lino.modlib.checkdata.Checker.no_auto' : _("""Whether this checker should be ignored by checkdata."""),
356
359
  'lino.modlib.checkdata.Checker.check_instance' : _("""Run get_checkdata_problems() on this checker for the given database object."""),
357
360
  'lino.modlib.checkdata.Checker.get_checkable_models' : _("""Return a list of the models to check."""),
358
361
  'lino.modlib.checkdata.Checker.activate' : _("""Creates an instance of this class and adds it as a choice to the Checkers choicelist."""),