django-restit 4.2.128__py3-none-any.whl → 4.2.131__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
account/models/member.py CHANGED
@@ -10,6 +10,7 @@ import time
10
10
  import re
11
11
  import hashlib
12
12
  import uuid
13
+ from objict import objict
13
14
 
14
15
  from auditlog.models import PersistentLog
15
16
  from sessionlog.models import SessionLog
@@ -387,6 +388,12 @@ class Member(User, RestModel, MetaDataModel):
387
388
  return False
388
389
  return self.getProperty(perm, 0, "permissions", bool)
389
390
 
391
+ def getPermissions(self):
392
+ return objict.fromdict(self.getProperties("permissions"))
393
+
394
+ def listPermissions(self):
395
+ return [k for k, v in self.getProperties("permissions").items() if v in [1, "1"]]
396
+
390
397
  def hasGroupPerm(self, group, perm):
391
398
  if group is None:
392
399
  return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-restit
3
- Version: 4.2.128
3
+ Version: 4.2.131
4
4
  Summary: A Rest Framework for DJANGO
5
5
  License: MIT
6
6
  Author: Ian Starnes
@@ -30,7 +30,7 @@ account/models/device.py,sha256=0AFeLMGk4im4KZVd3eGSyRbK4eQEXiFM2cdY8GUzihs,5934
30
30
  account/models/feeds.py,sha256=vI7fG4ASY1M0Zjke24RdnfDcuWeATl_yR_25jPmT64g,2011
31
31
  account/models/group.py,sha256=KnLj0cOJBg1JYo33wL-7NehD4_PCsLyEjLdX6GZJZAo,22308
32
32
  account/models/legacy.py,sha256=zYdtv4LC0ooxPVqWM-uToPwV-lYWQLorSE6p6yn1xDw,2720
33
- account/models/member.py,sha256=JziyYNC13SFWX07Thtoe2V_YlrKNn2RHJZcRk15Zh-s,54205
33
+ account/models/member.py,sha256=7JCf7HULo_CLqKLGbHFvFOVYSkKbVkDuUPjq0C1u2Ak,54450
34
34
  account/models/membership.py,sha256=90EpAhOsGaqphDAkONP6j_qQ0OWSRaQsI8H7E7fgMkE,9249
35
35
  account/models/notify.py,sha256=TOkuVBLAsbzT58FOxII_G3Cj_IDQx16vyehyEsNrDcY,15306
36
36
  account/models/passkeys.py,sha256=TJxITUi4DT4_1tW2K7ZlOcRjJuMVl2NtKz7pKQU8-Tw,1516
@@ -173,7 +173,7 @@ medialib/fixtures/medialib_test_fixture.json,sha256=7M7zvGI2S5G3ENV8OQ3Ks4149lEi
173
173
  medialib/forms.py,sha256=nrE6QTPNPiIeX7Nx4l9DEmAQeQXqFyCg1C3JEDBYJfE,5442
174
174
  medialib/migrations/0001_initial.py,sha256=H3JliH5aw7tiHef8MhrJr_9rGetqgA7UjTF-eKziRSM,20518
175
175
  medialib/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
176
- medialib/models.py,sha256=CrTS5znW8JMuj8gaAnJ5boYvN-deF8zyOuCmW3cFQVE,52228
176
+ medialib/models.py,sha256=XVqqyMeikK5qPGQ_kMsM9H9VcTPko9s3JmTGGfIScvs,53647
177
177
  medialib/ocr.py,sha256=zlP7-NBiXhW7jR9pljmEPl5xzLVZpLN5QLAELQgU0Fk,1189
178
178
  medialib/pdf.py,sha256=l28WwM0JKbT9boV-b_9TFh9jhvGcrquR8GqC8wfEaLk,1275
179
179
  medialib/qrcode.py,sha256=vHyA5egXOX70EFiUDgr1njI9zcF6bXQJ_hKAQrppRow,545
@@ -209,7 +209,7 @@ medialib/render/render_utils.py,sha256=ld0hMfiAWSqC8Nhi408ZPSjKHWYuQXtPdjbeBfav4
209
209
  medialib/render/schedule.py,sha256=iDSekfsY31SEXF-MR-YdJ9-a3UJ3SoJsZJpiEcbiNZA,370
210
210
  medialib/rpc/__init__.py,sha256=K84yMB6sUr7zTeECxca-_2jyHiUGCHTM-XYmJK6jQCo,82
211
211
  medialib/rpc/legacy.py,sha256=KTNmNJIxfFkZfAj0LXx9-e8bJonOfCoNE0-c_cugsNc,37925
212
- medialib/rpc/media.py,sha256=nZf5OvTZ6vMoJATqOiGJjWBecXkxoXQK9VU521xnVT0,617
212
+ medialib/rpc/media.py,sha256=118Ra_MnCtPZ-oSouA8x3inMb590JH9mwdF--cCqKdM,876
213
213
  medialib/rpc/tools.py,sha256=PQYP0r0NMLU_QdrnKxrMIPNbefzguwe-U8QBm_jhung,956
214
214
  medialib/scripts/init_config,sha256=jfXtOwYcSRFedKeSC8qcZo-ZpyvN8OvYZnQP0uXdURo,7875
215
215
  medialib/static/css/base_medialibui.css,sha256=yxhNcfzSfrx7vkHjBf_KWq-txLdbnVxjCG_dtb7pTqI,4499
@@ -273,7 +273,7 @@ medialib/stores/oauth2client/tools.py,sha256=OLSW5Iu7rtjGankFpjXlHBw8aAFOcIsWdDh
273
273
  medialib/stores/oauth2client/util.py,sha256=1Uc6qwqNhI3b507VtFnklX4sEZNt0MZbLAME2o6kCgg,5706
274
274
  medialib/stores/oauth2client/xsrfutil.py,sha256=4Plq0y5xEkDwvrveVA32gBRP0A3FkJ_36dIsyGHeJeA,3367
275
275
  medialib/stores/rtmpstore.py,sha256=CFRP6Ss4hbLKngbdaLvA8_oKCSe18B29-oWVgvTXMPA,500
276
- medialib/stores/s3.py,sha256=gAFiVHQkbYHZQ_2LU5nl2EXyoSs1mW4VdVCjsImisuE,5836
276
+ medialib/stores/s3.py,sha256=behvLC-ftoMVddJmgK5jfZsC5PVjRBPrSgXkwngNP9A,6280
277
277
  medialib/stores/s3store.py,sha256=uKO6I-X83sD9xefCQytLyrV-7t9TSX2p476n_ZBkSHw,4583
278
278
  medialib/stores/uritemplate/__init__.py,sha256=ONWR_KRz9au0O-XUUTrO_UN7GHTmZCTKyvflUQb8wxM,4996
279
279
  medialib/stores/youtubestore.py,sha256=swXOJYfbhgzAv1NjOehjKtfYRZYk2wiPK7a7_boAulc,406
@@ -299,7 +299,7 @@ medialib/templates/medialib/testpicker.html,sha256=I7KnrAu9e4kQhuEEN-51HGzcuJ06E
299
299
  medialib/tests.py,sha256=z1THDMo-R9HWMvCCOgX6aH6-2W2uWqrtbJ_k-zfpgyc,6045
300
300
  medialib/tq.py,sha256=ZDRBZ9qEsqNAsk48cciLyX7DIziqD2uPEawtQny1Ja8,544
301
301
  medialib/urls.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
302
- medialib/utils.py,sha256=Hw2lZ5vZg_JFjgbt2nhqTYdXwbERCvgnefl8rEdCUKQ,3756
302
+ medialib/utils.py,sha256=StQY7k9dOCksdTsYqUb3aGpwwrcYg07z7vskBQqCPnw,3774
303
303
  medialib/views.py,sha256=h_Fm3FOX04VV2G-J141wEHxT36d46HVdzPJg_u8r2vI,4321
304
304
  medialib/youtube/__init__.py,sha256=fmu9XPaDpadkb2OuroQsKiWpUPo7c5y8o83HmLLMIHA,4303
305
305
  medialib/youtube/apiclient/__init__.py,sha256=iGdQFKwzm0VxArl8X183-_ZBhIsETZlbXxhBAUz3MfI,601
@@ -376,14 +376,14 @@ pushit/utils.py,sha256=IeTCGa-164nmB1jIsK1lu1O1QzUhS3BKfuXHGjCW-ck,2121
376
376
  rest/.gitignore,sha256=TbEvWRMnAiajCTOdhiNrd9eeCAaIjRp9PRjE_VkMM5g,118
377
377
  rest/README.md,sha256=V3ETc-cJu8PZIbKr9xSe_pA4JEUpC8Dhw4bQeVCDJPw,5460
378
378
  rest/RemoteEvents.py,sha256=nL46U7AuxIrlw2JunphR1tsXyqi-ep_gD9CYGpYbNgE,72
379
- rest/__init__.py,sha256=GztWl-MikBJXtn-Tqs0kfE5M-LEleKPzMBuQycrIO4w,122
379
+ rest/__init__.py,sha256=NmUDpJwDRPihaYYG56xnn8xE0-EGE9ysndPTc4P9bDQ,122
380
380
  rest/arc4.py,sha256=y644IbF1ec--e4cUJ3KEYsewTCITK0gmlwa5mJruFC0,1967
381
381
  rest/cache.py,sha256=1Qg0rkaCJCaVP0-l5hZg2CIblTdeBSlj_0fP6vlKUpU,83
382
382
  rest/crypto/__init__.py,sha256=Tl0U11rgj1eBYqd6OXJ2_XSdNLumW_JkBZnaJqI6Ldw,72
383
383
  rest/crypto/aes.py,sha256=NOVRBRSHCV-om68YpGySWWG-4kako3iEVjq8hxZWPUU,4372
384
384
  rest/crypto/privpub.py,sha256=_FioylVcbMmDP80yPYjURmafEiDmEAMkskbc7WF10ac,4082
385
385
  rest/crypto/util.py,sha256=agFN2OCPHC70tHNGWrMkkZX4Tt_Ty6imoKEMdTkZpKA,4514
386
- rest/datem.py,sha256=JHMvWG8A-n4g915wrZiCtfuhgcLMgNYMXuzXIEtgaPg,12335
386
+ rest/datem.py,sha256=hX6bTbl5mQSg0x2hDK5P1TynkZFUfVTDwYkTuFObgbw,12626
387
387
  rest/decorators.py,sha256=AuB4agpog587CUsF8HkAZiHDfs_pueb2rdxXZD7dUUE,15327
388
388
  rest/encryption.py,sha256=x6Kiez0tVqfxK26MSsRL3k8OS05ni1gEX2aj3I0S9V0,788
389
389
  rest/errors.py,sha256=uKwG9OkLme36etabqK54DMjMQc1fgEoUIAUxXa7WFQw,612
@@ -481,10 +481,11 @@ telephony/rpc.py,sha256=PXPDFvgoXkCKlfMzIbt6lYZPay3fcveNj2X4Pjby7p4,3473
481
481
  wiki/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
482
482
  wiki/migrations/0001_initial.py,sha256=9jvUyjrbJrbDilRnwzQUPhPV8Xi_olEPBk_N0nycvM0,3606
483
483
  wiki/migrations/0002_alter_pagemedia_entry.py,sha256=9CUnfvBmj0D4akCkux7HFuXgw9B9avE8V-iMCm5cjds,485
484
+ wiki/migrations/0003_page_perms.py,sha256=qJBLI7t5mgiDTKCR9qhticmbhgo2KKYq7WbHaxH1Ykw,423
484
485
  wiki/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
485
486
  wiki/models/__init__.py,sha256=jE-9r_Hqpyo7ysKu9BschXOn5Zg34wUt894GwJpxA28,132
486
487
  wiki/models/faq.py,sha256=nvcEFerllQKT61kIYlasvZzRKwpXyfmQpiqkpHP1V1o,1745
487
- wiki/models/page.py,sha256=7_HAbvHtWIQpttiMoxrm8-Mfik6kUW6F47xkXZN2Omk,7311
488
+ wiki/models/page.py,sha256=06wyZsDX57B-d_wZhSNcrNAOv-ERHlKveJ15MZc9haQ,8751
488
489
  wiki/models/revision.py,sha256=St5-vz8SGvogsDL6jTWqHLE23PS5mp9iA0DUt3hWTsU,729
489
490
  wiki/periodic.py,sha256=t-UgXJIug-OLslJM_r03-5WrNKj39TxrvfuNFjVAhDs,334
490
491
  wiki/renderers/__init__.py,sha256=lLEoJvjU3ezXwBGcjleKk_kMyNeMD9MpfBlEiKayEiM,461
@@ -511,7 +512,7 @@ ws4redis/servers/uwsgi.py,sha256=VyhoCI1DnVFqBiJYHoxqn5Idlf6uJPHvfBKgkjs34mo,172
511
512
  ws4redis/settings.py,sha256=K0yBiLUuY81iDM4Yr-k8hbvjn5VVHu5zQhmMK8Dtz0s,1536
512
513
  ws4redis/utf8validator.py,sha256=S0OlfjeGRP75aO6CzZsF4oTjRQAgR17OWE9rgZdMBZA,5122
513
514
  ws4redis/websocket.py,sha256=R0TUyPsoVRD7Y_oU7w2I6NL4fPwiz5Vl94-fUkZgLHA,14848
514
- django_restit-4.2.128.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
515
- django_restit-4.2.128.dist-info/METADATA,sha256=4024f0T-AL9SvYKoCvIOuU4pPOKzCudxdtGTZWIKS2M,7663
516
- django_restit-4.2.128.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
517
- django_restit-4.2.128.dist-info/RECORD,,
515
+ django_restit-4.2.131.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
516
+ django_restit-4.2.131.dist-info/METADATA,sha256=lsS45ob1xhvxwvB4KRvDhmmLZSXP04I4797ajfnV5uc,7663
517
+ django_restit-4.2.131.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
518
+ django_restit-4.2.131.dist-info/RECORD,,
medialib/models.py CHANGED
@@ -330,6 +330,7 @@ class MediaItem(models.Model, RestModel, MetaDataModel):
330
330
  Media Item (a video or image)
331
331
  """
332
332
  class RestMeta:
333
+ POST_SAVE_FIELDS = ["rendernow"]
333
334
  SEARCH_FIELDS = ["name", "description"]
334
335
  VIEW_PERMS = ["view_media", "manage_users", "manage_media"]
335
336
  SAVE_PERMS = ["manage_media", "manage_users"]
@@ -451,6 +452,12 @@ class MediaItem(models.Model, RestModel, MetaDataModel):
451
452
  return self.library.default_store()
452
453
  return settings.MEDIALIB_DEFAULT_STORE
453
454
 
455
+ def s3_store(self):
456
+ return f's3://{settings.AWS_S3_BUCKET}'
457
+
458
+ def set_rendernow(self, value):
459
+ self.new_render()
460
+
454
461
  def uses(self):
455
462
  """
456
463
  List uses available for this item
@@ -551,6 +558,11 @@ class MediaItem(models.Model, RestModel, MetaDataModel):
551
558
  return super(MediaItem, self).save()
552
559
 
553
560
  def save(self, *args, **kwargs):
561
+ if self.pk is None:
562
+ return self.saveNew(*args, **kwargs)
563
+ return super(MediaItem, self).save(*args, **kwargs)
564
+
565
+ def saveNew(self, *args, **kwargs):
554
566
  """
555
567
  When saving MediaItem set newfile attribute to upload new file and start rendering
556
568
  """
@@ -1023,6 +1035,25 @@ class MediaItem(models.Model, RestModel, MetaDataModel):
1023
1035
  media.save()
1024
1036
  return media
1025
1037
 
1038
+ @staticmethod
1039
+ def on_upload_s3(request):
1040
+ filename = request.DATA.get("filename")
1041
+ filesize = request.DATA.get("filesize")
1042
+ filetype = request.DATA.get("filetype")
1043
+ kind = utils.guessMediaKindByName(filename)
1044
+ if kind is None:
1045
+ kind = "*"
1046
+ obj = MediaItem(name=filename, owner=request.member, kind=kind, group=request.group)
1047
+ obj.save()
1048
+ rendition = MediaItemRendition(
1049
+ mediaitem=obj, name="Original", bytes=filesize,
1050
+ use='original', kind=kind, is_original=True)
1051
+ rendition.save()
1052
+ rendition.url = rendition.generateURL(filename, store=obj.s3_store())
1053
+ rendition.save()
1054
+ return dict(url=rendition.generateUploadURL(filetype), id=obj.pk)
1055
+
1056
+
1026
1057
  class MediaItemMetaData(MetaDataBase):
1027
1058
  parent = models.ForeignKey(MediaItem, related_name="properties", on_delete=models.CASCADE)
1028
1059
 
@@ -1073,6 +1104,7 @@ class CuePointMeta(models.Model):
1073
1104
  def __str__(self):
1074
1105
  return self.key
1075
1106
 
1107
+
1076
1108
  class MediaItemRendition(models.Model):
1077
1109
  """
1078
1110
  A rendition of a media item
@@ -1119,20 +1151,18 @@ class MediaItemRendition(models.Model):
1119
1151
  except IndexError:
1120
1152
  return ''
1121
1153
 
1122
- def upload(self, fp, prefix=""):
1123
- """
1124
- Upload rendition to non-volatile storage
1125
- """
1126
- doclose = False
1127
- if type(fp) == str:
1128
- doclose = True
1129
- fp = open(fp)
1154
+ def generateUploadURL(self, filetype):
1155
+ from medialib.stores import s3
1156
+ return s3.generate_upload_url(self.url, filetype)
1130
1157
 
1158
+ def generateURL(self, name, prefix="", store=None):
1159
+ if store is None:
1160
+ store = self.mediaitem.default_store()
1131
1161
  paths = []
1132
- paths.append(self.mediaitem.default_store())
1162
+ paths.append(store)
1133
1163
  paths.append("/{}/".format(int_to_base36(self.mediaitem.pk)))
1134
1164
  path = "".join(paths)
1135
- if self.rendition_definition != None:
1165
+ if self.rendition_definition is not None:
1136
1166
  paths.append(int_to_base36(self.rendition_definition.pk))
1137
1167
  else:
1138
1168
  paths.append("0")
@@ -1140,13 +1170,24 @@ class MediaItemRendition(models.Model):
1140
1170
  paths.append(utils.toMD5(path, int(time.time())))
1141
1171
  paths.append(".")
1142
1172
  ext = ".dat"
1143
- if "." in fp.name:
1144
- ext = fp.name.split('.')[-1]
1173
+ if "." in name:
1174
+ ext = name.split('.')[-1]
1145
1175
  elif hasattr(self.mediaitem, "ext"):
1146
1176
  ext = self.mediaitem.ext
1147
1177
  paths.append(ext)
1148
1178
  # print(paths)
1149
- self.url = "".join(paths)
1179
+ return "".join(paths)
1180
+
1181
+ def upload(self, fp, prefix=""):
1182
+ """
1183
+ Upload rendition to non-volatile storage
1184
+ """
1185
+ doclose = False
1186
+ if type(fp) == str:
1187
+ doclose = True
1188
+ fp = open(fp)
1189
+
1190
+ self.url = self.generateURL(fp.name, prefix)
1150
1191
  stores.upload(self.url, fp, background=True)
1151
1192
  try:
1152
1193
  self.bytes = fp.size
medialib/rpc/media.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from rest import decorators as rd
2
+ from rest import views as rv
2
3
  from medialib import models as medialib
3
4
 
4
5
 
@@ -9,6 +10,13 @@ def rest_on_media_item(request, pk=None):
9
10
  return medialib.MediaItem.on_rest_request(request, pk)
10
11
 
11
12
 
13
+ @rd.urlPOST('media/item/s3')
14
+ @rd.login_required
15
+ @rd.requires_params(["filename", "filesize"])
16
+ def rest_on_media_upload_s3(request, pk=None):
17
+ return rv.restReturn(request, dict(data=medialib.MediaItem.on_upload_s3(request)))
18
+
19
+
12
20
  @rd.url(r'media/ref/$')
13
21
  @rd.url(r'media/ref/(?P<pk>\d+)$')
14
22
  @rd.login_required
medialib/stores/s3.py CHANGED
@@ -170,6 +170,20 @@ def get_file(url, fp=None):
170
170
  return obj.download(fp)
171
171
 
172
172
 
173
+ def generate_upload_url(url, filetype, expires=3600, acl="public-read"):
174
+ u = urlparse(url)
175
+ bucket_name = u.netloc
176
+ key = u.path.lstrip('/')
177
+ client = getS3(False)
178
+ params = dict(Bucket=bucket_name, Key=key, ContentType=filetype)
179
+ from rest import helpers
180
+ helpers.log_error("generate_upload_url", params)
181
+ return client.generate_presigned_url(
182
+ 'put_object',
183
+ ExpiresIn=expires,
184
+ Params=params)
185
+
186
+
173
187
  def delete(url):
174
188
  if url[-1] == "/":
175
189
  prefix = url.path.lstrip("/")
medialib/utils.py CHANGED
@@ -14,7 +14,8 @@ EXT_MAP = {
14
14
  KIND_MAP = {
15
15
  "video": "V",
16
16
  "image": "I",
17
- "http": "E"
17
+ "http": "E",
18
+ "other": "O"
18
19
  }
19
20
 
20
21
  def getFileExt(file):
rest/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  from .uberdict import UberDict # noqa: F401
2
2
  from .settings_helper import settings # noqa: F401
3
3
 
4
- __version__ = "4.2.128"
4
+ __version__ = "4.2.131"
rest/datem.py CHANGED
@@ -182,7 +182,9 @@ def getWeek(start, start_day=0):
182
182
  return week_start, week_end
183
183
 
184
184
 
185
- def getStartOfMonth(d):
185
+ def getStartOfMonth(d, clear_time=False):
186
+ if clear_time:
187
+ return d.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
186
188
  return d.replace(day=1)
187
189
 
188
190
 
@@ -190,13 +192,19 @@ def nextMonth(d):
190
192
  return getStartOfMonth(getStartOfMonth(d) + timedelta(days=32))
191
193
 
192
194
 
193
- def getEndOfMonth(start):
195
+ def getEndOfMonthNoTime(start):
194
196
  start = start.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
195
197
  end = start + timedelta(days=calendar.monthrange(start.year, start.month)[1])
196
198
  end = end.replace(hour=0, minute=0, second=0, microsecond=0)
197
199
  return end
198
200
 
199
201
 
202
+ def getEndOfMonth(d, clear_time=False):
203
+ if clear_time:
204
+ return getEndOfMonthNoTime(d)
205
+ return getStartOfMonth(nextMonth(d)) - timedelta(days=1)
206
+
207
+
200
208
  def parseDate(date_str, is_future=False, is_past=False, month_end=True, as_date=False):
201
209
  res = parseDateTime(date_str, is_future, is_past, month_end)
202
210
  if as_date and res:
@@ -284,10 +292,10 @@ def getDateRange(start, end=None, kind=None, zone=None, hour=0, eod=None, end_eo
284
292
  start, end = getWeek(start)
285
293
  elif kind == "month":
286
294
  start = start.replace(hour=0, day=1)
287
- end = getEndOfMonth(start)
295
+ end = getEndOfMonth(start, True)
288
296
  elif kind == "year":
289
297
  start = start.replace(hour=0, day=1, month=1)
290
- end = getEndOfMonth(start.replace(month=12))
298
+ end = getEndOfMonth(start.replace(month=12), True)
291
299
  elif isinstance(kind, int) or (isinstance(kind, str) and kind.isdigit()):
292
300
  end = start + timedelta(days=1)
293
301
  start = end - timedelta(days=int(kind))
@@ -0,0 +1,18 @@
1
+ # Generated by Django 4.2.11 on 2024-08-13 04:11
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('wiki', '0002_alter_pagemedia_entry'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='page',
15
+ name='perms',
16
+ field=models.CharField(db_index=True, default=None, max_length=255, null=True),
17
+ ),
18
+ ]
wiki/models/page.py CHANGED
@@ -1,11 +1,17 @@
1
1
  from django.db import models as dm
2
2
  from rest import models as rm
3
+ from rest import settings
4
+ from rest import helpers as rh
3
5
  from medialib import models as medialib
4
6
  import re
5
7
  import mistune
6
8
  from wiki.renderers import WikiRenderer
7
9
  from wiki.renderers.mistune import task_list
8
10
 
11
+ WIKI_PAGE_VIEW_PERMS = settings.get("WIKI_PAGE_VIEW_PERMS", ["view_wiki", "edit_wiki"])
12
+ WIKI_PAGE_EDIT_PERMS = settings.get("WIKI_PAGE_EDIT_PERMS", ["edit_wiki"])
13
+
14
+
9
15
  class Page(dm.Model, rm.RestModel, rm.MetaDataModel):
10
16
  """
11
17
  Blog (a collection of articles)
@@ -14,7 +20,8 @@ class Page(dm.Model, rm.RestModel, rm.MetaDataModel):
14
20
  SEARCH_FIELDS = ["title", "body"]
15
21
  SEARCH_TERMS = ["title", "body"]
16
22
  QUERY_FIELDS = ["all_fields", "parent__path"]
17
- VIEW_PERMS = ["view_wiki", "edit_wiki"]
23
+ VIEW_PERMS = WIKI_PAGE_VIEW_PERMS
24
+ EDIT_PERMS = WIKI_PAGE_EDIT_PERMS
18
25
  UNIQUE_LOOKUP = ["path"]
19
26
  DEFAULT_SORT = "-order"
20
27
  CAN_DELETE = True
@@ -30,6 +37,7 @@ class Page(dm.Model, rm.RestModel, rm.MetaDataModel):
30
37
  "order",
31
38
  "path",
32
39
  "slug",
40
+ "perms"
33
41
  ],
34
42
  },
35
43
  "default": {
@@ -103,6 +111,7 @@ class Page(dm.Model, rm.RestModel, rm.MetaDataModel):
103
111
  title = dm.CharField(max_length=255)
104
112
  path = dm.CharField(max_length=255, db_index=True)
105
113
  slug = dm.SlugField(db_index=True)
114
+ perms = dm.CharField(max_length=255, db_index=True, null=True, default=None)
106
115
 
107
116
  body = dm.TextField(blank=True)
108
117
 
@@ -144,6 +153,35 @@ class Page(dm.Model, rm.RestModel, rm.MetaDataModel):
144
153
  if qset.count():
145
154
  self.slug = f"{sanitized_slug}_{qset.count()+1}"
146
155
 
156
+ def on_rest_can_get(self, request):
157
+ if request is None:
158
+ return True
159
+ if self.perms:
160
+ perms = [p.strip() for p in self.perms.split(',')]
161
+ elif self.parent and self.parent.perms:
162
+ perms = [p.strip() for p in self.parent.perms.split(',')]
163
+ else:
164
+ perms = getattr(self.RestMeta, "VIEW_PERMS", None)
165
+ if perms:
166
+ if "public" in perms:
167
+ return True
168
+ if "owner" in perms and self.checkIsOwner(request.member):
169
+ return True
170
+ # we need to check if this user has permission
171
+ group_field = "group"
172
+ status, error, code = rh.requestHasPerms(request, perms, getattr(self, group_field, None))
173
+ if not status:
174
+ return False
175
+ return True
176
+
177
+ @classmethod
178
+ def on_rest_list_ready(cls, request, qset=None):
179
+ out = []
180
+ for page in qset:
181
+ if page.on_rest_can_get(request):
182
+ out.append(page)
183
+ return out
184
+
147
185
  def on_rest_pre_save(self, request):
148
186
  if not self.slug:
149
187
  self.set_slug(self.title)
@@ -206,7 +244,8 @@ class PageMetaData(rm.MetaDataBase):
206
244
 
207
245
  class PageMedia(dm.Model, rm.RestModel):
208
246
  class RestMeta:
209
- VIEW_PERMS = ["view_wiki", "edit_wiki"]
247
+ VIEW_PERMS = WIKI_PAGE_VIEW_PERMS
248
+ EDIT_PERMS = WIKI_PAGE_EDIT_PERMS
210
249
  GRAPHS = {
211
250
  "basic": {
212
251
  "graphs": {