From e2ad3e200179dd02bd8dfe42913b59bae9bd4263 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Sat, 18 Feb 2023 09:04:37 -0500 Subject: [PATCH] Revert "Merge pull request #1371 from AgentTNT/AdminLTE-Upgrade" This reverts commit 929cb6302da6bc0d78e2a7af9deb77cee9d80732, reversing changes made to 0418edddd9e5ddba0fdbe2b2abd2cf016cf887a3. --- configs/development.py | 11 - package.json | 14 +- powerdnsadmin/assets.py | 106 +- powerdnsadmin/routes/__init__.py | 3 +- powerdnsadmin/routes/admin.py | 25 +- powerdnsadmin/routes/base.py | 2 - powerdnsadmin/routes/index.py | 107 +- powerdnsadmin/static/custom/js/custom.js | 16 +- .../templates/admin_edit_account.html | 357 ++- powerdnsadmin/templates/admin_edit_key.html | 236 +- powerdnsadmin/templates/admin_edit_user.html | 248 +- .../templates/admin_global_search.html | 349 ++- powerdnsadmin/templates/admin_history.html | 258 +- .../templates/admin_history_table.html | 152 +- .../templates/admin_manage_account.html | 161 +- .../templates/admin_manage_keys.html | 254 +- .../templates/admin_manage_user.html | 378 ++- powerdnsadmin/templates/admin_pdns_stats.html | 211 +- .../admin_setting_authentication.html | 176 +- .../templates/admin_setting_basic.html | 183 +- .../templates/admin_setting_pdns.html | 177 +- .../templates/admin_setting_records.html | 172 +- powerdnsadmin/templates/base.html | 348 ++- powerdnsadmin/templates/dashboard.html | 498 ++-- powerdnsadmin/templates/dashboard_domain.html | 78 +- powerdnsadmin/templates/domain.html | 396 ++-- powerdnsadmin/templates/domain_add.html | 412 ++-- powerdnsadmin/templates/domain_changelog.html | 128 +- powerdnsadmin/templates/domain_remove.html | 210 +- powerdnsadmin/templates/domain_setting.html | 209 +- powerdnsadmin/templates/errors/400.html | 77 +- powerdnsadmin/templates/errors/403.html | 69 +- powerdnsadmin/templates/errors/404.html | 69 +- powerdnsadmin/templates/errors/500.html | 69 +- powerdnsadmin/templates/errors/SAML.html | 90 +- powerdnsadmin/templates/login.html | 325 ++- powerdnsadmin/templates/register.html | 282 +-- powerdnsadmin/templates/register_otp.html | 2 +- powerdnsadmin/templates/template.html | 218 +- powerdnsadmin/templates/template_add.html | 193 +- powerdnsadmin/templates/template_edit.html | 113 +- powerdnsadmin/templates/user_profile.html | 298 ++- requirements.txt | 39 +- yarn.lock | 2083 +++++++---------- 44 files changed, 4367 insertions(+), 5435 deletions(-) diff --git a/configs/development.py b/configs/development.py index b848d0c..d4bd24f 100644 --- a/configs/development.py +++ b/configs/development.py @@ -15,17 +15,6 @@ SQLA_DB_HOST = '127.0.0.1' SQLA_DB_NAME = 'pda' SQLALCHEMY_TRACK_MODIFICATIONS = True -#CAPTCHA Config -CAPTCHA_ENABLE = True -CAPTCHA_LENGTH = 6 -CAPTCHA_WIDTH = 160 -CAPTCHA_HEIGHT = 60 -CAPTCHA_SESSION_KEY = 'captcha_image' - -#Server side sessions tracking -#Set to TRUE for CAPTCHA, or enable another stateful session tracking system -FILESYSTEM_SESSIONS_ENABLED = True - ### DATABASE - MySQL #SQLALCHEMY_DATABASE_URI = 'mysql://{}:{}@{}/{}'.format( # urllib.parse.quote_plus(SQLA_DB_USER), diff --git a/package.json b/package.json index f6724c3..76982c8 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,15 @@ { "dependencies": { - "@fortawesome/fontawesome-free": "6.3.0", - "admin-lte": "3.2.0", - "bootstrap": "4.6.2", - "bootstrap-datepicker": "^1.9.0", + "admin-lte": "2.4.9", + "bootstrap": "^3.4.1", + "bootstrap-datepicker": "^1.8.0", "bootstrap-validator": "^0.11.9", - "datatables.net-plugins": "^1.13.1", + "datatables.net-plugins": "^1.10.19", "icheck": "^1.0.2", "jquery-slimscroll": "^1.3.8", - "jquery-sparkline": "^2.4.0", - "jquery-ui-dist": "^1.13.2", + "jquery-ui-dist": "^1.12.1", "jquery.quicksearch": "^2.4.0", - "jtimeout": "^3.2.0", + "jtimeout": "^3.1.0", "multiselect": "^0.9.12" } } diff --git a/powerdnsadmin/assets.py b/powerdnsadmin/assets.py index 7ad973f..233e23a 100644 --- a/powerdnsadmin/assets.py +++ b/powerdnsadmin/assets.py @@ -2,65 +2,67 @@ from flask_assets import Bundle, Environment, Filter class ConcatFilter(Filter): - """ - Filter that merges files, placing a semicolon between them. + """ + Filter that merges files, placing a semicolon between them. - Fixes issues caused by missing semicolons at end of JS assets, for example - with last statement of jquery.pjax.js. - """ - def concat(self, out, hunks, **kw): - out.write(';'.join([h.data() for h, info in hunks])) + Fixes issues caused by missing semicolons at end of JS assets, for example + with last statement of jquery.pjax.js. + """ + def concat(self, out, hunks, **kw): + out.write(';'.join([h.data() for h, info in hunks])) -css_login = Bundle( - 'node_modules/@fortawesome/fontawesome-free/css/all.min.css', - 'node_modules/icheck/skins/square/blue.css', - 'node_modules/admin-lte/dist/css/adminlte.css', - filters=('cssmin', 'cssrewrite'), - output='generated/login.css') -js_login = Bundle( - 'node_modules/jquery/dist/jquery.js', - 'node_modules/bootstrap/dist/js/bootstrap.js', - 'node_modules/icheck/icheck.js', - 'custom/js/custom.js', - filters=(ConcatFilter, 'rjsmin'), - output='generated/login.js') +css_login = Bundle('node_modules/bootstrap/dist/css/bootstrap.css', + 'node_modules/font-awesome/css/font-awesome.css', + 'node_modules/ionicons/dist/css/ionicons.css', + 'node_modules/icheck/skins/square/blue.css', + 'node_modules/admin-lte/dist/css/AdminLTE.css', + filters=('cssmin', 'cssrewrite'), + output='generated/login.css') -js_validation = Bundle( - 'node_modules/bootstrap-validator/dist/validator.js', - output='generated/validation.js') +js_login = Bundle('node_modules/jquery/dist/jquery.js', + 'node_modules/bootstrap/dist/js/bootstrap.js', + 'node_modules/icheck/icheck.js', + 'custom/js/custom.js', + filters=(ConcatFilter, 'rjsmin'), + output='generated/login.js') + +js_validation = Bundle('node_modules/bootstrap-validator/dist/validator.js', + output='generated/validation.js') css_main = Bundle( - 'node_modules/@fortawesome/fontawesome-free/css/all.min.css', - 'node_modules/datatables.net-bs4/css/dataTables.bootstrap4.min.css', - 'node_modules/icheck/skins/square/blue.css', - 'node_modules/multiselect/css/multi-select.css', - 'node_modules/admin-lte/dist/css/adminlte.css', - 'custom/css/custom.css', - 'node_modules/bootstrap-datepicker/dist/css/bootstrap-datepicker.css', - filters=('cssmin', 'cssrewrite'), - output='generated/main.css') + 'node_modules/bootstrap/dist/css/bootstrap.css', + 'node_modules/font-awesome/css/font-awesome.css', + 'node_modules/ionicons/dist/css/ionicons.css', + 'node_modules/datatables.net-bs/css/dataTables.bootstrap.css', + 'node_modules/icheck/skins/square/blue.css', + 'node_modules/multiselect/css/multi-select.css', + 'node_modules/admin-lte/dist/css/AdminLTE.css', + 'node_modules/admin-lte/dist/css/skins/_all-skins.css', + 'custom/css/custom.css', + 'node_modules/bootstrap-datepicker/dist/css/bootstrap-datepicker.css', + filters=('cssmin', 'cssrewrite'), + output='generated/main.css') -js_main = Bundle( - 'node_modules/jquery/dist/jquery.js', - 'node_modules/jquery-ui-dist/jquery-ui.js', - 'node_modules/bootstrap/dist/js/bootstrap.bundle.js', - 'node_modules/datatables.net/js/jquery.dataTables.js', - 'node_modules/datatables.net-bs4/js/dataTables.bootstrap4.js', - 'node_modules/jquery-sparkline/jquery.sparkline.js', - 'node_modules/jquery-slimscroll/jquery.slimscroll.js', - 'node_modules/icheck/icheck.js', - 'node_modules/fastclick/lib/fastclick.js', - 'node_modules/moment/moment.js', - 'node_modules/admin-lte/dist/js/adminlte.js', - 'node_modules/multiselect/js/jquery.multi-select.js', - 'node_modules/datatables.net-plugins/sorting/natural.js', - 'node_modules/jtimeout/src/jTimeout.js', - 'node_modules/jquery.quicksearch/src/jquery.quicksearch.js', - 'custom/js/custom.js', - 'node_modules/bootstrap-datepicker/dist/js/bootstrap-datepicker.js', - filters=(ConcatFilter, 'rjsmin'), - output='generated/main.js') +js_main = Bundle('node_modules/jquery/dist/jquery.js', + 'node_modules/jquery-ui-dist/jquery-ui.js', + 'node_modules/bootstrap/dist/js/bootstrap.js', + 'node_modules/datatables.net/js/jquery.dataTables.js', + 'node_modules/datatables.net-bs/js/dataTables.bootstrap.js', + 'node_modules/jquery-sparkline/jquery.sparkline.js', + 'node_modules/jquery-slimscroll/jquery.slimscroll.js', + 'node_modules/icheck/icheck.js', + 'node_modules/fastclick/lib/fastclick.js', + 'node_modules/moment/moment.js', + 'node_modules/admin-lte/dist/js/adminlte.js', + 'node_modules/multiselect/js/jquery.multi-select.js', + 'node_modules/datatables.net-plugins/sorting/natural.js', + 'node_modules/jtimeout/src/jTimeout.js', + 'node_modules/jquery.quicksearch/src/jquery.quicksearch.js', + 'custom/js/custom.js', + 'node_modules/bootstrap-datepicker/dist/js/bootstrap-datepicker.js', + filters=(ConcatFilter, 'rjsmin'), + output='generated/main.js') assets = Environment() assets.register('js_login', js_login) diff --git a/powerdnsadmin/routes/__init__.py b/powerdnsadmin/routes/__init__.py index 598b17a..7d8aa9a 100644 --- a/powerdnsadmin/routes/__init__.py +++ b/powerdnsadmin/routes/__init__.py @@ -1,5 +1,5 @@ from .base import ( - captcha, csrf, login_manager, handle_bad_request, handle_unauthorized_access, + csrf, login_manager, handle_bad_request, handle_unauthorized_access, handle_access_forbidden, handle_page_not_found, handle_internal_server_error ) @@ -14,7 +14,6 @@ from .api import api_bp, apilist_bp def init_app(app): login_manager.init_app(app) csrf.init_app(app) - captcha.init_app(app) app.register_blueprint(index_bp) app.register_blueprint(user_bp) diff --git a/powerdnsadmin/routes/admin.py b/powerdnsadmin/routes/admin.py index 98f4ef9..e419fa8 100644 --- a/powerdnsadmin/routes/admin.py +++ b/powerdnsadmin/routes/admin.py @@ -795,7 +795,7 @@ class DetailedHistory(): if 'domain_type' in detail_dict and 'account_id' in detail_dict: # this is a domain creation self.detailed_msg = render_template_string(""" - +
Domain Type:{{ domaintype }}
Domain type:{{ domaintype }}
Account:{{ account }}
""", @@ -804,23 +804,22 @@ class DetailedHistory(): elif 'authenticator' in detail_dict: # this is a user authentication self.detailed_msg = render_template_string(""" - +
+ + + + + - - - - - - - - - + - - + +
+

User {{ username }} authentication {{ auth_result }}

+
Username:{{ username }}
Authentication Result:{{ auth_result }}
Authenticator Type:{{ authenticator }}{{ authenticator }}
IP Address:{{ ip_address }}IP Address{{ ip_address }}
diff --git a/powerdnsadmin/routes/base.py b/powerdnsadmin/routes/base.py index 7af342c..16ed00a 100644 --- a/powerdnsadmin/routes/base.py +++ b/powerdnsadmin/routes/base.py @@ -3,12 +3,10 @@ import base64 from flask import render_template, url_for, redirect, session, request, current_app from flask_login import LoginManager from flask_seasurf import SeaSurf -from flask_session_captcha import FlaskSessionCaptcha from ..models.user import User -captcha = FlaskSessionCaptcha() csrf = SeaSurf() login_manager = LoginManager() diff --git a/powerdnsadmin/routes/index.py b/powerdnsadmin/routes/index.py index 14ad275..117fe14 100644 --- a/powerdnsadmin/routes/index.py +++ b/powerdnsadmin/routes/index.py @@ -10,7 +10,7 @@ from yaml import Loader, load from flask import Blueprint, render_template, make_response, url_for, current_app, g, session, request, redirect, abort from flask_login import login_user, logout_user, login_required, current_user -from .base import captcha, csrf, login_manager +from .base import csrf, login_manager from ..lib import utils from ..decorators import dyndns_login_required from ..models.base import db @@ -400,7 +400,7 @@ def login(): desc_prop = Setting().get('oidc_oauth_account_description_property') account_to_add = [] - #If the name_property and desc_property exist in me (A variable that contains all the userinfo from the IdP). + #If the name_property and desc_property exist in me (A variable that contains all the userinfo from the IdP). if name_prop in me and desc_prop in me: accounts_name_prop = [me[name_prop]] if type(me[name_prop]) is not list else me[name_prop] accounts_desc_prop = [me[desc_prop]] if type(me[desc_prop]) is not list else me[desc_prop] @@ -415,7 +415,7 @@ def login(): account_to_add.append(account) user_accounts = user.get_accounts() - # Add accounts + # Add accounts for account in account_to_add: if account not in user_accounts: account.add_user(user) @@ -651,73 +651,50 @@ def logout(): @index_bp.route('/register', methods=['GET', 'POST']) def register(): - CAPTCHA_ENABLE = current_app.config.get('CAPTCHA_ENABLE') - if Setting().get('signup_enabled'): - if current_user.is_authenticated: - return redirect(url_for('index.index')) - if request.method == 'GET': - return render_template('register.html', captcha_enable=CAPTCHA_ENABLE) - elif request.method == 'POST': - username = request.form.get('username', '').strip() - password = request.form.get('password', '') - firstname = request.form.get('firstname', '').strip() - lastname = request.form.get('lastname', '').strip() - email = request.form.get('email', '').strip() - rpassword = request.form.get('rpassword', '') + if Setting().get('signup_enabled'): + if request.method == 'GET': + return render_template('register.html') + elif request.method == 'POST': + username = request.form.get('username', '').strip() + password = request.form.get('password', '') + firstname = request.form.get('firstname', '').strip() + lastname = request.form.get('lastname', '').strip() + email = request.form.get('email', '').strip() + rpassword = request.form.get('rpassword', '') - is_valid_email = re.compile(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$') + if not username or not password or not email: + return render_template( + 'register.html', error='Please input required information') - error_messages = {} - if not firstname: - error_messages['firstname'] = 'First Name is required' - if not lastname: - error_messages['lastname'] = 'Last Name is required' - if not username: - error_messages['username'] = 'Username is required' - if not password: - error_messages['password'] = 'Password is required' - if not rpassword: - error_messages['rpassword'] = 'Password confirmation is required' - if not email: - error_messages['email'] = 'Email is required' - if not is_valid_email.match(email): - error_messages['email'] = 'Invalid email address' - if password != rpassword: - error_messages['password'] = 'Password confirmation does not match' - error_messages['rpassword'] = 'Password confirmation does not match' + if password != rpassword: + return render_template( + 'register.html', + error="Password confirmation does not match") - if not captcha.validate(): - return render_template( - 'register.html', error='Invalid CAPTCHA answer', error_messages=error_messages, captcha_enable=CAPTCHA_ENABLE) + user = User(username=username, + plain_text_password=password, + firstname=firstname, + lastname=lastname, + email=email) - if error_messages: - return render_template('register.html', error_messages=error_messages, captcha_enable=CAPTCHA_ENABLE) - - user = User(username=username, - plain_text_password=password, - firstname=firstname, - lastname=lastname, - email=email - ) - - try: - result = user.create_local_user() - if result and result['status']: - if Setting().get('verify_user_email'): - send_account_verification(email) - if Setting().get('otp_force') and Setting().get('otp_field_enabled'): - user.update_profile(enable_otp=True) - prepare_welcome_user(user.id) - return redirect(url_for('index.welcome')) - else: - return redirect(url_for('index.login')) - else: - return render_template('register.html', - error=result['msg'], captcha_enable=CAPTCHA_ENABLE) - except Exception as e: - return render_template('register.html', error=e, captcha_enable=CAPTCHA_ENABLE) + try: + result = user.create_local_user() + if result and result['status']: + if Setting().get('verify_user_email'): + send_account_verification(email) + if Setting().get('otp_force') and Setting().get('otp_field_enabled'): + user.update_profile(enable_otp=True) + prepare_welcome_user(user.id) + return redirect(url_for('index.welcome')) + else: + return redirect(url_for('index.login')) + else: + return render_template('register.html', + error=result['msg']) + except Exception as e: + return render_template('register.html', error=e) else: - return render_template('errors/404.html'), 404 + return render_template('errors/404.html'), 404 # Show welcome page on first login if otp_force is enabled diff --git a/powerdnsadmin/static/custom/js/custom.js b/powerdnsadmin/static/custom/js/custom.js index 0f096bf..e4b72b1 100644 --- a/powerdnsadmin/static/custom/js/custom.js +++ b/powerdnsadmin/static/custom/js/custom.js @@ -287,18 +287,4 @@ function copy_otp_secret_to_clipboard() { navigator.clipboard.writeText(copyBox.value); $("#copy_tooltip").css("visibility", "visible"); setTimeout(function(){ $("#copy_tooltip").css("visibility", "collapse"); }, 2000); - } - -// Side menu nav bar active selection -/** add active class and stay opened when selected */ -var url = window.location; - -// for sidebar menu entirely but not cover treeview -$('ul.nav-sidebar a').filter(function() { - return this.href == url; -}).addClass('active'); - -// for treeview -$('ul.nav-treeview a').filter(function() { - return this.href == url; -}).parentsUntil(".nav-sidebar > .nav-treeview").addClass('menu-open').prev('a').addClass('active'); + } \ No newline at end of file diff --git a/powerdnsadmin/templates/admin_edit_account.html b/powerdnsadmin/templates/admin_edit_account.html index 046866c..ee8d1be 100644 --- a/powerdnsadmin/templates/admin_edit_account.html +++ b/powerdnsadmin/templates/admin_edit_account.html @@ -1,211 +1,192 @@ {% extends "base.html" %} - {% set active_page = "admin_accounts" %} - -{% block title %} - - Edit Account - {{ SITE_NAME }} - -{% endblock %} +{% block title %}Edit Account - {{ SITE_NAME }}{% endblock %} {% block dashboard_stat %} -
-
-
-
-

- {% if create %}Add Account{% else %}Edit Account{% endif %} - {% if create %}Account{% else %}{{ account.name }}{% endif %} -

-
-
- -
-
-
-
+ +
+

+ Account + {% if create %}New account{% else %}{{ account.name }}{% endif %} +

+ +
{% endblock %} {% block content %}
-
-
-
-
-

{% if create %}Add{% else %}Edit{% endif %} Account

-
-
- - -
- {% if error %} -
- -

Error!

- {{ error }} +
+
+
+

{% if create %}Add{% else %}Edit{% endif %} account

- {{ error }} - {% endif %} -
- - - - {% if invalid_accountname %} - Cannot be blank and must only contain alphanumeric - characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens or underscores{% endif %}. - - {% elif duplicate_accountname %} - Account name already in use. - {% endif %} -
-
- - - -
-
- - - -
-
- - - -
+ + + + + +
+ {% if error %} +
+ +

Error!

+ {{ error }} +
+ {{ error }} + {% endif %} +
+ + + + {% if invalid_accountname %} + Cannot be blank and must only contain alphanumeric + characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens or underscores{% endif %}. + {% elif duplicate_accountname %} + Account name already in use. + {% endif %} +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+

Access Control

+
+
+

Users on the right have access to manage records in all domains + associated with the account.

+

Click on users to move between columns.

+
+ +
+
+
+

Domains on the right are associated with the account. Red marked domain names are already associated with other accounts. + Moving already associated domains to this account will overwrite the previous associated account. +

+

Hover over the red domain names to show the associated account. Click on domains to move between columns.

+
+ +
+
+ +
-
-

Access Control

-
-
-

Users on the right have access to manage records in all domains - associated with the account. -

-

Click on users to move between columns.

-
- -
-
-
-

Domains on the right are associated with the account. Red marked domain names are already associated with other accounts. - Moving already associated domains to this account will overwrite the previous associated account. -

-

Hover over the red domain names to show the associated account. Click on domains to move between columns.

-
- -
-
- -
-
-
-
-
-

Help with creating a new account

-
-
-

- An account allows grouping of domains belonging to a particular entity, such as a customer or - department. -
- A domain can be assigned to an account upon domain creation or through the domain administration - page. -

-

Fill in all the fields to the in the form to the left.

-

- Name is an account identifier. It will be lowercased and can contain alphanumeric - characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens and underscores (no space or other special character is allowed) - {% else %} (no extra character is allowed){% endif %}.
- Description is a user friendly name for this account.
- Contact person is the name of a contact person at the account.
- Mail Address is an e-mail address for the contact person. -

-
+
+
+
+

Help with creating a new account

+
+
+

+ An account allows grouping of domains belonging to a particular entity, such as a customer or + department.
+ A domain can be assigned to an account upon domain creation or through the domain administration + page. +

+

Fill in all the fields to the in the form to the left.

+

+ Name is an account identifier. It will be lowercased and can contain alphanumeric + characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens and underscores (no space or other special character is allowed) + {% else %} (no extra character is allowed){% endif %}.
+ Description is a user friendly name for this account.
+ Contact person is the name of a contact person at the account.
+ Mail Address is an e-mail address for the contact person. +

+
+
-
-
{% endblock %} {% block extrascripts %} {% endblock %} diff --git a/powerdnsadmin/templates/admin_edit_key.html b/powerdnsadmin/templates/admin_edit_key.html index 23f58d3..6a94340 100644 --- a/powerdnsadmin/templates/admin_edit_key.html +++ b/powerdnsadmin/templates/admin_edit_key.html @@ -1,92 +1,80 @@ {% extends "base.html" %} - {% set active_page = "admin_keys" %} - {% if (key is not none and key.role.name != "User") %}{% set hide_opts = True %}{%else %}{% set hide_opts = False %}{% endif %} - {% block title %} - - Edit Key - {{ SITE_NAME }} - +Edit Key - {{ SITE_NAME }} {% endblock %} - {% block dashboard_stat %} -
-
-
-
-

- API Keys - {% if create %}Add API Key{% else %}Edit API Key - {{ key.id }}{% endif %} -

-
-
- -
-
-
-
+ +
+

+ Key + {% if create %}New key{% else %}{{ key.id }}{% endif %} +

+ +
{% endblock %} {% block content %}
-
-
-
-
-
-

{% if create %}Add{% else %}Edit{% endif %} Key

-
-
- - -
-
- - -
-
- - - -
-
- - - -
{% endblock %} - {% block extrascripts %} + + + {% endblock %} diff --git a/powerdnsadmin/templates/admin_history.html b/powerdnsadmin/templates/admin_history.html index f2a8619..aa16c4a 100644 --- a/powerdnsadmin/templates/admin_history.html +++ b/powerdnsadmin/templates/admin_history.html @@ -1,108 +1,81 @@ {% extends "base.html" %} - {% set active_page = "admin_history" %} - {% block title %} - - History - {{ SITE_NAME }} - -{% endblock %} - -{% block dashboard_stat %} -
-
-
-
-

- History - Recent Events -

-
-
- -
-
-
-
+History - {{ SITE_NAME }} +{% endblock %} {% block dashboard_stat %} + +
+

+ History Recent events +

+ +
{% endblock %} - {% block content %} {% import 'applied_change_macro.html' as applied_change_macro %} + + +
-
-
-
-
-

History Management

- {% if current_user.role.name != 'User' %} - - {% endif %} -
-
-
- -
-
-
-
- - - - -
- +
+
+
+

History Management

- - -
-Record Changelog only   - - - -
- -
-
- - -
- -
- -
-
- +
+ +
+ +
+ + + + +
+
{% endblock %} {% block extrascripts %} @@ -485,45 +471,49 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000; {% endblock %} {% block modals %} - - - + {% endblock %} {% block extrascripts %} @@ -135,21 +114,21 @@ {% endblock %} {% block modals %} -