Skip to content

Commit 8290cff

Browse files
authored
Merge pull request #17 from renderbox/develop
Release version 0.2.6
2 parents 8856939 + a5f237a commit 8290cff

File tree

18 files changed

+789
-49
lines changed

18 files changed

+789
-49
lines changed

.github/workflows/permafrost_ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ jobs:
3131
python manage.py makemigrations --check --dry-run
3232
env:
3333
DJANGO_DEBUG: 1
34-
3534
- name: Install Build Dependencies
3635
run: |
3736
python -m pip install -e .[build]

.readthedocs.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Required
2+
version: 2
3+
4+
python:
5+
version: 3.7
6+
install:
7+
- method: pip
8+
path: .
9+
extra_requirements:
10+
- docs

MANIFEST.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
include LICENSE.txt
2+
include README.md
3+
recursive-include permafrost/templates *

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
![Permafrost CI](https://github.com/renderbox/django-permafrost/workflows/Permafrost%20CI/badge.svg)
44

5+
[![Documentation Status](https://readthedocs.org/projects/django-permafrost/badge/?version=latest)](https://django-permafrost.readthedocs.io/en/latest/?badge=latest)
6+
57
# Django Permafrost
68

79
Django Permafrost is an extension to Django's Permissions framework. It's goal is to allow developers to expose some permissions to Client Users on the site so they can create and manage custom User Roles.

develop/develop/settings.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@
155155
'optional': [
156156
{'label': _('Can add Role'), 'permission': ('add_permafrostrole', 'permafrost', 'permafrostrole') },
157157
{'label': _('Can change Role'), 'permission': ('change_permafrostrole', 'permafrost', 'permafrostrole') },
158-
{'label': _('Can view Role'), 'permission': ('view_permafrostrole', 'permafrost', 'permafrostrole') },
159158
],
160159
'required': [
161160
{'label': _('Can view Role'), 'permission': ('view_permafrostrole', 'permafrost', 'permafrostrole') },

docs/conf.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import sys
1515
import django
1616

17+
from permafrost.__version__ import VERSION
18+
1719
sys.path.insert(0, os.path.abspath('../develop'))
1820
os.environ['DJANGO_SETTINGS_MODULE'] = 'develop.settings'
1921
django.setup()
@@ -23,21 +25,31 @@
2325

2426
project = 'Permafrost'
2527
copyright = '2020, Grant Viklund'
26-
author = 'Grant Viklund'
28+
author = 'Grant Viklund, DeVon Jackson'
2729

2830
# The full version, including alpha/beta/rc tags
29-
release = version = django.conf.settings.BUILD_VERSION
31+
release = version = VERSION
3032

3133
# -- General configuration ---------------------------------------------------
3234

3335
# Add any Sphinx extension module names here, as strings. They can be
3436
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
3537
# ones.
3638
extensions = [
37-
'm2r',
39+
'recommonmark',
40+
'sphinx_rtd_theme',
41+
'sphinx.ext.todo',
42+
'sphinx.ext.viewcode',
43+
'sphinx.ext.autodoc',
44+
'sphinx.ext.coverage',
45+
'sphinx.ext.napoleon',
3846
]
3947

40-
source_suffix = ['.rst', '.md']
48+
source_suffix = {
49+
'.rst': 'restructuredtext',
50+
'.txt': 'markdown',
51+
'.md': 'markdown',
52+
}
4153

4254
# Add any paths that contain templates here, relative to this directory.
4355
templates_path = ['_templates']

permafrost/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = "0.2.5"
1+
VERSION = "0.2.6"

permafrost/fixtures/unit_test.json

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,9 @@
514514
"pk": 11,
515515
"fields": {
516516
"name": "1_user_student",
517-
"permissions": []
517+
"permissions": [
518+
16
519+
]
518520
}
519521
},
520522
{
@@ -527,6 +529,18 @@
527529
]
528530
}
529531
},
532+
{
533+
"model": "auth.group",
534+
"pk": 13,
535+
"fields": {
536+
"name": "2_administration_administrator",
537+
"permissions": [
538+
37,
539+
38,
540+
40
541+
]
542+
}
543+
},
530544
{
531545
"model": "auth.user",
532546
"pk": 1,
@@ -551,6 +565,7 @@
551565
"fields": {
552566
"name": "Student",
553567
"slug": "student",
568+
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
554569
"category": "user",
555570
"site": 1,
556571
"locked": false,
@@ -564,11 +579,26 @@
564579
"fields": {
565580
"name": "Councilor",
566581
"slug": "councilor",
582+
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
567583
"category": "staff",
568584
"site": 1,
569585
"locked": false,
570586
"deleted": false,
571587
"group": 12
572588
}
589+
},
590+
{
591+
"model": "permafrost.permafrostrole",
592+
"pk": 3,
593+
"fields": {
594+
"name": "Administrator",
595+
"slug": "administrator",
596+
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
597+
"category": "administration",
598+
"site": 2,
599+
"locked": false,
600+
"deleted": false,
601+
"group": 12
602+
}
573603
}
574604
]

permafrost/forms.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,134 @@
11
# Permafrost Forms
2+
from django.contrib.auth.models import Permission
3+
from django.forms import ModelForm, MultipleChoiceField, CheckboxSelectMultiple
4+
from django.forms.fields import CharField, ChoiceField, BooleanField
5+
from django.forms.widgets import CheckboxInput, Textarea
6+
from django.utils.translation import ugettext_lazy as _
7+
from .models import PermafrostRole, get_optional_by_category, get_required_by_category, get_choices
8+
9+
CHOICES = [('', _("Choose Role Type"))] + get_choices()
10+
11+
def assemble_optiongroups_for_widget(permissions):
12+
choices = []
13+
optgroups = {}
14+
if permissions:
15+
for perm in permissions:
16+
if perm.content_type.name in optgroups:
17+
optgroups[perm.content_type.name].append((perm.pk, perm.name,))
18+
else:
19+
optgroups[perm.content_type.name] = [(perm.pk, perm.name,)]
20+
21+
for model_name, options in optgroups.items():
22+
choices.append([model_name, options])
23+
24+
return choices
25+
26+
def bootstrappify(fields):
27+
for field in fields:
28+
widget = fields[field].widget
29+
if not isinstance(widget, CheckboxInput):
30+
if 'class' in widget.attrs:
31+
widget.attrs['class'] = widget.attrs['class'] + " form-control"
32+
else:
33+
widget.attrs.update({'class':'form-control'})
34+
35+
class SelectPermafrostRoleTypeForm(ModelForm):
36+
name = CharField(required=False)
37+
description = CharField(required=False, widget=Textarea())
38+
category = ChoiceField(choices=CHOICES)
39+
40+
class Meta:
41+
model = PermafrostRole
42+
fields = ('name', 'description', 'category',)
43+
44+
def __init__(self, *args, **kwargs):
45+
super().__init__(*args, **kwargs)
46+
bootstrappify(self.fields)
47+
48+
49+
50+
51+
class PermafrostRoleCreateForm(ModelForm):
52+
class Meta:
53+
model = PermafrostRole
54+
fields = ('name', 'description', 'category',)
55+
widgets = {
56+
'description': Textarea(),
57+
}
58+
59+
def __init__(self, *args, **kwargs):
60+
super().__init__(*args, **kwargs)
61+
62+
self.fields['category'].choices = CHOICES
63+
category = self.initial.get('category', None)
64+
65+
bootstrappify(self.fields)
66+
67+
if category:
68+
69+
required_perms = get_required_by_category(category)
70+
optional_perms = get_optional_by_category(category)
71+
required_choices = assemble_optiongroups_for_widget(required_perms)
72+
optional_choices = assemble_optiongroups_for_widget(optional_perms)
73+
74+
initial = [perm.pk for perm in required_perms]
75+
76+
self.fields[f'optional_{category}_perms'] = MultipleChoiceField(label=_("Optional Permissions"), choices=optional_choices, widget=CheckboxSelectMultiple(), required=False)
77+
self.fields[f'required_{category}_perms'] = MultipleChoiceField(label=_("Required Permissions"), initial=initial, choices=required_choices, widget=CheckboxSelectMultiple(attrs={'readonly':True, 'disabled': True}), required=False)
78+
79+
def save(self, commit=True):
80+
instance = super().save(commit)
81+
category = instance.category
82+
if self.cleaned_data and f'optional_{category}_perms' in self.cleaned_data:
83+
perm_ids = []
84+
if category:
85+
perm_ids = self.cleaned_data[f'optional_{category}_perms' ]
86+
if perm_ids:
87+
instance.permissions_set(Permission.objects.filter(id__in=perm_ids))
88+
else:
89+
instance.permissions_clear()
90+
return instance
91+
92+
class PermafrostRoleUpdateForm(PermafrostRoleCreateForm):
93+
"""
94+
Form used to display role detail
95+
Only allowed to edit optional permissions, name and description
96+
Category and required permissions stay locked
97+
"""
98+
category = ChoiceField(choices=CHOICES, required=False)
99+
deleted = BooleanField(required=False)
100+
101+
def __init__(self, *args, **kwargs):
102+
super().__init__(*args, **kwargs)
103+
104+
self.fields['category'].widget.attrs.update({'readonly': True, 'disabled': True})
105+
self.fields['category'].disabled = True
106+
self.fields['category'].required = False
107+
self.fields['category'].initial = self.instance.category
108+
109+
self.fields['deleted'].initial = self.instance.deleted
110+
111+
category = self.instance.category
112+
113+
optional_perms = get_optional_by_category(category)
114+
optional_choices = assemble_optiongroups_for_widget(optional_perms)
115+
116+
available_optional_ids = [permission.id for permission in optional_perms]
117+
preselected_optional = [permission.id for permission in self.instance.permissions().all() if permission.id in available_optional_ids]
118+
119+
self.fields.update({
120+
f'optional_{category}_perms': MultipleChoiceField(
121+
label=_("Optional Permissions"),
122+
initial=preselected_optional,
123+
choices=optional_choices,
124+
widget=CheckboxSelectMultiple(),
125+
required=False
126+
)
127+
})
128+
129+
def save(self, commit=True):
130+
if self.cleaned_data['deleted']:
131+
self.instance.deleted = self.cleaned_data['deleted']
132+
instance = super().save(commit)
133+
return instance
134+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 3.1.3 on 2020-11-24 15:34
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('permafrost', '0017_delete_permafrostcategory'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='permafrostrole',
15+
name='description',
16+
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Description'),
17+
),
18+
migrations.AlterField(
19+
model_name='permafrostrole',
20+
name='category',
21+
field=models.CharField(choices=[('administration', 'Administration'), ('staff', 'Staff'), ('user', 'User')], max_length=32, verbose_name='Role Type'),
22+
),
23+
]

0 commit comments

Comments
 (0)