accrete 0.0.58__py3-none-any.whl → 0.0.60__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.
- accrete/contrib/ui/filter.py +6 -1
- accrete/middleware.py +7 -5
- accrete/storage.py +37 -0
- accrete/urls.py +8 -0
- accrete/views.py +34 -19
- {accrete-0.0.58.dist-info → accrete-0.0.60.dist-info}/METADATA +1 -1
- {accrete-0.0.58.dist-info → accrete-0.0.60.dist-info}/RECORD +9 -7
- {accrete-0.0.58.dist-info → accrete-0.0.60.dist-info}/WHEEL +0 -0
- {accrete-0.0.58.dist-info → accrete-0.0.60.dist-info}/licenses/LICENSE +0 -0
accrete/contrib/ui/filter.py
CHANGED
@@ -158,7 +158,9 @@ class Filter:
|
|
158
158
|
'PositiveSmallIntegerField': self.int_param,
|
159
159
|
'DateTimeField': self.date_time_param,
|
160
160
|
'DateField': self.date_param,
|
161
|
-
'ForeignKey': self.foreign_key_param
|
161
|
+
'ForeignKey': self.foreign_key_param,
|
162
|
+
'FileField': self.file_param,
|
163
|
+
'ImageField': self.file_param
|
162
164
|
}
|
163
165
|
|
164
166
|
def parse_choices(self, choices):
|
@@ -269,6 +271,9 @@ class Filter:
|
|
269
271
|
return self.null_param(key, value)
|
270
272
|
return ''
|
271
273
|
|
274
|
+
def file_param(self, key, value):
|
275
|
+
return self.null_param(key, value)
|
276
|
+
|
272
277
|
def null_param(self, key, value):
|
273
278
|
options = self.parse_choices([
|
274
279
|
('true', _('True')),
|
accrete/middleware.py
CHANGED
@@ -82,10 +82,12 @@ class HtmxRedirectMiddleware(MiddlewareMixin):
|
|
82
82
|
@staticmethod
|
83
83
|
def process_response(request, response):
|
84
84
|
is_htmx = request.headers.get('HX-Request', 'false') == 'true'
|
85
|
-
|
86
|
-
response
|
87
|
-
)
|
88
|
-
|
89
|
-
|
85
|
+
if not is_htmx:
|
86
|
+
return response
|
87
|
+
is_redirect = isinstance(response, HttpResponseRedirect)
|
88
|
+
is_permanent_redirect = isinstance(response, HttpResponsePermanentRedirect)
|
89
|
+
if is_redirect or is_permanent_redirect:
|
90
90
|
response.status_code = 200
|
91
|
+
header = 'HX-Location' if is_redirect else 'HX-Redirect'
|
92
|
+
response[header] = response['Location']
|
91
93
|
return response
|
accrete/storage.py
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
import os
|
2
|
+
from urllib.parse import urljoin
|
3
|
+
from django.core.files.storage import FileSystemStorage
|
4
|
+
from django.utils.encoding import filepath_to_uri
|
5
|
+
from django.utils.deconstruct import deconstructible
|
6
|
+
from django.conf import settings
|
7
|
+
from accrete.tenant import get_tenant
|
8
|
+
|
9
|
+
|
10
|
+
@deconstructible(path="django.core.files.storage.FileSystemStorage")
|
11
|
+
class TenantFileSystemStorage(FileSystemStorage):
|
12
|
+
|
13
|
+
@property
|
14
|
+
def base_location(self):
|
15
|
+
tenant = get_tenant()
|
16
|
+
base_dir = f'{settings.MEDIA_ROOT}/{tenant.id}'
|
17
|
+
os.makedirs(os.path.dirname(base_dir), exist_ok=True)
|
18
|
+
return base_dir
|
19
|
+
|
20
|
+
@property
|
21
|
+
def location(self):
|
22
|
+
return os.path.abspath(self.base_location)
|
23
|
+
|
24
|
+
@property
|
25
|
+
def base_url(self):
|
26
|
+
if self._base_url is not None and not self._base_url.endswith("/"):
|
27
|
+
self._base_url += "/"
|
28
|
+
res = self._value_or_setting(self._base_url, f'{settings.MEDIA_URL.strip("/")}/{get_tenant().id}')
|
29
|
+
return res
|
30
|
+
|
31
|
+
def url(self, name):
|
32
|
+
if self.base_url is None:
|
33
|
+
raise ValueError("This file is not accessible via a URL.")
|
34
|
+
url = filepath_to_uri(name)
|
35
|
+
if url is not None:
|
36
|
+
url = url.lstrip("/")
|
37
|
+
return urljoin(f'/{self.base_url}/', url)
|
accrete/urls.py
ADDED
accrete/views.py
CHANGED
@@ -1,31 +1,15 @@
|
|
1
|
+
import os
|
1
2
|
from functools import wraps
|
3
|
+
from django.http import HttpResponse, HttpResponseNotFound
|
2
4
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
3
5
|
from django.contrib.auth.views import login_required
|
4
6
|
from django.core.exceptions import ImproperlyConfigured
|
5
7
|
from django.shortcuts import redirect
|
6
8
|
from django.conf import settings
|
9
|
+
from accrete.tenant import get_tenant
|
7
10
|
from . import config
|
8
11
|
|
9
12
|
|
10
|
-
def tenant_required(
|
11
|
-
redirect_field_name: str = None,
|
12
|
-
login_url: str = None
|
13
|
-
):
|
14
|
-
def decorator(f):
|
15
|
-
@wraps(f)
|
16
|
-
@login_required(
|
17
|
-
redirect_field_name=redirect_field_name,
|
18
|
-
login_url=login_url
|
19
|
-
)
|
20
|
-
def _wrapped_view(request, *args, **kwargs):
|
21
|
-
tenant = request.tenant
|
22
|
-
if not tenant:
|
23
|
-
return redirect(config.ACCRETE_TENANT_NOT_SET_URL)
|
24
|
-
return f(request, *args, **kwargs)
|
25
|
-
return _wrapped_view
|
26
|
-
return decorator
|
27
|
-
|
28
|
-
|
29
13
|
class TenantRequiredMixin(LoginRequiredMixin):
|
30
14
|
|
31
15
|
tenant_missing_url = None
|
@@ -80,3 +64,34 @@ class TenantRequiredMixin(LoginRequiredMixin):
|
|
80
64
|
f"{cls_name}.get_member_not_authorized_url()."
|
81
65
|
)
|
82
66
|
return url
|
67
|
+
|
68
|
+
|
69
|
+
def tenant_required(
|
70
|
+
redirect_field_name: str = None,
|
71
|
+
login_url: str = None
|
72
|
+
):
|
73
|
+
def decorator(f):
|
74
|
+
@wraps(f)
|
75
|
+
@login_required(
|
76
|
+
redirect_field_name=redirect_field_name,
|
77
|
+
login_url=login_url
|
78
|
+
)
|
79
|
+
def _wrapped_view(request, *args, **kwargs):
|
80
|
+
tenant = request.tenant
|
81
|
+
if not tenant:
|
82
|
+
return redirect(config.ACCRETE_TENANT_NOT_SET_URL)
|
83
|
+
return f(request, *args, **kwargs)
|
84
|
+
return _wrapped_view
|
85
|
+
return decorator
|
86
|
+
|
87
|
+
|
88
|
+
@tenant_required()
|
89
|
+
def get_tenant_file(request, tenant_id, filepath):
|
90
|
+
tenant = get_tenant()
|
91
|
+
if tenant.id != tenant_id:
|
92
|
+
return HttpResponseNotFound()
|
93
|
+
filepath = f'{settings.MEDIA_ROOT}/{tenant_id}/{filepath}'
|
94
|
+
if not os.path.exists(filepath):
|
95
|
+
return HttpResponseNotFound()
|
96
|
+
with open(filepath, 'rb') as f:
|
97
|
+
return HttpResponse(f)
|
@@ -5,11 +5,13 @@ accrete/apps.py,sha256=F7ynMLHJr_6bRujWtZVUzCliY2CGKiDvyUmL4F68L2E,146
|
|
5
5
|
accrete/config.py,sha256=eJUbvyBO3DvAD6xkVKjTAzlXy7V7EK9bVyb91girfUs,299
|
6
6
|
accrete/forms.py,sha256=2vUh80qNvPDD8Zl3agKBSJEQeY7bXVLOx_SAB34wf8E,1359
|
7
7
|
accrete/managers.py,sha256=CaIJLeBry4NYIXaVUrdUjp7zx4sEWgv-1-ssI1m-EOs,1156
|
8
|
-
accrete/middleware.py,sha256=
|
8
|
+
accrete/middleware.py,sha256=IABs2pAV-kXjp3n5_Wsc7reXGjU5FhmCbvFB8cC4ZRM,3422
|
9
9
|
accrete/models.py,sha256=grvRNXg0ZYAJU3KAIX-svuZXeXlfqP4qEJ00nlbV594,5145
|
10
|
+
accrete/storage.py,sha256=z7pHdQFw0hFGrrbfqIh7KFxabQ_JGqoPebmiX9TLmeU,1254
|
10
11
|
accrete/tenant.py,sha256=g3ZuTrQr2zqmIopNBRQeCmHEK2R3dlUme_hOV765J6U,1778
|
11
12
|
accrete/tests.py,sha256=Agltbzwwh5htvq_Qi9vqvxutzmg_GwgPS_N19xJZRlw,7197
|
12
|
-
accrete/
|
13
|
+
accrete/urls.py,sha256=goDFR-yhOlLLy7AMi9pmh2aBkxdtZtwXNg6mwI2zPhU,227
|
14
|
+
accrete/views.py,sha256=fFvga5kniVYCV5J7PIYKPNK4EWAXX3I1eEw22y0A1_E,3432
|
13
15
|
accrete/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
16
|
accrete/contrib/sequence/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
17
|
accrete/contrib/sequence/admin.py,sha256=mTjab5cVklRUIQcSrsUo-_JgtXEsSdcFj_gfWhlStS4,273
|
@@ -37,7 +39,7 @@ accrete/contrib/ui/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,6
|
|
37
39
|
accrete/contrib/ui/apps.py,sha256=E0ao2ox6PQ3ldfeR17FXJUUJuGiWjm2DPCxHbPXGzls,152
|
38
40
|
accrete/contrib/ui/context.py,sha256=tb4x_G4VsIa1LKIWl-CvLVtEd_DCbrClA-0Wmnne9bc,8363
|
39
41
|
accrete/contrib/ui/elements.py,sha256=0F5q0-XLdAWQjdYLMQt6RXqz4xPD_ECrhs0kcBfYkvo,1856
|
40
|
-
accrete/contrib/ui/filter.py,sha256=
|
42
|
+
accrete/contrib/ui/filter.py,sha256=UAIkUNKu2nVQLafwZlkQu4PDfOPyFM61e_mE7OWRhYs,12410
|
41
43
|
accrete/contrib/ui/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
|
42
44
|
accrete/contrib/ui/urls.py,sha256=TUBlz_CGs9InTZoxM78GSnucA73I8knoh_obt12RUHM,186
|
43
45
|
accrete/contrib/ui/views.py,sha256=WpBKMsxFFG8eG4IN7TW_TPE6i3OFF7gnLDTK7JMKti8,191
|
@@ -220,7 +222,7 @@ accrete/utils/dates.py,sha256=apM6kt6JhGrKgoT0jfav1W-8AUVTxNc9xt3fJQ2n0JI,1492
|
|
220
222
|
accrete/utils/forms.py,sha256=Lll-DvAhKZDw72XeuCtb4wxQEJNFp7lQWh_Z1GyH3Zk,3049
|
221
223
|
accrete/utils/http.py,sha256=mAtQRgADv7zu1_j7A-EKVyb-oqa5a21i4Gd0QfjzGV0,3540
|
222
224
|
accrete/utils/models.py,sha256=EEhv7-sQVtQD24PEb3XcDUAh3VVhVFoMMLyFrDjGEaI,706
|
223
|
-
accrete-0.0.
|
224
|
-
accrete-0.0.
|
225
|
-
accrete-0.0.
|
226
|
-
accrete-0.0.
|
225
|
+
accrete-0.0.60.dist-info/METADATA,sha256=EmTPYxeLQvtOr0nPLX-AcCzbzJViET1tufDFhgqPfak,4892
|
226
|
+
accrete-0.0.60.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
227
|
+
accrete-0.0.60.dist-info/licenses/LICENSE,sha256=_7laeMIHnsd3Y2vJEXDYXq_PEXxIcjgJsGt8UIKTRWc,1057
|
228
|
+
accrete-0.0.60.dist-info/RECORD,,
|
File without changes
|
File without changes
|