Skip to content

Commit

Permalink
Merge branch 'master' into update-qa-dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
nemesifier authored Feb 29, 2024
2 parents 3edcd67 + 2440f96 commit 5d5f3d1
Show file tree
Hide file tree
Showing 56 changed files with 2,323 additions and 273 deletions.
6 changes: 0 additions & 6 deletions .stylelintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,11 @@
"comment-empty-line-before": ["always", {
"ignore": ["stylelint-commands", "after-comment"]
}],
"declaration-colon-space-after": "always",
"indentation": [2, {
"except": ["value"]
}],
"max-empty-lines": 4,
"rule-empty-line-before": ["never-multi-line", {
"except": ["first-nested"],
"ignore": ["after-comment", "inside-block"]
}],
"unit-allowed-list": ["em", "rem", "%", "s", "px", "vh", "deg", "dpi", "fr"],
"declaration-block-trailing-semicolon": "always",
"property-no-unknown": true
}
}
2 changes: 1 addition & 1 deletion CONTRIBUTING.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Contributing
============

Please read the Contributing section in the README of this project.
Please refer to the `OpenWISP contributing guidelines <http://openwisp.io/docs/developer/contributing.html>`_.

294 changes: 269 additions & 25 deletions README.rst

Large diffs are not rendered by default.

20 changes: 8 additions & 12 deletions openwisp-qa-check
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,14 @@ runblack() {

runcheckcommit() {
if [ -z "$COMMIT_MESSAGE" ]; then COMMIT_MESSAGE=$(git log -1 --pretty=%B); fi
if [ "$TRAVIS" = true ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then
echo "SKIPPED: Commit message check skipped!"
else
checkcommit --message "$COMMIT_MESSAGE" &&
echo "SUCCESS: Commit message check successful!" ||
{
echo -e "Checked commit message:\n\n"
echo -e "$COMMIT_MESSAGE\n\n"
echoerr "ERROR: Commit message check failed!"
FAILURE=1
}
fi

checkcommit --message "$COMMIT_MESSAGE" &&
echo "SUCCESS: Commit message check successful!" ||
{
echo -e "Checked commit message:\n\n$COMMIT_MESSAGE\n\n"
echoerr "ERROR: Commit message check failed!"
FAILURE=1
}
}

runcheckpendingmigrations() {
Expand Down
22 changes: 22 additions & 0 deletions openwisp_utils/admin_theme/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@

from django.conf import settings
from django.contrib import admin
from django.shortcuts import render
from django.urls import path
from django.utils.module_loading import import_string
from django.utils.translation import gettext_lazy
from django.utils.translation import gettext_lazy as _

from . import settings as app_settings
from .dashboard import get_dashboard_context
from .system_info import (
get_enabled_openwisp_modules,
get_openwisp_version,
get_os_details,
)

logger = logging.getLogger(__name__)

Expand All @@ -30,6 +37,16 @@ def index(self, request, extra_context=None):
context = {'dashboard_enabled': False}
return super().index(request, extra_context=context)

def openwisp_info(self, request, *args, **kwargs):
context = {
'enabled_openwisp_modules': get_enabled_openwisp_modules(),
'system_info': get_os_details(),
'openwisp_version': get_openwisp_version(),
'title': _('System Information'),
'site_title': self.site_title,
}
return render(request, 'admin/openwisp_info.html', context)

def get_urls(self):
autocomplete_view = import_string(app_settings.AUTOCOMPLETE_FILTER_VIEW)
return [
Expand All @@ -38,6 +55,11 @@ def get_urls(self):
self.admin_view(autocomplete_view.as_view(admin_site=self)),
name='ow-auto-filter',
),
path(
'openwisp-system-info/',
self.admin_view(self.openwisp_info),
name='ow-info',
),
] + super().get_urls()


Expand Down
8 changes: 8 additions & 0 deletions openwisp_utils/admin_theme/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ def register_menu_groups(self):
position=10,
config={'label': _('Home'), 'url': '/admin', 'icon': 'ow-dashboard-icon'},
)
register_menu_group(
position=899,
config={
'label': _('System info'),
'url': '/admin/openwisp-system-info/',
'icon': 'ow-info-icon',
},
)

def modify_admin_theme_settings_links(self):
link_files = []
Expand Down
57 changes: 48 additions & 9 deletions openwisp_utils/admin_theme/dashboard.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import copy
import html

from django.core.exceptions import ImproperlyConfigured
from django.db.models import Count
Expand All @@ -18,7 +19,11 @@ def _validate_chart_config(config):
assert 'name' in config
assert 'app_label' in query_params
assert 'model' in query_params
assert 'group_by' in query_params or 'annotate' in query_params
assert (
'filter' in query_params
or 'group_by' in query_params
or 'annotate' in query_params
)
assert not ('group_by' in query_params and 'annotate' in query_params)
if 'annotate' in query_params:
assert 'filters' in config, 'filters must be defined when using annotate'
Expand Down Expand Up @@ -135,12 +140,18 @@ def get_dashboard_context(request):
query_params = value['query_params']
app_label = query_params['app_label']
model_name = query_params['model']
qs_filter = query_params.get('filter')
group_by = query_params.get('group_by')
annotate = query_params.get('annotate')
aggregate = query_params.get('aggregate')
org_field = query_params.get('organization_field')
default_org_field = 'organization_id'

labels_i18n = value.get('labels')
# HTML escape labels defined in configuration to prevent breaking the JS
if labels_i18n:
for label_key, label_value in labels_i18n.items():
labels_i18n[label_key] = html.escape(label_value)

try:
model = load_model(app_label, model_name)
Expand All @@ -150,7 +161,12 @@ def get_dashboard_context(request):
f'REASON: {app_label}.{model_name} could not be loaded.'
)

qs = model.objects.all()
qs = model.objects
if qs_filter:
for field, lookup_value in qs_filter.items():
if callable(lookup_value):
qs_filter[field] = lookup_value()
qs = qs.filter(**qs_filter)

# Filter query according to organization of user
if not request.user.is_superuser and (
Expand Down Expand Up @@ -179,6 +195,20 @@ def get_dashboard_context(request):
labels = []
colors = []
filters = []
main_filters = []
url_operator = '?'
value['target_link'] = f'/admin/{app_label}/{model_name}/'
if value.get('main_filters'):
for main_filter_key, main_filter_value in value['main_filters'].items():
if callable(main_filter_value):
main_filter_value = str(main_filter_value())
main_filters.append(f'{main_filter_key}={main_filter_value}')

value['target_link'] = '{path}?{main_filters}'.format(
path=value['target_link'], main_filters='&'.join(main_filters)
)
value.pop('main_filters', None)
url_operator = '&'

if group_by:
for obj in qs:
Expand All @@ -192,18 +222,22 @@ def get_dashboard_context(request):
if labels_i18n and qs_key in labels_i18n:
# store original label as filter, but only
# if we have more than the empty default label defined
# if len(labels_i18n.keys()) > 1
filters.append(label)
label = labels_i18n[qs_key]
else:
# HTML escape labels coming from values in the DB
# to avoid possible XSS attacks caused by
# malicious DB values set by users
label = html.escape(label)
labels.append(label)
# use predefined colors if available,
# otherwise the JS lib will choose automatically
if value.get('colors') and qs_key in value['colors']:
colors.append(value['colors'][qs_key])
values.append(obj['count'])
value[
'target_link'
] = f'/admin/{app_label}/{model_name}/?{group_by}__exact='
value['target_link'] = '{path}{url_operator}{group_by}__exact='.format(
path=value['target_link'], url_operator=url_operator, group_by=group_by
)

if aggregate:
for qs_key, qs_value in qs.items():
Expand All @@ -212,9 +246,14 @@ def get_dashboard_context(request):
labels.append(labels_i18n[qs_key])
values.append(qs_value)
colors.append(value['colors'][qs_key])
filters.append(value['filters'][qs_key])
filter_key = value['filters']['key']
value['target_link'] = f'/admin/{app_label}/{model_name}/?{filter_key}='
if value.get('filters'):
filters.append(value['filters'][qs_key])
if value.get('filters'):
value['target_link'] = '{path}{url_operator}{filter_key}='.format(
url_operator=url_operator,
path=value['target_link'],
filter_key=value['filters']['key'],
)

value['query_params'] = {'values': values, 'labels': labels}
value['colors'] = colors
Expand Down
Loading

0 comments on commit 5d5f3d1

Please sign in to comment.