django-bom 1.259__py3-none-any.whl → 1.265__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.
- bom/auth_backends.py +24 -14
- bom/decorators.py +3 -3
- bom/models.py +3 -0
- bom/settings.py +1 -0
- bom/static/bom/css/style.css +75 -11
- bom/templates/bom/bom-form-modal.html +2 -2
- bom/templates/bom/bom-modal-add-users.html +3 -4
- bom/templates/bom/settings.html +542 -398
- {django_bom-1.259.dist-info → django_bom-1.265.dist-info}/METADATA +3 -3
- {django_bom-1.259.dist-info → django_bom-1.265.dist-info}/RECORD +13 -13
- {django_bom-1.259.dist-info → django_bom-1.265.dist-info}/WHEEL +1 -1
- {django_bom-1.259.dist-info → django_bom-1.265.dist-info}/licenses/LICENSE +0 -0
- {django_bom-1.259.dist-info → django_bom-1.265.dist-info}/top_level.txt +0 -0
bom/auth_backends.py
CHANGED
|
@@ -12,36 +12,46 @@ class OrganizationPermissionBackend:
|
|
|
12
12
|
|
|
13
13
|
- Uses Django's has_perm(user, perm, obj) to evaluate permissions tied to an Organization.
|
|
14
14
|
- Superusers are granted all permissions.
|
|
15
|
-
-
|
|
15
|
+
- Owners and Admins are granted all permissions within their organization.
|
|
16
|
+
- Viewers are granted view-only permissions within their organization.
|
|
16
17
|
"""
|
|
17
18
|
|
|
18
19
|
def authenticate(self, request, **credentials): # pragma: no cover - not used for auth
|
|
19
20
|
return None
|
|
20
21
|
|
|
21
22
|
def has_perm(self, user_obj, perm: str, obj: Optional[object] = None):
|
|
22
|
-
# Only handle our specific object-level permission. Let other backends process others.
|
|
23
23
|
if not user_obj or not user_obj.is_authenticated:
|
|
24
24
|
return False
|
|
25
25
|
|
|
26
26
|
if user_obj.is_superuser:
|
|
27
27
|
return True
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
# Only handle 'bom' app permissions.
|
|
30
|
+
if not perm.startswith('bom.'):
|
|
30
31
|
return None
|
|
31
32
|
|
|
32
|
-
if obj is None or not isinstance(obj, Organization):
|
|
33
|
-
return False
|
|
34
|
-
|
|
35
33
|
profile = user_obj.bom_profile()
|
|
36
|
-
if not profile or profile.
|
|
34
|
+
if not profile or not profile.organization:
|
|
37
35
|
return False
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
# If an object is provided, ensure it belongs to the user's organization.
|
|
38
|
+
if obj is not None:
|
|
39
|
+
obj_org = None
|
|
40
|
+
if isinstance(obj, Organization):
|
|
41
|
+
obj_org = obj
|
|
42
|
+
elif hasattr(obj, 'organization'):
|
|
43
|
+
obj_org = obj.organization
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
if obj_org and obj_org.id != profile.organization_id:
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
# Owners and Admins can do everything within their organization
|
|
49
|
+
if profile.can_manage_organization():
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
# Viewers can only view within their organization
|
|
53
|
+
if profile.role == constants.ROLE_TYPE_VIEWER:
|
|
54
|
+
if perm.startswith('bom.view_'):
|
|
55
|
+
return True
|
|
46
56
|
|
|
47
|
-
return
|
|
57
|
+
return False
|
bom/decorators.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from django.contrib import messages
|
|
2
|
-
from django.core.exceptions import PermissionDenied
|
|
3
2
|
from django.http import HttpResponseRedirect
|
|
4
3
|
from django.urls import reverse
|
|
5
4
|
|
|
@@ -22,9 +21,10 @@ def google_authenticated(function):
|
|
|
22
21
|
|
|
23
22
|
def organization_admin(function):
|
|
24
23
|
def wrap(request, *args, **kwargs):
|
|
25
|
-
|
|
24
|
+
organization = request.user.bom_profile().organization
|
|
25
|
+
if not request.user.has_perm('bom.manage_members', organization):
|
|
26
26
|
messages.error(request, "You don't have permission to perform this action.")
|
|
27
|
-
return HttpResponseRedirect(request.META.get('HTTP_REFERER')
|
|
27
|
+
return HttpResponseRedirect(request.META.get('HTTP_REFERER') or reverse('bom:home'))
|
|
28
28
|
return function(request, *args, **kwargs)
|
|
29
29
|
|
|
30
30
|
wrap.__doc__ = function.__doc__
|
bom/models.py
CHANGED
|
@@ -187,6 +187,9 @@ class AbstractUserMeta(models.Model):
|
|
|
187
187
|
def is_organization_owner(self) -> bool:
|
|
188
188
|
return self.organization.owner == self.user if self.organization else False
|
|
189
189
|
|
|
190
|
+
def can_manage_organization(self) -> bool:
|
|
191
|
+
return self.role == ROLE_TYPE_ADMIN or self.is_organization_owner()
|
|
192
|
+
|
|
190
193
|
class Meta:
|
|
191
194
|
abstract = True
|
|
192
195
|
|
bom/settings.py
CHANGED
bom/static/bom/css/style.css
CHANGED
|
@@ -8,38 +8,36 @@ main {
|
|
|
8
8
|
min-height: 100vh;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
p {
|
|
12
|
-
font-size: 1.
|
|
13
|
-
font-weight: 300;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
li {
|
|
17
|
-
font-size: 1.2em;
|
|
11
|
+
p, li {
|
|
12
|
+
font-size: 1.1rem; /* Slightly larger to compensate for thin weight */
|
|
18
13
|
font-weight: 300;
|
|
19
14
|
}
|
|
20
15
|
|
|
16
|
+
/* We use significant size differences to replace the lack of bold weights */
|
|
21
17
|
h1 {
|
|
22
|
-
font-size:
|
|
18
|
+
font-size: 2.5rem;
|
|
23
19
|
font-weight: 300;
|
|
20
|
+
line-height: 1.1;
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
h2 {
|
|
27
|
-
font-size:
|
|
24
|
+
font-size: 2.0rem;
|
|
28
25
|
font-weight: 300;
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
h3 {
|
|
32
|
-
font-size: 1.
|
|
29
|
+
font-size: 1.7rem;
|
|
33
30
|
font-weight: 300;
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
h4 {
|
|
37
|
-
font-size: 1.
|
|
34
|
+
font-size: 1.4rem;
|
|
38
35
|
font-weight: 300;
|
|
39
36
|
}
|
|
40
37
|
|
|
41
38
|
h5 {
|
|
42
39
|
font-size: 1.1rem;
|
|
40
|
+
font-weight: 400;
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
.tabs {
|
|
@@ -415,4 +413,70 @@ a.anchor {
|
|
|
415
413
|
.container-app .right-align .btn,
|
|
416
414
|
.container-app .right-align .btn-flat {
|
|
417
415
|
margin-left: 4px;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
/* Master-Detail Settings Layout */
|
|
420
|
+
.settings-grid {
|
|
421
|
+
margin-top: 20px;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
@media only screen and (min-width: 993px) {
|
|
425
|
+
.settings-grid {
|
|
426
|
+
display: flex;
|
|
427
|
+
flex-wrap: nowrap;
|
|
428
|
+
align-items: stretch;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.settings-nav {
|
|
433
|
+
padding: 0 !important;
|
|
434
|
+
border-right: 1px solid #e0e0e0;
|
|
435
|
+
min-height: 70vh;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.settings-nav .collection {
|
|
439
|
+
border: none;
|
|
440
|
+
margin: 0;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.settings-nav .collection .collection-item {
|
|
444
|
+
border: none;
|
|
445
|
+
padding: 12px 20px;
|
|
446
|
+
display: flex;
|
|
447
|
+
align-items: center;
|
|
448
|
+
color: #5f6368;
|
|
449
|
+
cursor: pointer;
|
|
450
|
+
transition: background-color 0.2s;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.settings-nav .collection .collection-item:hover {
|
|
454
|
+
background-color: #f1f3f4;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.settings-nav .collection .collection-item.active {
|
|
458
|
+
background-color: rgba(165, 214, 167, 0.5);
|
|
459
|
+
color: #000000;
|
|
460
|
+
font-weight: 500;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.settings-nav .collection .collection-item .material-icons {
|
|
464
|
+
margin-right: 16px;
|
|
465
|
+
font-size: 20px;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.settings-content {
|
|
469
|
+
padding: 0 40px !important;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
@media only screen and (max-width: 992px) {
|
|
473
|
+
.settings-nav {
|
|
474
|
+
border-right: none;
|
|
475
|
+
border-bottom: 1px solid #e0e0e0;
|
|
476
|
+
min-height: auto;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.settings-content {
|
|
480
|
+
padding: 0 20px !important;
|
|
481
|
+
}
|
|
418
482
|
}
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
19
19
|
<div class="modal-footer">
|
|
20
|
-
<
|
|
20
|
+
<button type="button" class="modal-close waves-effect waves-green btn-flat">Cancel</button>
|
|
21
21
|
{% if form %}
|
|
22
22
|
<button class="waves-effect waves-light btn btn-primary" type="submit" name="{{ name }}">Submit</button>
|
|
23
23
|
{% else %}
|
|
24
|
-
<
|
|
24
|
+
<button type="button" class="modal-close waves-effect green lighten-1 waves-green btn-flat">Ok</button>
|
|
25
25
|
{% endif %}
|
|
26
26
|
</div>
|
|
27
27
|
</form>
|
|
@@ -28,13 +28,12 @@
|
|
|
28
28
|
</div>
|
|
29
29
|
</div>
|
|
30
30
|
<div class="modal-footer">
|
|
31
|
-
<
|
|
31
|
+
<button type="button" class="modal-close waves-effect waves-green btn-flat">Cancel</button>
|
|
32
|
+
|
|
32
33
|
{% if is_pro and user_can_manage_members %}
|
|
33
34
|
<button class="waves-effect waves-light btn btn-primary" type="submit" name="{{ name }}">Submit</button>
|
|
34
|
-
{% elif is_pro and not user_can_manage_members %}
|
|
35
|
-
<a href="#!" class="modal-close waves-effect green lighten-1 waves-green btn-flat">Ok</a>
|
|
36
35
|
{% else %}
|
|
37
|
-
<
|
|
36
|
+
<button type="button" class="modal-close waves-effect green lighten-1 waves-green btn-flat">Ok</button>
|
|
38
37
|
{% endif %}
|
|
39
38
|
</div>
|
|
40
39
|
</form>
|