Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions openwisp_utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
VERSION = (0, 1, 0, 'alpha')
__version__ = VERSION # alias


def get_version():
version = '%s.%s.%s' % (VERSION[0], VERSION[1], VERSION[2])
if VERSION[3] != 'final':
first_letter = VERSION[3][0:1]
try:
suffix = VERSION[4]
except IndexError:
suffix = 0
version = '%s%s%s' % (version, first_letter, suffix)
return version
98 changes: 98 additions & 0 deletions openwisp_utils/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from django.contrib import admin
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _


class MultitenantAdminMixin(object):
"""
Mixin that makes a ModelAdmin class multitenant:
users will see only the objects related to the organizations
they are associated with.
"""
multitenant_shared_relations = []

def get_repr(self, obj):
return str(obj)

get_repr.short_description = _('name')

def get_queryset(self, request):
"""
If current user is not superuser, show only the
objects associated to organizations he/she is associated with
"""
qs = super(MultitenantAdminMixin, self).get_queryset(request)
if request.user.is_superuser:
return qs
organizations = request.user.organizations_pk
return qs.filter(organization__in=organizations)

def _edit_form(self, request, form):
"""
Modifies the form querysets as follows;
if current user is not superuser:
* show only relevant organizations
* show only relations associated to relevant organizations
or shared relations
else show everything
"""
fields = form.base_fields
if not request.user.is_superuser:
orgs_pk = request.user.organizations_pk
# organizations relation;
# may be readonly and not present in field list
if 'organization' in fields:
org_field = fields['organization']
org_field.queryset = org_field.queryset.filter(pk__in=orgs_pk)
# other relations
q = Q(organization__in=orgs_pk) | Q(organization=None)
for field_name in self.multitenant_shared_relations:
# each relation may be readonly
# and not present in field list
if field_name not in fields:
continue
field = fields[field_name]
field.queryset = field.queryset.filter(q)

def get_form(self, request, obj=None, **kwargs):
form = super(MultitenantAdminMixin, self).get_form(request, obj, **kwargs)
self._edit_form(request, form)
return form

def get_formset(self, request, obj=None, **kwargs):
formset = super(MultitenantAdminMixin, self).get_formset(request, obj=None, **kwargs)
self._edit_form(request, formset.form)
return formset


class MultitenantOrgFilter(admin.RelatedFieldListFilter):
"""
Admin filter that shows only organizations the current
user is associated with in its available choices
"""
multitenant_lookup = 'pk__in'

def field_choices(self, field, request, model_admin):
if request.user.is_superuser:
return super(MultitenantOrgFilter, self).field_choices(field, request, model_admin)
organizations = request.user.organizations_pk
return field.get_choices(include_blank=False,
limit_choices_to={self.multitenant_lookup: organizations})


class MultitenantObjectFilter(MultitenantOrgFilter):
"""
Admin filter that shows only objects of
organizations the current user is associated with
"""
multitenant_lookup = 'organization__in'


class TimeReadonlyAdminMixin(object):
"""
mixin that automatically flags
`created` and `modified` as readonly
"""
def __init__(self, *args, **kwargs):
self.readonly_fields += ('created', 'modified',)
super(TimeReadonlyAdminMixin, self).__init__(*args, **kwargs)
Empty file.
13 changes: 13 additions & 0 deletions openwisp_utils/admin_theme/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.contrib import admin
from django.utils.translation import ugettext_lazy


def openwisp_admin(site_url=None):
# <title>
admin.site.site_title = ugettext_lazy('OpenWISP2 Admin')
# link to frontend
admin.site.site_url = site_url
# h1 text
admin.site.site_header = ugettext_lazy('OpenWISP')
# text at the top of the admin index page
admin.site.index_title = ugettext_lazy('Network administration')
102 changes: 102 additions & 0 deletions openwisp_utils/admin_theme/static/admin/css/openwisp.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#header{
background-color: #464646;
color: #fff;
height: auto
}

#user-tools{ margin-top: 12px }

#site-name a{
display: block;
width: 150px;
height: 70px;
background: url(../../../static/ui/openwisp/images/openwisp-logo.svg) no-repeat scroll 0 50% / 100%;
text-indent: -2000px;
overflow: hidden;
}

.login #branding{
float: none;
padding: 8px 0
}
.login #site-name a{ margin: 0 auto }

.button.default,
input[type=submit].default,
.submit-row input.default,
.object-tools a:focus,
.object-tools a:hover{
background-color: #333;
}

.button.default:active, input[type=submit].default:active,
.button.default:focus, input[type=submit].default:focus,
.button.default:hover, input[type=submit].default:hover{
background-color: #000;
}

#branding h1, #branding h1 a:link, #branding h1 a:visited {
color: #fff;
}

div.breadcrumbs,
.module h2, .module caption, .inline-group h2,
#container .selector-chosen h2{
background-color: #df5d43;
color: #fff;
}

div.breadcrumbs{
color: #ffcbc0
}

div.breadcrumbs a:focus, div.breadcrumbs a:hover{
color: #ffe8e3
}

.button, input[type=submit], input[type=button], .submit-row input, a.button{
background-color: #666;
}

.button:active, input[type=submit]:active, input[type=button]:active,
.button:focus, input[type=submit]:focus, input[type=button]:focus,
.button:hover, input[type=submit]:hover, input[type=button]:hover {
background-color: #444;
}

#user-tools a:focus, #user-tools a:hover{
border-bottom-color: #ffcbc0;
color: #ffcbc0;
}

#container .object-tools a{
padding: 5px 15px;
}

#container .object-tools a.addlink{ padding-right: 28px }

table thead th.sorted .sortoptions a.sortremove:focus:after,
table thead th.sorted .sortoptions a.sortremove:hover:after,
a:link, a:visited {
color: #777;
}

a:focus, a:hover {
color: #000;
}

#changelist-filter li.selected a{
color: #df5d43 !important
}

#netjsonconfig-hint, #netjsonconfig-hint a,
#container .field-backend a{ color: #df5d43 }

/* temporary frontend fix */
a.button.secondaryAction{
background: transparent;
border: 0 none;
padding: 0;
margin: 15px 0;
display: block;
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading