From 81d64162f4ded425b850a1f61158b12bbc9f07d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez?= Date: Tue, 12 Nov 2013 01:09:17 +0000 Subject: [PATCH] Start the work over admin interface (Dashboard) in parallel with REST api --- .../org.eclipse.core.resources.prefs | 4 + server/src/uds/REST/__init__.py | 13 +- server/src/uds/REST/handlers.py | 24 +- server/src/uds/REST/methods/authentication.py | 14 +- server/src/uds/REST/methods/providers.py | 35 ++ server/src/uds/admin/__init__.py | 0 server/src/uds/admin/urls.py | 40 ++ server/src/uds/admin/views.py | 62 ++++ server/src/uds/core/auths/auth.py | 4 +- server/src/uds/templates/uds/admin/index.html | 344 ++++++++++++++++++ .../src/uds/templates/uds/admin/sample.html | 344 ++++++++++++++++++ .../templates/uds/admin/templates/base.html | 71 ++++ .../templates/uds/html5/snippets/navbar.html | 8 +- server/src/uds/templatetags/REST.py | 2 +- server/src/uds/urls.py | 12 +- server/src/uds/web/views.py | 2 +- 16 files changed, 956 insertions(+), 23 deletions(-) create mode 100644 server/src/uds/REST/methods/providers.py create mode 100644 server/src/uds/admin/__init__.py create mode 100644 server/src/uds/admin/urls.py create mode 100644 server/src/uds/admin/views.py create mode 100644 server/src/uds/templates/uds/admin/index.html create mode 100644 server/src/uds/templates/uds/admin/sample.html create mode 100644 server/src/uds/templates/uds/admin/templates/base.html diff --git a/server/.settings/org.eclipse.core.resources.prefs b/server/.settings/org.eclipse.core.resources.prefs index 117355f8..15d55178 100644 --- a/server/.settings/org.eclipse.core.resources.prefs +++ b/server/.settings/org.eclipse.core.resources.prefs @@ -11,8 +11,11 @@ encoding//src/server/urls.py=utf-8 encoding//src/uds/REST/__init__.py=utf-8 encoding//src/uds/REST/handlers.py=utf-8 encoding//src/uds/REST/methods/authentication.py=utf-8 +encoding//src/uds/REST/methods/providers.py=utf-8 encoding//src/uds/REST/processors.py=utf-8 encoding//src/uds/__init__.py=utf-8 +encoding//src/uds/admin/urls.py=utf-8 +encoding//src/uds/admin/views.py=utf-8 encoding//src/uds/auths/ActiveDirectory/Authenticator.py=utf-8 encoding//src/uds/auths/ActiveDirectory/__init__.py=utf-8 encoding//src/uds/auths/ActiveDirectory_enterprise/Authenticator.py=utf-8 @@ -194,6 +197,7 @@ encoding//src/uds/services/Vmware_enterprise/client/Exceptions.py=utf-8 encoding//src/uds/services/Vmware_enterprise/client/Server.py=utf-8 encoding//src/uds/services/Vmware_enterprise/client/Task.py=utf-8 encoding//src/uds/services/__init__.py=utf-8 +encoding//src/uds/templatetags/REST.py=utf-8 encoding//src/uds/templatetags/html5.py=utf-8 encoding//src/uds/tests/__init__.py=utf-8 encoding//src/uds/tests/core/__init__.py=utf-8 diff --git a/server/src/uds/REST/__init__.py b/server/src/uds/REST/__init__.py index 0d2be70e..3c54a232 100644 --- a/server/src/uds/REST/__init__.py +++ b/server/src/uds/REST/__init__.py @@ -35,7 +35,7 @@ from django import http from django.views.generic.base import View from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext as _, activate from django.conf import settings from handlers import Handler, HandlerError, AccessDenied @@ -102,12 +102,18 @@ class Dispatcher(View): for l in settings.LANGUAGES: if args[-1] == l[0]: lang = l[0] + activate(lang) logger.error('Found lang {0}'.format(l)) args = args[:-1] break - # Intantiate method handler and locate http_method dispatcher + # Instantiate method handler and locate http_method dispatcher try: handler = cls(request, full_path, http_method, processor.processParameters(), *args, **kwargs) + # If no lang on request, try to get the one from + if lang is None: + activate(handler.getValue('locale')) + else: + handler.setValue('locale', lang) # Update Locale if request had one operation = getattr(handler, http_method) except processors.ParametersException as e: @@ -119,12 +125,13 @@ class Dispatcher(View): allowedMethods.append(n) return http.HttpResponseNotAllowed(allowedMethods) except AccessDenied: - return http.HttpResponseForbidden('method access denied') + return http.HttpResponseForbidden('access denied') except: logger.exception('error accessing attribute') logger.debug('Getting attribute {0} for {1}'.format(http_method, full_path)) return http.HttpResponseServerError('Unexcepected error') + # Invokes the handler's operation, add headers to response and returns try: response = processor.getResponse(operation()) diff --git a/server/src/uds/REST/handlers.py b/server/src/uds/REST/handlers.py index c90c7eac..1f427a07 100644 --- a/server/src/uds/REST/handlers.py +++ b/server/src/uds/REST/handlers.py @@ -53,13 +53,14 @@ class Handler(object): name = None # If name is not used, name will be the class name in lower case path = None # Path for this method, so we can do /auth/login, /auth/logout, /auth/auths in a simple way authenticated = True # By default, all handlers needs authentication - only_admin = False # By default, the methods will be accesible by anyone + needs_admin = False # By default, the methods will be accessible by anyone if nothine else indicated + needs_staff = False # By default, staff # method names: 'get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace' def __init__(self, request, path, operation, params, *args, **kwargs): - if self.only_admin: - self.authenticated = True # If only_admin, must also be authenticated + if self.needs_admin: + self.authenticated = True # If needs_admin, must also be authenticated self._request = request self._path = path @@ -87,7 +88,10 @@ class Handler(object): if self._authToken is None: raise AccessDenied() - if self.only_admin and not self.getValue('is_admin'): + if self.needs_admin and not self.getValue('is_admin'): + raise AccessDenied() + + if self.needs_staff and not self.getValue('staff_member'): raise AccessDenied() def headers(self): @@ -111,7 +115,9 @@ class Handler(object): @staticmethod def storeSessionAuthdata(session, id_auth, username, locale, is_admin, staff_member): - session['REST'] = { 'auth': id_auth, 'username': username, 'locale': locale, 'is_admin': is_admin, 'staff_member': staff_member } + session['REST'] = { 'auth': id_auth, 'username': username, + 'locale': locale, 'is_admin': is_admin, + 'staff_member': staff_member } def genAuthToken(self, id_auth, username, locale, is_admin, staf_member): @@ -135,3 +141,11 @@ class Handler(object): return self._session['REST'].get(key) except: return None + + def setValue(self, key, value): + try: + self._session['REST'][key] = value + self._session.accessed = True + self._session.save() + except: + pass diff --git a/server/src/uds/REST/methods/authentication.py b/server/src/uds/REST/methods/authentication.py index bf948fb4..62ecaf99 100644 --- a/server/src/uds/REST/methods/authentication.py +++ b/server/src/uds/REST/methods/authentication.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2012 Virtual Cable S.L. +# Copyright (c) 2014 Virtual Cable S.L. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -41,11 +41,11 @@ import logging logger = logging.getLogger(__name__) +# Enclosed methods under /auth path class Login(Handler): - path = 'auth' - authenticated = False # By default, all handlers needs authentication - admin_method = False # By default, the methods will be accesible by anyone + path = 'auth' + authenticated = False # Public method def post(self): ''' @@ -72,6 +72,7 @@ class Login(Handler): return{'result': 'ok', 'token': self.getAuthToken()} else: raise Exception('Invalid credentials') + raise Exception('Invalid Credentials') except Exception as e: logger.exception('exception') return {'result': 'error', 'error': unicode(e)} @@ -80,7 +81,6 @@ class Login(Handler): class Logout(Handler): path = 'auth' authenticated = True # By default, all handlers needs authentication - admin_method = False # By default, the methods will be accesible by anyone def get(self): # Remove auth token @@ -90,9 +90,9 @@ class Logout(Handler): def post(self): return self.get() -class Auth(Handler): +class Auths(Handler): + path = 'auth' authenticated = False # By default, all handlers needs authentication - admin_method = False # By default, the methods will be accesible by anyone def auths(self): for a in Authenticator.all(): diff --git a/server/src/uds/REST/methods/providers.py b/server/src/uds/REST/methods/providers.py new file mode 100644 index 00000000..92a0509f --- /dev/null +++ b/server/src/uds/REST/methods/providers.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2014 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from __future__ import unicode_literals + +from uds.REST import Handler, HandlerError diff --git a/server/src/uds/admin/__init__.py b/server/src/uds/admin/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/src/uds/admin/urls.py b/server/src/uds/admin/urls.py new file mode 100644 index 00000000..95e6d2d6 --- /dev/null +++ b/server/src/uds/admin/urls.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' + +from django.conf import settings +from django.conf.urls import patterns, include +from uds.core.util.modfinder import loadModulesUrls +from uds import REST + +urlpatterns = patterns('uds.admin.views', + (r'^$', 'index'), +) diff --git a/server/src/uds/admin/views.py b/server/src/uds/admin/views.py new file mode 100644 index 00000000..9fa01c42 --- /dev/null +++ b/server/src/uds/admin/views.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2014 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from __future__ import unicode_literals + +from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden +from django.views.decorators.csrf import csrf_exempt +from django.shortcuts import render_to_response +from django.shortcuts import render +from django.template import RequestContext +from django.utils.translation import ugettext as _ +from django.core.urlresolvers import reverse +from uds.core.auths.auth import getIp, webLogin, webLogout, webLoginRequired, authenticate, webPassword, authenticateViaCallback, authLogLogin, authLogLogout +from uds.models import Authenticator, DeployedService, Transport, UserService, Network +from uds.web.forms.LoginForm import LoginForm +from uds.core.managers.UserServiceManager import UserServiceManager +from uds.core.managers.UserPrefsManager import UserPrefsManager +from uds.core.managers.DownloadsManager import DownloadsManager +from uds.core.util.Config import GlobalConfig +from uds.core.util.Cache import Cache +from uds.core.util import OsDetector +from uds.core.util import log + +import logging + + +logger = logging.getLogger(__name__) + +@webLoginRequired +def index(request): + if request.user.isStaff() is False: + return HttpResponseForbidden(_('Forbidden')) + + return render(request, 'uds/admin/index.html') diff --git a/server/src/uds/core/auths/auth.py b/server/src/uds/core/auths/auth.py index 789f6fe8..5123f52f 100644 --- a/server/src/uds/core/auths/auth.py +++ b/server/src/uds/core/auths/auth.py @@ -223,13 +223,13 @@ def webLogin(request, response, user, password): Helper function to, once the user is authenticated, store the information at the user session. @return: Always returns True ''' - from uds.REST import Handler + from uds import REST user.updateLastAccess() request.session.clear() request.session[USER_KEY] = user.id request.session[PASS_KEY] = CryptoManager.manager().xor(password.encode('utf-8'), request.COOKIES['uds']) # Ensures that this user will have access througt REST api if logged in through web interface - Handler.storeSessionAuthdata(request.session, user.manager.small_name, user.name, get_language(), user.is_admin, user.staff_member) + REST.Handler.storeSessionAuthdata(request.session, user.manager.small_name, user.name, get_language(), user.is_admin, user.staff_member) return True diff --git a/server/src/uds/templates/uds/admin/index.html b/server/src/uds/templates/uds/admin/index.html new file mode 100644 index 00000000..029fbf21 --- /dev/null +++ b/server/src/uds/templates/uds/admin/index.html @@ -0,0 +1,344 @@ +{% extends 'uds/admin/templates/base.html' %} +{% block body %} + +
+
+ +
+ + + My Dashboard + +
+ +
+ + + + +
+
Inbox Messages 3
+ +
+ + + +
+ +
+

Reports

+
+ + Success +
+
+ 72% Complete +
+
+ Info +
+
+ 20% Complete +
+
+ Warning +
+
+ 60% Complete (warning) +
+
+ Danger +
+
+ 80% Complete +
+
+ +
+
+ +
+ + +
+ + +
+
+

+ Lorem profile dolor sit amet, consectetur adipiscing elit. Duis pharetra varius quam sit amet vulputate. +

Quisque mauris augue, molestie tincidunt condimentum vitae, gravida a libero. Aenean sit amet felis + dolor, in sagittis nisi. Sed ac orci quis tortor imperdiet venenatis. Duis elementum auctor accumsan. + Aliquam in felis sit amet augue.

+
+
+

+ Message ipsum dolor sit amet, consectetur adipiscing elit. Duis pharetra varius quam sit amet vulputate. +

Quisque mauris augue, molestie tincidunt condimentum vitae, gravida a libero. Aenean sit amet felis + dolor, in sagittis nisi. Sed ac orci quis tortor imperdiet venenatis. Duis elementum auctor accumsan. + Aliquam in felis sit amet augue.

+
+
+

+ Lorem settings dolor sit amet, consectetur adipiscing elit. Duis pharetra varius quam sit amet vulputate. +

Quisque mauris augue, molestie tincidunt condimentum vitae, gravida a libero. Aenean sit amet felis + dolor, in sagittis nisi. Sed ac orci quis tortor imperdiet venenatis. Duis elementum auctor accumsan. + Aliquam in felis sit amet augue.

+
+
+
+ + +
+ + + +
+
+
+

Notices

+
+ +
+ + This is a dismissable alert.. just sayin'. +
+ + This is a dashboard-style layout that uses Bootstrap 3. You can use this template as a starting point to create something more unique. +

+ Visit the Bootstrap Playground at Bootply to tweak this layout or discover more useful code snippets. +
+
+ + + + + + + + + + + + +
VisitsROISource
452.45%Direct
28956.2%Referral
9825%Type
......
......
+ +
+
+
+ +

Post Request

+
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+ +
+

Engagement

+
+
+
+
+
+
+ +
+ +
+ +
+ + Discussions + +
+ + +
+
+
+ + + + + + +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/admin/sample.html b/server/src/uds/templates/uds/admin/sample.html new file mode 100644 index 00000000..029fbf21 --- /dev/null +++ b/server/src/uds/templates/uds/admin/sample.html @@ -0,0 +1,344 @@ +{% extends 'uds/admin/templates/base.html' %} +{% block body %} + +
+
+ +
+ + + My Dashboard + +
+ +
+ + + + +
+
Inbox Messages 3
+ +
+ + + +
+ +
+

Reports

+
+ + Success +
+
+ 72% Complete +
+
+ Info +
+
+ 20% Complete +
+
+ Warning +
+
+ 60% Complete (warning) +
+
+ Danger +
+
+ 80% Complete +
+
+ +
+
+ +
+ + +
+ + +
+
+

+ Lorem profile dolor sit amet, consectetur adipiscing elit. Duis pharetra varius quam sit amet vulputate. +

Quisque mauris augue, molestie tincidunt condimentum vitae, gravida a libero. Aenean sit amet felis + dolor, in sagittis nisi. Sed ac orci quis tortor imperdiet venenatis. Duis elementum auctor accumsan. + Aliquam in felis sit amet augue.

+
+
+

+ Message ipsum dolor sit amet, consectetur adipiscing elit. Duis pharetra varius quam sit amet vulputate. +

Quisque mauris augue, molestie tincidunt condimentum vitae, gravida a libero. Aenean sit amet felis + dolor, in sagittis nisi. Sed ac orci quis tortor imperdiet venenatis. Duis elementum auctor accumsan. + Aliquam in felis sit amet augue.

+
+
+

+ Lorem settings dolor sit amet, consectetur adipiscing elit. Duis pharetra varius quam sit amet vulputate. +

Quisque mauris augue, molestie tincidunt condimentum vitae, gravida a libero. Aenean sit amet felis + dolor, in sagittis nisi. Sed ac orci quis tortor imperdiet venenatis. Duis elementum auctor accumsan. + Aliquam in felis sit amet augue.

+
+
+
+ + +
+ + + +
+
+
+

Notices

+
+ +
+ + This is a dismissable alert.. just sayin'. +
+ + This is a dashboard-style layout that uses Bootstrap 3. You can use this template as a starting point to create something more unique. +

+ Visit the Bootstrap Playground at Bootply to tweak this layout or discover more useful code snippets. +
+
+ + + + + + + + + + + + +
VisitsROISource
452.45%Direct
28956.2%Referral
9825%Type
......
......
+ +
+
+
+ +

Post Request

+
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+ +
+

Engagement

+
+
+
+
+
+
+ +
+ +
+ +
+ + Discussions + +
+ + +
+
+
+ + + + + + +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/admin/templates/base.html b/server/src/uds/templates/uds/admin/templates/base.html new file mode 100644 index 00000000..2140c2ef --- /dev/null +++ b/server/src/uds/templates/uds/admin/templates/base.html @@ -0,0 +1,71 @@ +{% load l10n i18n static html5 compress REST %}{% spaceless %} +{% get_current_language as LANGUAGE_CODE %} +{% get_available_languages as LANGUAGES %} +{% endspaceless %} + + + {% block title %}{% endblock %} + + + + + {% block meta %}{% endblock %} + + {% block icon %}{% endblock %} + + {% compress css %} + + + + + + + + + {% endcompress %} + + {% compress css %} + {% block css %}{% endblock %} + {% endcompress %} + + + + + {% block menu %}{% include 'uds/html5/snippets/navbar.html' %}{% endblock %} + +
+ {% block messages %} + {% if messages %} + {% for message in messages %} +
+ + {{ message }} +
+ {% endfor %} + {% endif %} + {% endblock %} + + {% block body %}{% endblock %} + +
+ + {% compress js %} + + + + + + {% endcompress %} + + {% compress js %} + {% block js %}{% endblock %} + {% endcompress %} + + \ No newline at end of file diff --git a/server/src/uds/templates/uds/html5/snippets/navbar.html b/server/src/uds/templates/uds/html5/snippets/navbar.html index 91d5a261..136393fa 100644 --- a/server/src/uds/templates/uds/html5/snippets/navbar.html +++ b/server/src/uds/templates/uds/html5/snippets/navbar.html @@ -21,7 +21,13 @@