Merge pull request #1392 from PowerDNS-Admin/1391-feature-mobile-first-responsive-ui-design

Issue 1391 - Feature: Mobile First Responsive UI Design
This commit is contained in:
Matt Scott 2023-02-20 15:52:22 -05:00 committed by GitHub
commit b01bf41bf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 8395 additions and 7256 deletions

View File

@ -74,9 +74,10 @@ def create_app(config=None):
app.jinja_env.filters['display_record_name'] = utils.display_record_name app.jinja_env.filters['display_record_name'] = utils.display_record_name
app.jinja_env.filters['display_master_name'] = utils.display_master_name app.jinja_env.filters['display_master_name'] = utils.display_master_name
app.jinja_env.filters['display_second_to_time'] = utils.display_time app.jinja_env.filters['display_second_to_time'] = utils.display_time
app.jinja_env.filters[ app.jinja_env.filters['display_setting_state'] = utils.display_setting_state
'display_setting_state'] = utils.display_setting_state
app.jinja_env.filters['pretty_domain_name'] = utils.pretty_domain_name app.jinja_env.filters['pretty_domain_name'] = utils.pretty_domain_name
app.jinja_env.filters['format_datetime_local'] = utils.format_datetime
app.jinja_env.filters['format_zone_type'] = utils.format_zone_type
# Register context proccessors # Register context proccessors
from .models.setting import Setting from .models.setting import Setting

View File

@ -132,6 +132,16 @@ def display_master_name(data):
return ", ".join(matches) return ", ".join(matches)
def format_zone_type(data):
"""Formats the given zone type for modern social standards."""
data = str(data).lower()
if data == 'master':
data = 'primary'
elif data == 'slave':
data = 'secondary'
return data.title()
def display_time(amount, units='s', remove_seconds=True): def display_time(amount, units='s', remove_seconds=True):
""" """
Convert timestamp to normal time format Convert timestamp to normal time format
@ -225,6 +235,7 @@ class customBoxes:
} }
order = ["reverse", "ip6arpa", "inaddrarpa"] order = ["reverse", "ip6arpa", "inaddrarpa"]
def pretty_domain_name(domain_name): def pretty_domain_name(domain_name):
# Add a debugging statement to print out the domain name # Add a debugging statement to print out the domain name
print("Received domain name:", domain_name) print("Received domain name:", domain_name)
@ -258,9 +269,16 @@ def to_idna(value, action):
elif action == 'decode': elif action == 'decode':
for split in splits: for split in splits:
if not split.startswith('_') and not split.startswith('--'): if not split.startswith('_') and not split.startswith('--'):
result.append(idna.decode(split)) result.append(idna.decode(split))
else: else:
result.append(split) result.append(split)
else: else:
raise Exception('No valid action received') raise Exception('No valid action received')
return '.'.join(result) return '.'.join(result)
def format_datetime(value, format_str="%Y-%m-%d %I:%M %p"):
"""Format a date time to (Default): YYYY-MM-DD HH:MM P"""
if value is None:
return ""
return value.strftime(format_str)

File diff suppressed because it is too large Load Diff

View File

@ -44,4 +44,16 @@ table td {
.search-input { .search-input {
width: 100%; width: 100%;
} }
.sidebar .image { padding-top: 0.7em; }
.sidebar .info { color: #fff; }
.sidebar .info p { margin: 0; }
.sidebar .info a { font-size: 0.8em; }
/* Global Styles */
table.records thead th, table.records tbody td { text-align: center; vertical-align: middle; }
table.records thead th:last-of-type { width: 50px; }
div.records > div.dataTables_wrapper > div.row:first-of-type { margin: 0 0.5em 0 0.5em; }
div.records > div.dataTables_wrapper > div.row:last-of-type { margin: 0.4em 0.5em 0.4em 0.5em; }
div.records > div.dataTables_wrapper table.dataTable { margin: 0 !important; }

View File

@ -93,8 +93,8 @@ function saveRow(oTable, nRow) {
oTable.cell(nRow,5).data(jqInputs[2].value); oTable.cell(nRow,5).data(jqInputs[2].value);
var record = jqInputs[0].value; var record = jqInputs[0].value;
var button_edit = "<button type=\"button\" class=\"btn btn-flat btn-warning button_edit\" id=\"" + record + "\">Edit&nbsp;<i class=\"fa fa-edit\"></i></button>" var button_edit = "<button type=\"button\" class=\"btn btn-warning button_edit\" id=\"" + record + "\">Edit&nbsp;<i class=\"fa fa-edit\"></i></button>"
var button_delete = "<button type=\"button\" class=\"btn btn-flat btn-danger button_delete\" id=\"" + record + "\">Delete&nbsp;<i class=\"fa fa-trash\"></i></button>" var button_delete = "<button type=\"button\" class=\"btn btn-danger button_delete\" id=\"" + record + "\">Delete&nbsp;<i class=\"fa fa-trash\"></i></button>"
oTable.cell(nRow,6).data(button_edit); oTable.cell(nRow,6).data(button_edit);
oTable.cell(nRow,7).data(button_delete); oTable.cell(nRow,7).data(button_delete);
@ -142,8 +142,8 @@ function editRow(oTable, nRow) {
jqTds[3].innerHTML = '<select class="form-control" id="record_ttl" name="record_ttl" value="' + aData[3] + '">' + ttl_opts + '</select>'; jqTds[3].innerHTML = '<select class="form-control" id="record_ttl" name="record_ttl" value="' + aData[3] + '">' + ttl_opts + '</select>';
jqTds[4].innerHTML = '<input type="text" style="display:table-cell; width:100% !important" id="current_edit_record_data" name="current_edit_record_data" class="form-control input-small advance-data" value="' + aData[4].replace(/\"/g,"&quot;") + '">'; jqTds[4].innerHTML = '<input type="text" style="display:table-cell; width:100% !important" id="current_edit_record_data" name="current_edit_record_data" class="form-control input-small advance-data" value="' + aData[4].replace(/\"/g,"&quot;") + '">';
jqTds[5].innerHTML = '<input type="text" style="display:table-cell; width:100% !important" id="record_comment" name="record_comment" class="form-control input-small advance-data" value="' + aData[5].replace(/\"/g, "&quot;") + '">'; jqTds[5].innerHTML = '<input type="text" style="display:table-cell; width:100% !important" id="record_comment" name="record_comment" class="form-control input-small advance-data" value="' + aData[5].replace(/\"/g, "&quot;") + '">';
jqTds[6].innerHTML = '<button type="button" class="btn btn-flat btn-primary button_save">Save</button>'; jqTds[6].innerHTML = '<button type="button" class="btn btn-primary button_save">Save</button>';
jqTds[7].innerHTML = '<button type="button" class="btn btn-flat btn-primary button_cancel">Cancel</button>'; jqTds[7].innerHTML = '<button type="button" class="btn btn-primary button_cancel">Cancel</button>';
// set current value of dropdown column // set current value of dropdown column
if (aData[2] == 'Active'){ if (aData[2] == 'Active'){
@ -192,12 +192,12 @@ function getdnssec(url, domain){
if (dnssec.length == 0 && parseFloat(PDNS_VERSION) >= 4.1) { if (dnssec.length == 0 && parseFloat(PDNS_VERSION) >= 4.1) {
dnssec_msg = '<h3>DNSSEC is disabled. Click on Enable to activate it.'; dnssec_msg = '<h3>DNSSEC is disabled. Click on Enable to activate it.';
modal.find('.modal-body p').html(dnssec_msg); modal.find('.modal-body p').html(dnssec_msg);
dnssec_footer = '<button type="button" class="btn btn-flat btn-success button_dnssec_enable pull-left" id="'+domain+'">Enable</button><button type="button" class="btn btn-flat btn-default pull-right" data-dismiss="modal">Cancel</button>'; dnssec_footer = '<button type="button" class="btn btn-success button_dnssec_enable pull-left" id="'+domain+'">Enable</button><button type="button" class="btn btn-default pull-right" data-dismiss="modal">Cancel</button>';
modal.find('.modal-footer ').html(dnssec_footer); modal.find('.modal-footer ').html(dnssec_footer);
} }
else { else {
if (parseFloat(PDNS_VERSION) >= 4.1) { if (parseFloat(PDNS_VERSION) >= 4.1) {
dnssec_footer = '<button type="button" class="btn btn-flat btn-danger button_dnssec_disable pull-left" id="'+domain+'">Disable DNSSEC</button><button type="button" class="btn btn-flat btn-default pull-right" data-dismiss="modal">Close</button>'; dnssec_footer = '<button type="button" class="btn btn-danger button_dnssec_disable pull-left" id="'+domain+'">Disable DNSSEC</button><button type="button" class="btn btn-default pull-right" data-dismiss="modal">Close</button>';
modal.find('.modal-footer ').html(dnssec_footer); modal.find('.modal-footer ').html(dnssec_footer);
} }
for (var i = 0; i < dnssec.length; i++) { for (var i = 0; i < dnssec.length; i++) {
@ -291,14 +291,13 @@ function copy_otp_secret_to_clipboard() {
// Side menu nav bar active selection // Side menu nav bar active selection
/** add active class and stay opened when selected */ /** add active class and stay opened when selected */
var url = window.location;
// for sidebar menu entirely but not cover treeview // for sidebar menu entirely but not cover treeview
$('ul.nav-sidebar a').filter(function() { $('ul.nav-sidebar a').filter(function() {
return this.href == url; return this.href == window.location.href.split('?')[0];
}).addClass('active'); }).addClass('active');
// for treeview // for treeview
$('ul.nav-treeview a').filter(function() { $('ul.nav-treeview a').filter(function() {
return this.href == url; return this.href == window.location.href.split('?')[0];
}).parentsUntil(".nav-sidebar > .nav-treeview").addClass('menu-open').prev('a').addClass('active'); }).parentsUntil(".nav-sidebar > .nav-treeview").addClass('menu-open').prev('a').addClass('active');

View File

@ -1,211 +1,247 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_accounts" %} {% set active_page = "admin_accounts" %}
{% if create %}
{% block title %} {% set action_label = 'Create' %}
<title> {% set form_action = url_for('admin.edit_account') %}
Edit Account - {{ SITE_NAME }} {% else %}
</title> {% set action_label = 'Edit' %}
{% endblock %} {% set form_action = url_for('admin.edit_account', account_name=account.name) %}
{% endif %}
{% block title %}<title>{{ action_label }} Account - {{ SITE_NAME }}</title>{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">{{ action_label }} Account</h1>
{% if create %}Add Account{% else %}Edit Account{% endif %} </div>
<small>{% if create %}Account{% else %}{{ account.name }}{% endif %}</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('admin.manage_account') }}">Accounts</a></li>
<li class="breadcrumb-item active">{{ action_label }} Account</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('admin.manage_account') }}">Accounts</a></li>
<li class="breadcrumb-item active">{% if create %}Add Account{% else %}Edit Account: {{ account.name }}{% endif %}</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-4"> <div class="col-12 col-sm-6 col-lg-4">
<div class="card"> <form role="form" method="post" action="{{ form_action }}">
<div class="card-header"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<h3 class="card-title">{% if create %}Add{% else %}Edit{% endif %} Account</h3> <input type="hidden" name="create" value="{{ create }}">
</div> <div class="card card-outline card-primary shadow">
<form role="form" method="post" action="{% if create %}{{ url_for('admin.edit_account') }}{% else %}{{ url_for('admin.edit_account', account_name=account.name) }}{% endif %}"> <div class="card-header">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <h3 class="card-title">Account Editor</h3>
<input type="hidden" name="create" value="{{ create }}"> </div>
<div class="card-body"> <!-- /.card-header -->
{% if error %} <div class="card-body">
<div class="alert alert-danger alert-dismissible"> {% if error %}
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <div class="alert alert-danger alert-dismissible">
<h4><i class="fa-solid fa-ban"></i> Error!</h4> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">
{{ error }} &times;
</div> </button>
<span class="help-block">{{ error }}</span> <h4><i class="fa-solid fa-ban"></i> Error!</h4>
{% endif %} {{ error }}
<div class="form-group has-feedback {% if invalid_accountname or duplicate_accountname %}has-error{% endif %}"> </div>
<label class="control-label" for="accountname">Name</label> <span class="help-block">{{ error }}</span>
<input type="text" class="form-control" placeholder="Account Name (required)" {% endif %}
name="accountname" {% if account %}value="{{ account.name }}" {% endif %} <div
{% if not create %}disabled{% endif %} required> class="form-group has-feedback {% if invalid_accountname or duplicate_accountname %}has-error{% endif %}">
<span class="form-control-feedback"></span> <label class="control-label" for="accountname">Name</label>
{% if invalid_accountname %} <input type="text" class="form-control" placeholder="Account Name (required)"
<span class="help-block text-danger">Cannot be blank and must only contain alphanumeric name="accountname" id="accountname"
{% if account %}value="{{ account.name }}" {% endif %}
{% if not create %}disabled{% endif %} required>
<span class="form-control-feedback"></span>
{% if invalid_accountname %}
<span class="help-block text-danger">Cannot be blank and must only contain alphanumeric
characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens or underscores{% endif %}. characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens or underscores{% endif %}.
</span> </span>
{% elif duplicate_accountname %} {% elif duplicate_accountname %}
<span class="help-block text-danger">Account name already in use.</span> <span class="help-block text-danger">Account name already in use.</span>
{% endif %} {% endif %}
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label class="control-label" for="accountdescription">Description</label> <label class="control-label" for="accountdescription">Description</label>
<input type="text" class="form-control" placeholder="Account Description (optional)" <input type="text" class="form-control" placeholder="Account Description (optional)"
name="accountdescription" {% if account %}value="{{ account.description }}" {% endif %}> name="accountdescription" id="accountdescription"
<span class="orm-control-feedback"></span> {% if account %}value="{{ account.description }}" {% endif %}>
</div> <span class="orm-control-feedback"></span>
<div class="form-group has-feedback"> </div>
<label class="control-label" for="accountcontact">Contact Person</label> <div class="form-group has-feedback">
<input type="text" class="form-control" placeholder="Contact Person (optional)" <label class="control-label" for="accountcontact">Contact Person</label>
name="accountcontact" {% if account %}value="{{ account.contact }}" {% endif %}> <input type="text" class="form-control" placeholder="Contact Person (optional)"
<span class="form-control-feedback"></span> name="accountcontact" id="accountcontact"
</div> {% if account %}value="{{ account.contact }}" {% endif %}>
<div class="form-group has-feedback"> <span class="form-control-feedback"></span>
<label class="control-label" for="accountmail">Mail Address</label> </div>
<input type="email" class="form-control" placeholder="Mail Address (optional)" <div class="form-group has-feedback">
name="accountmail" {% if account %}value="{{ account.mail }}" {% endif %}> <label class="control-label" for="accountmail">Mail Address</label>
<span class="form-control-feedback"></span> <input type="email" class="form-control" placeholder="Mail Address (optional)"
</div> name="accountmail" id="accountmail"
{% if account %}value="{{ account.mail }}" {% endif %}>
<span class="form-control-feedback"></span>
</div>
</div>
<!-- /.card-body -->
<div class="card-header">
<h3 class="card-title">Access Control</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<p>Users on the right have access to manage records in all domains
associated with the account.
</p>
<p>Click on users to move between columns.</p>
<div class="form-group col-2">
<select multiple="multiple" class="form-control" id="account_multi_user"
name="account_multi_user">
{% for user in users %}
<option {% if user.id in account_user_ids|default([]) %}selected{% endif %}
value="{{ user.username }}">{{ user.username }}
</option>
{% endfor %}
</select>
</div>
</div>
<!-- /.card-body -->
<div class="card-body">
<p>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.
</p>
<p>Hover over the red domain names to show the associated account. Click on domains to
move between columns.</p>
<div class="form-group col-2">
<select multiple="multiple" class="form-control" id="account_domains"
name="account_domains">
{% for domain in domains %}
{% if account != None and domain.account_id != None and account.id != domain.account_id %}
{% with account_id=domain.account_id %}
<option style="color: red" data-toggle="tooltip"
title="Associated with: {{ accounts[account_id].name }}"
value="{{ domain.name }}">
{{ domain.name }}
</option>
{% endwith %}
{% else %}
<option {% if account.id == domain.account_id %}selected{% endif %}
value="{{ domain.name }}">
{{ domain.name }}
</option>
{% endif %}
{% endfor %}
</select>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="button" class="btn btn-secondary float-left" title="Cancel"
onclick="window.location.href='{{ url_for('admin.manage_account') }}'">
Cancel
</button>
<button type="submit" class="btn btn-primary float-right"
title="{{ action_label }} Account">
{{ action_label }} Account
</button>
</div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form>
</div>
<!-- /.col -->
<div class="col-12 col-sm-6 col-lg-8">
<div class="card card-outline card-secondary shadow">
<div class="card-header">
<h3 class="card-title">Account Editor Help</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<p>
An account allows grouping of domains belonging to a particular entity, such as a
customer or
department.
</p>
<p>
A domain can be assigned to an account upon domain creation or through the domain
administration
page.
</p>
<p>Fill in all the fields to the in the form to the left.</p>
<p>
<strong>Name</strong> is an account identifier. It will be lowercase 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 %}.<br/>
<strong>Description</strong> is a user-friendly name for this account.<br/>
<strong>Contact person</strong> is the name of a contact person at the account.<br/>
<strong>Mail Address</strong> is an e-mail address for the contact person.
</p>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div> </div>
<div class="card-header"> <!-- /.row -->
<h3 class="card-title">Access Control</h3>
</div>
<div class="card-body">
<p>Users on the right have access to manage records in all domains
associated with the account.
</p>
<p>Click on users to move between columns.</p>
<div class="form-group col-2">
<select multiple="multiple" class="form-control" id="account_multi_user"
name="account_multi_user">
{% for user in users %}
<option {% if user.id in account_user_ids|default([]) %}selected{% endif %}
value="{{ user.username }}">{{ user.username }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="card-body">
<p>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.
</p>
<p>Hover over the red domain names to show the associated account. Click on domains to move between columns.</p>
<div class="form-group col-2">
<select multiple="multiple" class="form-control" id="account_domains" name="account_domains">
{% for domain in domains %}
{% if account != None and domain.account_id != None and account.id != domain.account_id %}
{% with account_id=domain.account_id %}
<option style="color: red" data-toggle="tooltip" title="Associated with: {{ accounts[account_id].name }}" value="{{ domain.name }}">
{{ domain.name }}
</option>
{% endwith %}
{% else %}
<option {% if account.id == domain.account_id %}selected{% endif %} value="{{ domain.name }}">
{{ domain.name }}
</option>
{% endif %}
{% endfor %}
</select>
</div>
</div>
<div class="card-footer">
<button type="button" class="btn btn-secondary float-left" onclick="window.location.href='{{ url_for('admin.manage_account') }}'">
Cancel
</button>
<button type="submit" class="btn btn-primary float-right">
{% if create %}Create{% else %}Update{% endif %} Account
</button>
</div>
</form>
</div> </div>
</div> <!-- /.container-fluid -->
<div class="col-8"> </section>
<div class="card">
<div class="card-header">
<h3 class="card-title">Help with creating a new account</h3>
</div>
<div class="card-body">
<p>
An account allows grouping of domains belonging to a particular entity, such as a customer or
department.
<br />
A domain can be assigned to an account upon domain creation or through the domain administration
page.
</p>
<p>Fill in all the fields to the in the form to the left.</p>
<p>
<strong>Name</strong> 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 %}.<br />
<strong>Description</strong> is a user friendly name for this account.<br />
<strong>Contact person</strong> is the name of a contact person at the account.<br />
<strong>Mail Address</strong> is an e-mail address for the contact person.
</p>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
function addMultiSelect(id, placeholder) { function addMultiSelect(id, placeholder) {
$(id).multiSelect({ $(id).multiSelect({
selectableHeader: `<input type='text' class='search-input' autocomplete='off' placeholder='${placeholder}'>`, selectableHeader: `<input type='text' class='search-input' autocomplete='off' placeholder='${placeholder}'>`,
selectionHeader: `<input type='text' class='search-input' autocomplete='off' placeholder='${placeholder}'>`, selectionHeader: `<input type='text' class='search-input' autocomplete='off' placeholder='${placeholder}'>`,
afterInit: function (ms) { afterInit: function (ms) {
var that = this, var that = this,
$selectableSearch = that.$selectableUl.prev(), $selectableSearch = that.$selectableUl.prev(),
$selectionSearch = that.$selectionUl.prev(), $selectionSearch = that.$selectionUl.prev(),
selectableSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selectable:not(.ms-selected)', selectableSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selectable:not(.ms-selected)',
selectionSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selection.ms-selected'; selectionSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selection.ms-selected';
that.qs1 = $selectableSearch.quicksearch(selectableSearchString) that.qs1 = $selectableSearch.quicksearch(selectableSearchString)
.on('keydown', function (e) { .on('keydown', function (e) {
if (e.which === 40) { if (e.which === 40) {
that.$selectableUl.focus(); that.$selectableUl.focus();
return false; return false;
} }
}); });
that.qs2 = $selectionSearch.quicksearch(selectionSearchString) that.qs2 = $selectionSearch.quicksearch(selectionSearchString)
.on('keydown', function (e) { .on('keydown', function (e) {
if (e.which == 40) { if (e.which == 40) {
that.$selectionUl.focus(); that.$selectionUl.focus();
return false; return false;
} }
}); });
}, },
afterSelect: function () { afterSelect: function () {
this.qs1.cache(); this.qs1.cache();
this.qs2.cache(); this.qs2.cache();
}, },
afterDeselect: function () { afterDeselect: function () {
this.qs1.cache(); this.qs1.cache();
this.qs2.cache(); this.qs2.cache();
} }
}); });
} }
addMultiSelect("#account_multi_user", "Username")
addMultiSelect("#account_domains", "Domain") addMultiSelect("#account_multi_user", "Username")
</script> addMultiSelect("#account_domains", "Domain")
</script>
{% endblock %} {% endblock %}

View File

@ -1,303 +1,338 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_keys" %} {% set active_page = "admin_keys" %}
{% if create %}
{% if (key is not none and key.role.name != "User") %}{% set hide_opts = True %}{%else %}{% set hide_opts = False %}{% endif %} {% set action_label = 'Create' %}
{% set form_action = url_for('admin.edit_key') %}
{% block title %} {% else %}
<title> {% set action_label = 'Edit' %}
Edit Key - {{ SITE_NAME }} {% set form_action = url_for('admin.edit_key', key_id=key.id) %}
</title> {% endif %}
{% endblock %} {% if (key is not none and key.role.name != "User") %}{% set hide_opts = True %}{% else %}
{% set hide_opts = False %}{% endif %}
{% block title %}<title>{{ action_label }} API Key - {{ SITE_NAME }}</title>{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">{{ action_label }} API Key</h1>
API Keys </div>
<small>{% if create %}Add API Key{% else %}Edit API Key - {{ key.id }}{% endif %}</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('admin.manage_keys') }}">API Keys</a></li>
<li class="breadcrumb-item active">{{ action_label }} API Key</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('admin.manage_keys') }}">API Keys</a></li>
<li class="breadcrumb-item active">{% if create %}Add API Key{% else %}Edit API Key - {% endif %}{{ key.id }}</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-4"> <div class="col-12 col-sm-6 col-lg-4">
<div class="card "> <form role="form" method="post" action="{{ form_action }}">
<div class="card-header"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<h3 class="card-title">{% if create %}Add{% else %}Edit{% endif %} Key</h3> <input type="hidden" name="create" value="{{ create }}">
</div> <div class="card card-outline card-primary shadow">
<form role="form" method="post" <div class="card-header">
action="{% if create %}{{ url_for('admin.edit_key') }}{% else %}{{ url_for('admin.edit_key', key_id=key.id) }}{% endif %}"> <h3 class="card-title">API Key Editor</h3>
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> </div>
<input type="hidden" name="create" value="{{ create }}"> <!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label class="control-label" for="role">Role</label> <label class="control-label" for="role">Role</label>
<select class="key_role form-control" id="key_role" name="key_role"> <select class="key_role form-control" id="key_role" name="key_role">
{% for role in roles %} {% for role in roles %}
<option value="{{ role.name }}" <option value="{{ role.name }}"
{% if (key is not none) and (role.id==key.role.id) %}selected{% endif %} {% if (key is not none) and (role.id==key.role.id) %}selected{% endif %}
{% if (key is none) and (role.name=="User") %}selected{% endif %}> {% if (key is none) and (role.name=="User") %}selected{% endif %}>
{{ role.name }} {{ role.name }}
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<div class="form-group has-feedback"> <!-- /.form-group -->
<label class="control-label" for="description">Description</label> <div class="form-group has-feedback">
<input type="text" class="form-control" placeholder="Description" name="description" <label class="control-label" for="description">Description</label>
{% if key is not none %} value="{{ key.description }}" {% endif %}> <input type="text" class="form-control" placeholder="Description" name="description"
<span class="glyphicon glyphicon-pencil form-control-feedback"></span> {% if key is not none %} value="{{ key.description }}" {% endif %}>
</div> <span class="glyphicon glyphicon-pencil form-control-feedback"></span>
</div> </div>
<div class="card-header key-opts"{% if hide_opts %} style="display: none;"{% endif %}> <!-- /.form-group -->
<h3 class="card-title">Accounts Access Control</h3> </div>
</div> <!-- /.card-body -->
<div class="card-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}> <div class="card-header key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
<p>This key will be linked to the accounts on the right,</p> <h3 class="card-title">Accounts Access Control</h3>
<p>thus granting access to domains owned by the selected accounts.</p> </div>
<p>Click on accounts to move between the columns.</p> <!-- /.card-header -->
<div class="form-group col-2"> <div class="card-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
<select multiple="multiple" class="form-control" id="key_multi_account" name="key_multi_account"> <p>This key will be linked to the accounts on the right,</p>
{% for account in accounts %} <p>thus granting access to domains owned by the selected accounts.</p>
<option {% if key and account in key.accounts %}selected{% endif %} value="{{ account.name }}">{{ account.name }}</option> <p>Click on accounts to move between the columns.</p>
{% endfor %} <div class="form-group col-2">
</select> <select multiple="multiple" class="form-control" id="key_multi_account"
</div> name="key_multi_account">
</div> {% for account in accounts %}
<div class="card-header key-opts"{% if hide_opts %} style="display: none;"{% endif %}> <option {% if key and account in key.accounts %}selected{% endif %}
<h3 class="card-title">Domain Access Control</h3> value="{{ account.name }}">{{ account.name }}</option>
</div> {% endfor %}
<div class="card-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}> </select>
<p>This key will have acess to the domains on the right.</p> </div>
<p>Click on domains to move between the columns.</p> <!-- /.form-group -->
<div class="form-group col-2"> </div>
<select multiple="multiple" class="form-control" id="key_multi_domain" <!-- /.card-body -->
name="key_multi_domain"> <div class="card-header key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
{% for domain in domains %} <h3 class="card-title">Domain Access Control</h3>
<option {% if key and domain in key.domains %}selected{% endif %} value="{{ domain.name }}">{{ domain.name }}</option> </div>
{% endfor %} <!-- /.card-header -->
</select> <div class="card-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
<p>This key will have access to the domains on the right.</p>
<p>Click on domains to move between the columns.</p>
<div class="form-group col-2">
<select multiple="multiple" class="form-control" id="key_multi_domain"
name="key_multi_domain">
{% for domain in domains %}
<option {% if key and domain in key.domains %}selected{% endif %}
value="{{ domain.name }}">{{ domain.name }}</option>
{% endfor %}
</select>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="button" class="btn btn-secondary float-left" title="Cancel"
onclick="window.location.href='{{ url_for('admin.manage_keys') }}'">
<i class="fa-solid fa-window-close"></i>&nbsp;Cancel
</button>
<button type="submit" class="btn btn-primary float-right"
title="{{ action_label }} API Key" id="key_submit">
<i class="fa-solid fa-save"></i>&nbsp;{{ action_label }} API Key
</button>
</div>
<!-- /.card-footer -->
</div> </div>
</div> <!-- /.card -->
<div class="card-footer"> </form>
<button type="button" class="btn btn-secondary float-left" onclick="window.location.href='{{ url_for('admin.manage_keys') }}'">
<i class="fa-solid fa-window-close"></i>&nbsp;Cancel
</button>
<button type="submit" class="btn btn-primary float-right" id="key_submit">
<i class="fa-solid fa-save"></i>&nbsp;{% if create %}Create{% else %}Update{% endif %} Key
</button>
</div>
</form>
</div>
</div>
<div class="col-8">
<div class="card">
<div class="card-header">
<h3 class="card-title">Help with {% if create %}creating a new{% else%}updating a{% endif %} key
</h3>
</div> </div>
<div class="card-body"> <!-- /.col -->
<p>Fill in all the fields in the form to the left.</p>
<p><strong>Role</strong> The role of the key.</p> <div class="col-12 col-sm-6 col-lg-8">
<p><strong>Description</strong> The key description.</p> <div class="card card-outline card-secondary shadow">
<p><strong>Access Control</strong> The domains or accounts which the key has access to.</p> <div class="card-header">
<h3 class="card-title">API Key Editor Help</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<p>Fill in all the fields in the form to the left.</p>
<p><strong>Role</strong> The role of the key.</p>
<p><strong>Description</strong> The key description.</p>
<p><strong>Access Control</strong> The domains or accounts which the key has access to.</p>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</div> <!-- /.container-fluid -->
</div> </section>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
$('form').submit(function (e) { $('form').submit(function (e) {
var selectedRole = $("#key_role").val(); var selectedRole = $("#key_role").val();
var selectedDomains = $("#key_multi_domain option:selected").length; var selectedDomains = $("#key_multi_domain option:selected").length;
var selectedAccounts = $("#key_multi_account option:selected").length; var selectedAccounts = $("#key_multi_account option:selected").length;
var warn_modal = $("#modal_warning"); var warn_modal = $("#modal_warning");
if (selectedRole != "User" && selectedDomains > 0 && selectedAccounts > 0){ if (selectedRole != "User" && selectedDomains > 0 && selectedAccounts > 0) {
var warning = "Administrator and Operators have access to all domains. Your domain an account selection won't be saved."; var warning = "Administrator and Operators have access to all domains. Your domain an account selection won't be saved.";
e.preventDefault(e); e.preventDefault(e);
warn_modal.modal('show'); warn_modal.modal('show');
}
if (selectedRole == "User" && selectedDomains == 0 && selectedAccounts == 0) {
var warning = "User role must have at least one account or one domain bound. None selected.";
e.preventDefault(e);
warn_modal.modal('show');
}
warn_modal.find('.modal-body p').text(warning);
warn_modal.find('#button_key_confirm_warn').click(clearModal);
});
function clearModal() {
$("#modal_warning").modal('hide');
} }
if (selectedRole == "User" && selectedDomains == 0 && selectedAccounts == 0){ $('#key_role').on('change', function (e) {
var warning = "User role must have at least one account or one domain bound. None selected."; var optionSelected = $("option:selected", this);
e.preventDefault(e); if (this.value != "User") {
warn_modal.modal('show'); // Clear the visible list
} $('#ms-key_multi_domain .ms-selection li').each(function () {
$(this).css('display', 'none');
})
$('#ms-key_multi_domain .ms-selectable li').each(function () {
$(this).css('display', '');
})
$('#ms-key_multi_account .ms-selection li').each(function () {
$(this).css('display', 'none');
})
$('#ms-key_multi_account .ms-selectable li').each(function () {
$(this).css('display', '');
})
// Deselect invisible selectbox
$('#key_multi_domain option:selected').each(function () {
$(this).prop('selected', false);
})
$('#key_multi_account option:selected').each(function () {
$(this).prop('selected', false);
})
// Hide the lists
$(".key-opts").hide();
} else {
$(".key-opts").show();
}
});
$("#key_multi_domain").multiSelect({
selectableHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Domain Name'>",
selectionHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Domain Name'>",
afterInit: function (ms) {
var that = this,
$selectableSearch = that.$selectableUl.prev(),
$selectionSearch = that.$selectionUl.prev(),
selectableSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selectable:not(.ms-selected)',
selectionSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selection.ms-selected';
warn_modal.find('.modal-body p').text(warning); that.qs1 = $selectableSearch.quicksearch(selectableSearchString)
warn_modal.find('#button_key_confirm_warn').click(clearModal); .on('keydown', function (e) {
}); if (e.which === 40) {
function clearModal(){ that.$selectableUl.focus();
$("#modal_warning").modal('hide'); return false;
} }
$('#key_role').on('change', function (e) { });
var optionSelected = $("option:selected", this);
if (this.value != "User") {
// Clear the visible list
$('#ms-key_multi_domain .ms-selection li').each(function(){ $(this).css('display', 'none');})
$('#ms-key_multi_domain .ms-selectable li').each(function(){ $(this).css('display', '');})
$('#ms-key_multi_account .ms-selection li').each(function(){ $(this).css('display', 'none');})
$('#ms-key_multi_account .ms-selectable li').each(function(){ $(this).css('display', '');})
// Deselect invisible selectbox
$('#key_multi_domain option:selected').each(function(){ $(this).prop('selected', false);})
$('#key_multi_account option:selected').each(function(){ $(this).prop('selected', false);})
// Hide the lists
$(".key-opts").hide();
}
else {
$(".key-opts").show();
}
});
$("#key_multi_domain").multiSelect({
selectableHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Domain Name'>",
selectionHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Domain Name'>",
afterInit: function (ms) {
var that = this,
$selectableSearch = that.$selectableUl.prev(),
$selectionSearch = that.$selectionUl.prev(),
selectableSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selectable:not(.ms-selected)',
selectionSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selection.ms-selected';
that.qs1 = $selectableSearch.quicksearch(selectableSearchString) that.qs2 = $selectionSearch.quicksearch(selectionSearchString)
.on('keydown', function (e) { .on('keydown', function (e) {
if (e.which === 40) { if (e.which == 40) {
that.$selectableUl.focus(); that.$selectionUl.focus();
return false; return false;
} }
}); });
},
afterSelect: function () {
this.qs1.cache();
this.qs2.cache();
},
afterDeselect: function () {
this.qs1.cache();
this.qs2.cache();
}
});
$("#key_multi_account").multiSelect({
selectableHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Account Name'>",
selectionHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Account Name'>",
afterInit: function (ms) {
var that = this,
$selectableSearch = that.$selectableUl.prev(),
$selectionSearch = that.$selectionUl.prev(),
selectableSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selectable:not(.ms-selected)',
selectionSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selection.ms-selected';
that.qs2 = $selectionSearch.quicksearch(selectionSearchString) that.qs1 = $selectableSearch.quicksearch(selectableSearchString)
.on('keydown', function (e) { .on('keydown', function (e) {
if (e.which == 40) { if (e.which === 40) {
that.$selectionUl.focus(); that.$selectableUl.focus();
return false; return false;
} }
}); });
},
afterSelect: function () {
this.qs1.cache();
this.qs2.cache();
},
afterDeselect: function () {
this.qs1.cache();
this.qs2.cache();
}
});
$("#key_multi_account").multiSelect({
selectableHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Account Name'>",
selectionHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Account Name'>",
afterInit: function (ms) {
var that = this,
$selectableSearch = that.$selectableUl.prev(),
$selectionSearch = that.$selectionUl.prev(),
selectableSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selectable:not(.ms-selected)',
selectionSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selection.ms-selected';
that.qs1 = $selectableSearch.quicksearch(selectableSearchString) that.qs2 = $selectionSearch.quicksearch(selectionSearchString)
.on('keydown', function (e) { .on('keydown', function (e) {
if (e.which === 40) { if (e.which == 40) {
that.$selectableUl.focus(); that.$selectionUl.focus();
return false; return false;
} }
}); });
},
afterSelect: function () {
this.qs1.cache();
this.qs2.cache();
},
afterDeselect: function () {
this.qs1.cache();
this.qs2.cache();
}
});
{% if plain_key %}
$(document.body).ready(function () {
var modal = $("#modal_show_key");
var info = "Please copy this key to a secure location. You will be unable to view it again once you close this window: {{ plain_key }}";
modal.find('.modal-body p').text(info);
modal.find('#button_key_confirm').click(redirect_modal);
modal.find('#button_close_modal').click(redirect_modal);
modal.modal('show');
});
that.qs2 = $selectionSearch.quicksearch(selectionSearchString) function redirect_modal() {
.on('keydown', function (e) { window.location.href = '{{ url_for('admin.manage_keys') }}';
if (e.which == 40) { modal.modal('hide');
that.$selectionUl.focus(); }
return false; {% endif %}
} </script>
});
},
afterSelect: function () {
this.qs1.cache();
this.qs2.cache();
},
afterDeselect: function () {
this.qs1.cache();
this.qs2.cache();
}
});
{% if plain_key %}
$(document.body).ready(function () {
var modal = $("#modal_show_key");
var info = "Please copy this key to a secure location. You will be unable to view it again once you close this window: {{ plain_key }}";
modal.find('.modal-body p').text(info);
modal.find('#button_key_confirm').click(redirect_modal);
modal.find('#button_close_modal').click(redirect_modal);
modal.modal('show');
});
function redirect_modal() {
window.location.href = '{{ url_for('admin.manage_keys') }}';
modal.modal('hide');
}
{% endif %}
</script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<div class="modal fade" id="modal_show_key"> <div class="modal fade" id="modal_show_key">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">New API Key</h5> <h5 class="modal-title">New API Key</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_modal"> <button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_modal">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p></p> <p></p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary" id="button_key_confirm"> <button type="button" class="btn btn-primary" id="button_key_confirm">
Confirm Confirm
</button> </button>
</div> </div>
</div>
</div>
</div> </div>
</div> <div class="modal fade" id="modal_warning">
</div> <div class="modal-dialog">
<div class="modal fade" id="modal_warning"> <div class="modal-content">
<div class="modal-dialog"> <div class="modal-header">
<div class="modal-content"> <h5 class="modal-title">WARNING</h5>
<div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"
<h5 class="modal-title">WARNING</h5> id="button_close_warn_modal">
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_warn_modal"> <span aria-hidden="true">&times;</span>
<span aria-hidden="true">&times;</span> </button>
</button> </div>
</div> <div class="modal-body">
<div class="modal-body"> <p></p>
<p></p> </div>
</div> <div class="modal-footer">
<div class="modal-footer"> <button type="button" class="btn btn-secondary close" data-dismiss="modal" id="button_close_modal">
<button type="button" class="btn btn-secondary close" data-dismiss="modal" id="button_close_modal"> Close
Close </button>
</button> <button type="button" class="btn btn-primary" id="button_key_confirm_warn">
<button type="button" class="btn btn-primary" id="button_key_confirm_warn"> OK
OK </button>
</button> </div>
</div> </div>
</div>
</div> </div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,190 +1,205 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_users" %} {% set active_page = "admin_users" %}
{% if create %}
{% block title %} {% set action_label = 'Create' %}
<title> {% set form_action = url_for('admin.edit_user') %}
{% if create %}Add user{% else %}Edit user "{{ user.username }}"{% endif %} - {{ SITE_NAME }} {% else %}
</title> {% set action_label = 'Edit' %}
{% endblock %} {% set form_action = url_for('admin.edit_user', user_username=user.username) %}
{% endif %}
{% block title %}<title>{{ action_label }} User - {{ SITE_NAME }}</title>{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">{{ action_label }} User</h1>
Users </div>
<small>{% if create %}New user{% else %}Edit user {{ user.username }}{% endif %}</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('admin.manage_user') }}">Users</a></li>
<li class="breadcrumb-item active">{{ action_label }} User</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('admin.manage_user') }}">Users</a></li>
<li class="breadcrumb-item active">{% if create %}Add user{% else %}Edit user "{{ user.username }}"{% endif %}</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-4"> <div class="col-12 col-sm-6 col-lg-4">
<div class="card card-secondary"> <form role="form" method="post" action="{{ form_action }}">
<div class="card-header with-border"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<h3 class="card-title">{% if create %}Add{% else %}Edit{% endif %} user</h3> <input type="hidden" name="create" value="{{ create }}">
</div> <div class="card card-outline card-primary shadow">
<form role="form" method="post" <div class="card-header with-border">
action="{% if create %}{{ url_for('admin.edit_user') }}{% else %}{{ url_for('admin.edit_user', user_username=user.username) }}{% endif %}"> <h3 class="card-title">User Editor</h3>
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> </div>
<input type="hidden" name="create" value="{{ create }}"> <!-- /.card-header -->
<div class="card-body"> <div class="card-body">
{% if error %} {% if error %}
<div class="alert alert-danger alert-dismissible"> <div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">
<h4><i class="fa-solid fa-ban"></i> Error!</h4> &times;
{{ error }} </button>
<h4><i class="fa-solid fa-ban"></i> Error!</h4>
{{ error }}
</div>
<span class="help-block">{{ error }}</span>
{% endif %}
<div class="form-group has-feedback">
<label class="control-label" for="firstname">First Name</label>
<input type="text" class="form-control" placeholder="First Name" name="firstname"
id="firstname"
{% if user %}value="{{ user.firstname }}" {% endif %}>
<span class="form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="lastname">Last Name</label>
<input type="text" class="form-control" placeholder="Last name" name="lastname"
id="lastname"
{% if user %}value="{{ user.lastname }}" {% endif %}>
<span class="form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="email">E-mail address</label>
<input type="email" class="form-control" placeholder="Email" name="email" id="email"
{% if user %}value="{{ user.email }}" {% endif %}>
<span class="form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="username">Username</label>
<input type="text" class="form-control" placeholder="Username" name="username"
id="username"
{% if user %}value="{{ user.username }}" {% endif %}
{% if not create %}disabled{% endif %}>
<span class="form-control-feedback"></span>
</div>
<div class="form-group has-feedback {% if blank_password %}has-error{% endif %}">
<label class="control-label" for="password">Password</label>
<input type="password" class="form-control"
placeholder="Password {% if create %}(Required){% else %}(Leave blank to keep unchanged){% endif %}"
name="password" id="password">
<span class="form-control-feedback"></span>
{% if blank_password %}
<span class="help-block">The password cannot be blank.</span>
{% endif %}
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="submit" class="btn btn-primary" title="{{ action_label }} User">
{{ action_label }} User
</button>
</div>
<!-- /.card-footer -->
</div>
</form>
{% if not create %}
{% if user.otp_secret %}
<div class="card">
<div class="card-header">
<h3 class="card-title">Two Factor Authentication</h3>
</div>
<div class="card-body">
<p>If two-factor authentication is configured for this user and is causing problems
due to a lost device or
technical issue, it can be disabled here.
</p>
<p>The user will need to reconfigure two-factor authentication, to re-enable it.</p>
<p><strong>Beware: This could compromise security!</strong></p>
</div>
<div class="card-footer">
<button type="button" class="btn btn-warning button_otp_disable"
id="{{ user.username }}">
Disable Two Factor Authentication
</button>
</div>
</div>
{% endif %}
{% endif %}
</div> </div>
<span class="help-block">{{ error }}</span> <!-- /.col -->
{% endif %}
<div class="form-group has-feedback"> <div class="col-12 col-sm-6 col-lg-8">
<label class="control-label" for="firstname">First Name</label> <div class="card card-outline card-secondary shadow">
<input type="text" class="form-control" placeholder="First Name" name="firstname" <div class="card-header">
{% if user %}value="{{ user.firstname }}" {% endif %}> <h3 class="card-title">User Editor Help</h3>
<span class="form-control-feedback"></span> </div>
</div> <!-- /.card-header -->
<div class="form-group has-feedback"> <div class="card-body">
<label class="control-label" for="lastname">Last Name</label> <p>Fill in all the fields to the in the form to the left.</p>
<input type="text" class="form-control" placeholder="Last name" name="lastname" {% if create %}
{% if user %}value="{{ user.lastname }}" {% endif %}> <p><strong>Newly created users do not have access to any domains.</strong> You will need
<span class="form-control-feedback"></span> to grant
</div> access to the user once it is created via the domain management buttons on the
<div class="form-group has-feedback"> dashboard.
<label class="control-label" for="email">E-mail address</label> </p>
<input type="email" class="form-control" placeholder="Email" name="email" id="email" {% else %}
{% if user %}value="{{ user.email }}" {% endif %}> <p><strong>Username</strong> cannot be changed.</p>
<span class="form-control-feedback"></span> <p><strong>Password</strong> can be left empty to keep the current password.</p>
</div> {% endif %}
<p class="login-box-msg">Enter the account details below</p> </div>
<div class="form-group has-feedback"> <!-- /.card-body -->
<label class="control-label" for="username">Username</label> </div>
<input type="text" class="form-control" placeholder="Username" name="username" <!-- /.card -->
{% if user %}value="{{ user.username }}" {% endif %} </div>
{% if not create %}disabled{% endif %}> <!-- /.col -->
<span class="form-control-feedback"></span>
</div>
<div class="form-group has-feedback {% if blank_password %}has-error{% endif %}">
<label class="control-label" for="username">Password</label>
<input type="password" class="form-control"
placeholder="Password {% if create %}(Required){% else %}(Leave blank to keep unchanged){% endif %}"
name="password">
<span class="form-control-feedback"></span>
{% if blank_password %}
<span class="help-block">The password cannot be blank.</span>
{% endif %}
</div>
</div> </div>
<div class="card-footer"> <!-- /.row -->
<button type="submit" class="btn btn-primary">
{% if create %}Create{% else %}Update{% endif %} User
</button>
</div>
</form>
</div> </div>
{% if not create %} <!-- /.container-fluid -->
{% if user.otp_secret %} </section>
<div class="card">
<div class="card-header">
<h3 class="card-title">Two Factor Authentication</h3>
</div>
<div class="card-body">
<p>If two factor authentication is configured for this user and is causing problems due to a lost device or
technical issue, it can be disabled here.
</p>
<p>The user will need to reconfigure two factor authentication, to re-enable it.</p>
<p><strong>Beware: This could compromise security!</strong></p>
</div>
<div class="card-footer">
<button type="button" class="btn btn-warning button_otp_disable" id="{{ user.username }}">
Disable Two Factor Authentication
</button>
</div>
</div>
{% endif %}
{% endif %}
</div>
<div class="col-8">
<div class="card">
<div class="card-header">
<h3 class="card-title">
Help with {% if create %}creating a new{% else%}editing a{% endif %} user
</h3>
</div>
<div class="card-body">
<p>Fill in all the fields to the in the form to the left.</p>
{% if create %}
<p><strong>Newly created users do not have access to any domains.</strong> You will need to grant
access to the user once it is created via the domain management buttons on the dashboard.
</p>
{% else %}
<p><strong>Username</strong> cannot be changed.</p>
<p><strong>Password</strong> can be left empty to keep the current password.</p>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// handle disabling two factor authentication // handle disabling two-factor authentication
$(document.body).on('click', '.button_otp_disable', function () { $(document.body).on('click', '.button_otp_disable', function () {
var modal = $("#modal_otp_disable"); var modal = $("#modal_otp_disable");
var username = $(this).prop('id'); var username = $(this).prop('id');
var info = "Are you sure you want to disable two factor authentication for user " + username + "?"; var info = "Are you sure you want to disable two factor authentication for user " + username + "?";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.find('#button_otp_disable_confirm').click(function () { modal.find('#button_otp_disable_confirm').click(function () {
var postdata = { var postdata = {
'action': 'user_otp_disable', 'action': 'user_otp_disable',
'data': username, 'data': username,
'_csrf_token': '{{ csrf_token() }}' '_csrf_token': '{{ csrf_token() }}'
} }
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', false, true); applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', false, true);
}) })
modal.modal('show'); modal.modal('show');
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<div class="modal fade modal-warning" id="modal_otp_disable"> <div class="modal fade modal-warning" id="modal_otp_disable">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Confirmation</h4> <h4 class="modal-title">Confirmation</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p></p> <p></p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"> <button type="button" class="btn btn-default" data-dismiss="modal">
Close Close
</button> </button>
<button type="button" class="btn btn-danger float-right" id="button_otp_disable_confirm"> <button type="button" class="btn btn-danger float-right" id="button_otp_disable_confirm">
Disable Two Factor Authentication Disable Two Factor Authentication
</button> </button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,201 +1,266 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_global_search" %} {% set active_page = "admin_global_search" %}
{% block title %}<title>Global Search - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
Global Search - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">Global Search</h1>
Global Search </div>
<small>Search for domains, records and comments directly from PDNS API</small> <!-- /.col -->
</h1> <div class="col-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Global Search</li>
</ol>
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div> </div>
<div class="col-sm-6"> <!-- /.container-fluid -->
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Global Search</li>
</ol>
</div>
</div>
</div> </div>
</div> <!-- /.content-header -->
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="card"> <form action="" method="get">
<div class="card-header"> <div class="card card-outline card-primary shadow">
<h3 class="card-title">Global Search</h3> <div class="card-header">
</div> <h3 class="card-title">Search Form</h3>
<div class="card-body"> </div>
<form action="" method="get"> <!-- /.card-header -->
<div class="input-group"> <div class="card-body">
<input type="text" name="q" class="form-control" placeholder="Your keyword..."> <div class="callout callout-info">
<div class="input-group-btn"> <p>This tool can be used to search for domains, records, and comments via the PDNS
<button type="submit" class="btn btn-success"><i class="fa fa-search"></i></button> API.</p>
</div> </div>
<!-- /.callout -->
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label id="txtQuery" class="input-group-text">
Search Query
</label>
</div>
<input type="text" id="txtQuery" name="q" class="form-control"
value="{{ query }}"
placeholder="Enter search query...">
<div class="input-group-append">
<button type="submit" class="btn btn-success" title="Execute Query">
<i class="fa-solid fa-search"></i>
</button>
</div>
</div>
</div>
<!-- /.form-group -->
<div class="callout callout-warning">
<p>The * character can be used in your keyword as a wildcard character and the ?
character can be used as a wildcard for a single character.</p>
</div>
<!-- /.callout -->
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</form>
</div> </div>
</form> <!-- /.col -->
<div>
<p><b>Hints:</b> The * character can be used in your keyword as a wildcard character and the ? character can be used as
a wildcard for a
single character.
</p>
</div>
</div> </div>
</div> <!-- /.row -->
</div>
</div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Domains ({{ domains|length }})</h3> <h3 class="card-title">Zones ({{ domains|length }})</h3>
</div>
<!-- /.card-header -->
<div class="card-body table-responsive">
<table id="tbl_domain" class="table table-bordered table-striped table-hover table-sm">
<thead>
<tr>
<th>Zone Name</th>
</tr>
</thead>
<tbody>
{% for domain in domains %}
<tr class="odd gradeX">
<td>
<a href="{{ url_for('domain.domain', domain_name=domain['name']) }}">{{ domain['name'] }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div> </div>
<div class="card-body"> <!-- /.row -->
<table id="tbl_domain" class="table table-bordered table-striped">
<thead>
<tr>
<th>Domain</th>
</tr>
</thead>
<tbody>
{% for domain in domains %}
<tr class="odd gradeX">
<td>
<a href="{{ url_for('domain.domain', domain_name=domain['name']) }}">{{ domain['name'] }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Records ({{ records|length }})</h3> <h3 class="card-title">Zone Records ({{ records|length }})</h3>
</div>
<!-- /.card-header -->
<div class="card-body table-responsive">
<table id="tbl_record" class="table table-bordered table-striped table-hover table-sm">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Status</th>
<th>TTL</th>
<th>Data</th>
</tr>
</thead>
<tbody>
{% for record in records %}
<tr class="odd gradeX">
<td>
<a href="{{ url_for('domain.domain', domain_name=record['zone_id']) }}">{{ record['name'] }}</a>
</td>
<td>{{ record['type'] }}</td>
<td>{{ 'Disabled' if record['disabled'] else 'Active' }}</td>
<td>{{ record['ttl'] }}</td>
<td>{{ record['content'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div> </div>
<div class="card-body"> <!-- /.row -->
<table id="tbl_record" class="table table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Status</th>
<th>TTL</th>
<th>Data</th>
</tr>
</thead>
<tbody>
{% for record in records %}
<tr class="odd gradeX">
<td>
<a href="{{ url_for('domain.domain', domain_name=record['zone_id']) }}">{{ record['name'] }}</a>
</td>
<td>{{ record['type'] }}</td>
<td>{{ 'Disabled' if record['disabled'] else 'Active' }}</td>
<td>{{ record['ttl'] }}</td>
<td>{{ record['content'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Comments ({{ comments|length }})</h3> <h3 class="card-title">Comments ({{ comments|length }})</h3>
</div>
<!-- /.card-header -->
<div class="card-body table-responsive">
<table id="tbl_comment" class="table table-bordered table-striped table-hover table-sm">
<thead>
<tr>
<th>Comment</th>
<th>Record</th>
<th>Zone</th>
</tr>
</thead>
<tbody>
{% for comment in comments %}
<tr class="odd gradeX">
<td>{{ comment['content'] }}</td>
<td>{{ comment['name'] }}</td>
<td>
<a href="{{ url_for('domain.domain', domain_name=comment['zone_id']) }}">{{ comment['zone_id'] }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div> </div>
<div class="card-body"> <!-- /.row -->
<table id="tbl_comment" class="table table-bordered table-striped">
<thead>
<tr>
<th>Comment</th>
<th>Record</th>
<th>Domain</th>
</tr>
</thead>
<tbody>
{% for comment in comments %}
<tr class="odd gradeX">
<td>{{ comment['content'] }}</td>
<td>{{ comment['name'] }}</td>
<td>
<a href="{{ url_for('domain.domain', domain_name=comment['zone_id']) }}">{{ comment['zone_id'] }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div> </div>
</div> <!-- /.container-fluid -->
</div> </section>
</section> {% endblock %}
{% block head_styles %}
<style>
table#tbl_record { table-layout: fixed; }
table#tbl_record thead th:nth-child(0n+1) { width: 20%;}
table#tbl_record thead th:nth-child(0n+2),
table#tbl_record thead th:nth-child(0n+3),
table#tbl_record thead th:nth-child(0n+4) { width: 5%; }
table#tbl_record thead th:nth-child(0n+2),
table#tbl_record thead th:nth-child(0n+3),
table#tbl_record thead th:nth-child(0n+4),
table#tbl_record tbody td:nth-child(0n+2),
table#tbl_record tbody td:nth-child(0n+3),
table#tbl_record tbody td:nth-child(0n+4) { text-align: center; }
table#tbl_record tbody td:first-of-type,
table#tbl_record tbody td:last-of-type { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media only screen and (max-device-width: 480px) {
table#tbl_record thead th:nth-child(0n+1) { width: 10%;}
table#tbl_record thead th:nth-child(0n+2),
table#tbl_record thead th:nth-child(0n+3),
table#tbl_record thead th:nth-child(0n+4) { width: 15%; }
}
@media only screen and (max-device-width: 992px) {
table#tbl_record { table-layout: auto; }
}
@media only screen and (min-device-width: 481px) and (max-device-width: 992px) {
table#tbl_record thead th:nth-child(0n+2),
table#tbl_record thead th:nth-child(0n+3),
table#tbl_record thead th:nth-child(0n+4) { width: 10%; }
}
</style>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// set up domain result data table // Initialize DataTables for zones
$("#tbl_domain").DataTable({ $("#tbl_domain").DataTable({
"paging": false, "paging": true,
"lengthChange": false, "lengthChange": true,
"searching": false, "searching": true,
"ordering": true, "ordering": true,
"info": false, "info": false,
"autoWidth": false, "autoWidth": false,
"order": [ "order": [
[0, "asc"] [0, "asc"]
] ]
}); });
// set up domain result data table // Initialize DataTables for zone records
$("#tbl_record").DataTable({ $("#tbl_record").DataTable({
"paging": false, "paging": true,
"lengthChange": false, "lengthChange": true,
"searching": false, "searching": true,
"ordering": true, "ordering": true,
"info": false, "info": false,
"autoWidth": false, "autoWidth": false,
"order": [ "order": [
[0, "asc"] [0, "asc"]
] ]
}); });
// set up domain result data table // Initialize DataTables for comments
$("#tbl_comment").DataTable({ $("#tbl_comment").DataTable({
"paging": false, "paging": true,
"lengthChange": false, "lengthChange": true,
"searching": false, "searching": true,
"ordering": true, "ordering": true,
"info": false, "info": false,
"autoWidth": false, "autoWidth": false,
"order": [ "order": [
[0, "asc"] [0, "asc"]
] ]
}); });
</script> </script>
{% endblock %} {% endblock %}

File diff suppressed because it is too large Load Diff

View File

@ -1,159 +1,167 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_accounts" %} {% set active_page = "admin_accounts" %}
{% block title %}<title>Accounts - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
Account Management - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">Accounts</h1>
Accounts </div>
<small>Manage</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
<li class="breadcrumb-item active">Accounts</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
<li class="breadcrumb-item active">Accounts</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card card-outline card-primary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Account Management</h3> <h3 class="card-title">Accounts</h3>
<a href="{{ url_for('admin.edit_account') }}"> <div class="card-tools">
<button type="button" class="btn btn-primary float-right button_add_account"> <a href="{{ url_for('admin.edit_account') }}">
<i class="fa-solid fa-plus"></i>&nbsp;Add Account <button type="button" class="btn btn-primary button_add_account"
</button> title="Add Account">
</a> <i class="fa-solid fa-plus"></i>&nbsp;Add Account
</div> </button>
<div class="card-body"> </a>
<table id="tbl_accounts" class="table table-bordered table-striped"> </div>
<thead> </div>
<tr> <!-- /.card-header -->
<th>Name</th> <div class="card-body table-responsive">
<th>Description</th> <table id="tbl_accounts"
<th>Contact</th> class="table table-bordered table-striped table-hover table-sm records">
<th>Mail</th> <thead>
<th>Member</th> <tr>
<th>Domain</th> <th>Name</th>
<th>Action</th> <th>Description</th>
</tr> <th>Contact</th>
</thead> <th>Mail</th>
<tbody> <th>Member</th>
{% for account in accounts %} <th>Domain</th>
<tr class="odd gradeX"> <th>Action</th>
<td>{{ account.name }}</td> </tr>
<td>{{ account.description }}</td> </thead>
<td>{{ account.contact }}</td> <tbody>
<td>{{ account.mail }}</td> {% for account in accounts %}
<td>{{ account.user_num }}</td> <tr class="odd gradeX">
<td>{{ account.domains|length }}</td> <td>{{ account.name }}</td>
<td width="15%"> <td>{{ account.description }}</td>
<div class="dropdown"> <td>{{ account.contact }}</td>
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <td>{{ account.mail }}</td>
<i class="fa-solid fa-bars"></i>&nbsp;Actions <td>{{ account.user_num }}</td>
</button> <td>{{ account.domains|length }}</td>
<div class="dropdown-menu" aria-labelledby="dropdownMenu"> <td>
<button type="button" class="dropdown-item btn-warning" onclick="window.location.href='{{ url_for('admin.edit_account', account_name=account.name) }}'"> <div class="dropdown">
<i class="fa-solid fa-edit"></i>&nbsp;Edit Account <button class="btn btn-primary dropdown-toggle" type="button"
</button> id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true"
<div class="dropdown-divider"></div> aria-expanded="false">
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{ account.name }}"> <i class="fa-solid fa-bars"></i>
<font color="red"> </button>
<i class="fa-solid fa-trash"></i>&nbsp;Delete Account <div class="dropdown-menu" aria-labelledby="dropdownMenu">
</font> <button type="button" class="dropdown-item btn-warning"
</button> onclick="window.location.href='{{ url_for('admin.edit_account', account_name=account.name) }}'">
</div> <i class="fa-solid fa-edit"></i>&nbsp;Edit Account
</div> </button>
</td> <div class="dropdown-divider"></div>
</tr> <button type="button"
{% endfor %} class="dropdown-item btn-secondary button_delete"
</tbody> id="{{ account.name }}">
</table> <span style="color: red;">
</div> <i class="fa-solid fa-trash"></i>&nbsp;Delete Account
</span>
</button>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div> </div>
</div> <!-- /.container-fluid -->
</div> </section>
</div>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %}
<script>
// set up accounts data table
$("#tbl_accounts").DataTable({
"paging": true,
"lengthChange": true,
"searching": true,
"ordering": true,
"columnDefs": [{
"orderable": false,
"targets": [-1]
}],
"info": false,
"autoWidth": false,
"lengthMenu": [
[10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"]
],
"pageLength": 10
});
// handle deletion of account {% block extrascripts %}
$(document.body).on('click', '.button_delete', function () { <script>
var modal = $("#modal_delete"); // Intialize DataTable
var accountname = $(this).prop('id'); $("#tbl_accounts").DataTable({
var info = "Are you sure you want to delete " + accountname + "?"; "paging": true,
modal.find('.modal-body p').text(info); "lengthChange": true,
modal.find('#button_delete_confirm').click(function () { "searching": true,
var postdata = { "ordering": true,
'action': 'delete_account', "columnDefs": [{
'data': accountname, "orderable": false,
'_csrf_token': '{{ csrf_token() }}' "targets": [-1]
} }],
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-account', false, true); "info": false,
modal.modal('hide'); "autoWidth": false,
}) "lengthMenu": [
modal.modal('show'); [10, 25, 50, 100, -1],
}); [10, 25, 50, 100, "All"]
</script> ],
"pageLength": 10
});
// handle deletion of account
$(document.body).on('click', '.button_delete', function () {
var modal = $("#modal_delete");
var accountname = $(this).prop('id');
var info = "Are you sure you want to delete " + accountname + "?";
modal.find('.modal-body p').text(info);
modal.find('#button_delete_confirm').click(function () {
var postdata = {
'action': 'delete_account',
'data': accountname,
'_csrf_token': '{{ csrf_token() }}'
}
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-account', false, true);
modal.modal('hide');
})
modal.modal('show');
});
</script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<div class="modal fade" id="modal_delete"> <div class="modal fade" id="modal_delete">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Confirmation</h4> <h4 class="modal-title">Confirmation</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p></p> <p></p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default float-left" data-dismiss="modal">Close</button> <button type="button" class="btn btn-default float-left" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-danger" id="button_delete_confirm">Delete</button> <button type="button" class="btn btn-danger" id="button_delete_confirm">Delete</button>
</div>
</div> </div>
<!-- /.modal-content -->
</div> </div>
<!-- /.modal-content --> <!-- /.modal-dialog -->
</div> </div>
<!-- /.modal-dialog -->
</div>
{% endblock %} {% endblock %}

View File

@ -1,153 +1,156 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_keys" %} {% set active_page = "admin_keys" %}
{% block title %}<title>API Keys - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
Key Management - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">API Keys</h1>
API Keys </div>
<small>Management</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">API Keys</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">API Keys</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="card"> <div class="card">
<div class="card-header with-border"> <div class="card-header with-border">
<h3 class="card-title">Key Management</h3> <h3 class="card-title">API Keys</h3>
<a href="{{ url_for('admin.edit_key') }}"> <div class="card-tools">
<button type="button" class="btn btn-primary float-right button_add_key"> <a href="{{ url_for('admin.edit_key') }}">
<i class="fa-solid fa-plus"></i>&nbsp;Add Key <button type="button" class="btn btn-primary float-right button_add_key" title="Create Key">
</button> <i class="fa-solid fa-plus"></i>&nbsp;Create Key
</a> </button>
</div> </a>
<div class="card-body"> </div>
<table id="tbl_keys" class="table table-bordered table-striped"> <!-- /.card-tools -->
<thead> </div>
<tr> <!-- /.card-header -->
<th>Id</th> <div class="card-body table-responsive">
<th>Role</th> <table id="tbl_keys" class="table table-bordered table-striped table-hover table-sm records">
<th>Description</th> <thead>
<th>Domains</th> <tr>
<th>Accounts</th> <th>Id</th>
<th>Actions</th> <th>Role</th>
</tr> <th>Description</th>
</thead> <th>Domains</th>
<tbody> <th>Accounts</th>
{% for key in keys %} <th>Actions</th>
<tr class="odd gradeX"> </tr>
<td>{{ key.id }}</td> </thead>
<td>{{ key.role.name }}</td> <tbody>
<td>{{ key.description }}</td> {% for key in keys %}
<td>{% for domain in key.domains %}{{ domain.name }}{% if not loop.last %}, {% endif %}{% endfor %}</td> <tr class="odd gradeX">
<td>{% for account in key.accounts %}{{ account.name }}{% if not loop.last %}, {% endif %}{% endfor %}</td> <td>{{ key.id }}</td>
<td width="15%"> <td>{{ key.role.name }}</td>
<div class="dropdown"> <td>{{ key.description }}</td>
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <td>{% for domain in key.domains %}{{ domain.name }}{% if not loop.last %},
<i class="fa-solid fa-bars"></i>&nbsp;Actions {% endif %}{% endfor %}</td>
</button> <td>{% for account in key.accounts %}{{ account.name }}{% if not loop.last %},
<div class="dropdown-menu" aria-labelledby="dropdownMenu"> {% endif %}{% endfor %}</td>
<button type="button" class="dropdown-item btn-warning" onclick="window.location.href='{{ url_for('admin.edit_key', key_id=key.id) }}'"> <td>
<i class="fa-solid fa-edit"></i>&nbsp;Edit API Key <div class="dropdown">
</button> <button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu"
<div class="dropdown-divider"></div> data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{ key.id }}"> <i class="fa-solid fa-bars"></i>
<font color="red"> </button>
<i class="fa-solid fa-trash"></i>&nbsp;Delete API Key <div class="dropdown-menu" aria-labelledby="dropdownMenu">
</font> <button type="button" class="dropdown-item btn-warning"
</button> onclick="window.location.href='{{ url_for('admin.edit_key', key_id=key.id) }}'">
</div> <i class="fa-solid fa-edit"></i>&nbsp;Edit API Key
</div> </button>
</td> <div class="dropdown-divider"></div>
</tr> <button type="button" class="dropdown-item btn-secondary button_delete"
{% endfor %} id="{{ key.id }}">
</tbody> <font color="red">
</table> <i class="fa-solid fa-trash"></i>&nbsp;Delete API Key
</div> </font>
</div> </button>
</div> </div>
</section> </div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.container-fluid -->
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// set up key data table // set up key data table
$("#tbl_keys").DataTable({ $("#tbl_keys").DataTable({
"paging": true, "paging": true,
"lengthChange": true, "lengthChange": true,
"searching": true, "searching": true,
"ordering": true, "ordering": true,
"info": false, "info": false,
"autoWidth": false, "autoWidth": false,
"lengthMenu": [ "lengthMenu": [
[10, 25, 50, 100, -1], [10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"] [10, 25, 50, 100, "All"]
], ],
"pageLength": 10 "pageLength": 10
}); });
// handle deletion of keys // handle deletion of keys
$(document.body).on('click', '.button_delete', function () { $(document.body).on('click', '.button_delete', function () {
var modal = $("#modal_delete"); var modal = $("#modal_delete");
var key_id = $(this).prop('id'); var key_id = $(this).prop('id');
var info = "Are you sure you want to delete key #" + key_id + "?"; var info = "Are you sure you want to delete key #" + key_id + "?";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.find('#button_delete_confirm').click(function () { modal.find('#button_delete_confirm').click(function () {
var postdata = { var postdata = {
'action': 'delete_key', 'action': 'delete_key',
'data': key_id, 'data': key_id,
'_csrf_token': '{{ csrf_token() }}' '_csrf_token': '{{ csrf_token() }}'
} }
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-keys', false, true); applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-keys', false, true);
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<div class="modal fade modal-warning" id="modal_delete"> <div class="modal fade modal-warning" id="modal_delete">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Confirmation</h4> <h4 class="modal-title">Confirmation</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button type="button" class="btn btn-danger" id="button_delete_confirm">
Delete
</button>
</div>
</div>
</div> </div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button type="button" class="btn btn-danger" id="button_delete_confirm">
Delete
</button>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,237 +1,243 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_users" %} {% set active_page = "admin_users" %}
{% block title %}<title>Users - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
User Management - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">Users</h1>
User </div>
<small>Manage user privileges</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
<li class="breadcrumb-item active">Users</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
<li class="breadcrumb-item active">User</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card card-outline card-primary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">User Management</h3> <h3 class="card-title">Users</h3>
<a href="{{ url_for('admin.edit_user') }}"> <div class="card-tools">
<button type="button" class="btn btn-primary float-right button_add_user"> <a href="{{ url_for('admin.edit_user') }}">
<i class="fa-solid fa-plus"></i>&nbsp;Add User <button type="button" class="btn btn-primary button_add_user" title="Create User">
</button> <i class="fa-solid fa-plus"></i>&nbsp;Create User
</a> </button>
</div> </a>
<div class="card-body"> </div>
<table id="tbl_users" class="table table-bordered table-striped"> <!-- /.card-tools -->
<thead>
<tr>
<th>Username</th>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Role</th>
<th>Privileges</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr class="odd gradeX">
<td>{{ user.username }}</td>
<td>{{ user.firstname }}</td>
<td>{{ user.lastname }}</td>
<td>{{ user.email }}</td>
<td>
<select id="{{ user.username }}" class="user_role"
{% if user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %}disabled{% endif %}>
{% for role in roles %}
<option value="{{ role.name }}"
{% if role.id==user.role.id %}selected{% endif %}>
{{ role.name }}
</option>
{% endfor %}
</select>
</td>
<td width="6%">
<button type="button" class="btn btn-warning button_revoke"
id="{{ user.username }}"
{% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
<i class="fa-solid fa-link-slash"></i>&nbsp;Revoke
</button>
</td>
<td width="15%">
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
{% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
<i class="fa-solid fa-bars"></i>&nbsp;Actions
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu">
<button type="button" class="dropdown-item btn-warning" onclick="window.location.href='{{ url_for('admin.edit_user', user_username=user.username) }}'">
<i class="fa-solid fa-edit"></i>&nbsp;Edit User
</button>
{% if not user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %}
<div class="dropdown-divider"></div>
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{ user.username }}">
<font color="red">
<i class="fa-solid fa-trash"></i>&nbsp;Delete User
</font>
</button>
{% endif %}
</div>
</div> </div>
</td> <!-- /.card-header -->
</tr> <div class="card-body table-responsive">
{% endfor %} <table id="tbl_users"
</tbody> class="table table-bordered table-striped table-hover table-sm records">
</table> <thead>
<tr>
<th>Username</th>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Role</th>
<th>Privileges</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr class="odd gradeX">
<td>{{ user.username }}</td>
<td>{{ user.firstname }}</td>
<td>{{ user.lastname }}</td>
<td>{{ user.email }}</td>
<td>
<select id="{{ user.username }}" class="user_role"
{% if user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %}disabled{% endif %}>
{% for role in roles %}
<option value="{{ role.name }}"
{% if role.id==user.role.id %}selected{% endif %}>
{{ role.name }}
</option>
{% endfor %}
</select>
</td>
<td>
<button type="button" class="btn btn-warning button_revoke"
title="Revoke Privileges"
id="{{ user.username }}"
{% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
<i class="fa-solid fa-link-slash"></i>
</button>
</td>
<td>
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button"
id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false"
{% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
<i class="fa-solid fa-bars"></i>
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu">
<button type="button" class="dropdown-item btn-warning"
onclick="window.location.href='{{ url_for('admin.edit_user', user_username=user.username) }}'">
<i class="fa-solid fa-edit"></i>&nbsp;Edit User
</button>
{% if not user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %}
<div class="dropdown-divider"></div>
<button type="button"
class="dropdown-item btn-secondary button_delete"
id="{{ user.username }}">
<span style="color: red;">
<i class="fa-solid fa-trash"></i>&nbsp;Delete User
</span>
</button>
{% endif %}
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div> </div>
</div> <!-- /.row -->
</div> </div>
</div> <!-- /.container-fluid -->
</div> </section>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// set up user data table // set up user data table
$("#tbl_users").DataTable({ $("#tbl_users").DataTable({
"paging": true, "paging": true,
"lengthChange": true, "lengthChange": true,
"searching": true, "searching": true,
"ordering": true, "ordering": true,
"info": false, "info": false,
"autoWidth": false, "autoWidth": false,
"lengthMenu": [ "lengthMenu": [
[10, 25, 50, 100, -1], [10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"] [10, 25, 50, 100, "All"]
], ],
"pageLength": 10 "pageLength": 10
}); });
// handle revocation of privileges // handle revocation of privileges
$(document.body).on('click', '.button_revoke', function () { $(document.body).on('click', '.button_revoke', function () {
var modal = $("#modal_revoke"); var modal = $("#modal_revoke");
var username = $(this).prop('id'); var username = $(this).prop('id');
var info = "Are you sure you want to revoke all privileges for user " + username + var info = "Are you sure you want to revoke all privileges for user " + username +
"? They will not able to access any domain."; "? They will not able to access any domain.";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.find('#button_revoke_confirm').click(function () { modal.find('#button_revoke_confirm').click(function () {
var postdata = { var postdata = {
'action': 'revoke_user_privileges', 'action': 'revoke_user_privileges',
'data': username, 'data': username,
'_csrf_token': '{{ csrf_token() }}' '_csrf_token': '{{ csrf_token() }}'
} }
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', true); applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', true);
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
}); });
// handle deletion of user // handle deletion of user
$(document.body).on('click', '.button_delete', function () { $(document.body).on('click', '.button_delete', function () {
var modal = $("#modal_delete"); var modal = $("#modal_delete");
var username = $(this).prop('id'); var username = $(this).prop('id');
var info = "Are you sure you want to delete user " + username + "?"; var info = "Are you sure you want to delete user " + username + "?";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.find('#button_delete_confirm').click(function () { modal.find('#button_delete_confirm').click(function () {
var postdata = { var postdata = {
'action': 'delete_user', 'action': 'delete_user',
'data': username, 'data': username,
'_csrf_token': '{{ csrf_token() }}' '_csrf_token': '{{ csrf_token() }}'
} }
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', false, true); applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', false, true);
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
}); });
// handle user role changing // handle user role changing
$(document.body).on('change', '.user_role', function () { $(document.body).on('change', '.user_role', function () {
var role_name = this.value; var role_name = this.value;
var username = $(this).prop('id'); var username = $(this).prop('id');
var postdata = { var postdata = {
'action': 'update_user_role', 'action': 'update_user_role',
'data': { 'data': {
'username': username, 'username': username,
'role_name': role_name 'role_name': role_name
}, },
'_csrf_token': '{{ csrf_token() }}' '_csrf_token': '{{ csrf_token() }}'
}; };
applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', showResult = true); applyChanges(postdata, $SCRIPT_ROOT + '/admin/manage-user', showResult = true);
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<div class="modal fade" id="modal_revoke"> <div class="modal fade" id="modal_revoke">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Confirmation</h4> <h4 class="modal-title">Confirmation</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
Close
</button>
<button type="button" class="btn btn-danger float-right" id="button_revoke_confirm">
Revoke
</button>
</div>
</div>
</div> </div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
Close
</button>
<button type="button" class="btn btn-danger float-right" id="button_revoke_confirm">
Revoke
</button>
</div>
</div>
</div> </div>
</div>
<div class="modal fade" id="modal_delete"> <div class="modal fade" id="modal_delete">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Confirmation</h4> <h4 class="modal-title">Confirmation</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default float-right" data-dismiss="modal">
Close
</button>
<button type="button" class="btn btn-danger float-right" id="button_delete_confirm">
Delete
</button>
</div>
</div>
</div> </div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default float-right" data-dismiss="modal">
Close
</button>
<button type="button" class="btn btn-danger float-right" id="button_delete_confirm">
Delete
</button>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,127 +0,0 @@
{% extends "base.html" %}
{% set active_page = "admin_console" %}
{% block title %}
<title>
Admin Console - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %}
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">
PowerDNS
<small>Server Statistics & Configuration</small>
</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">PowerDNS Server Statistics & Configuration</li>
</ol>
</div>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card shadow">
<div class="card-header">
<h3 class="card-title">PowerDNS Server Statistics</h3>
</div>
<div class="card-body">
<table id="tbl_statistics" class="table table-bordered table-striped">
<thead>
<tr>
<th width="30%">Statistic</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for statistic in statistics %}
<tr class="odd gradeX">
<td>
<a href="https://doc.powerdns.com/authoritative/search.html?q={{ statistic['name'] }}"
target="_blank" class="btn btn-primary">
<i class="fa fa-search"></i>&nbsp;{{ statistic['name'] }}
</a>
</td>
<td>{{ statistic['value'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card shadow">
<div class="card-header">
<h3 class="card-title">PowerDNS Server Configuration</h3>
</div>
<div class="card-body">
<table id="tbl_configuration" class="table table-bordered table-striped">
<thead>
<tr>
<th width="30%">Configuration</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for config in configs %}
<tr class="odd gradeX">
<td>
<a href="https://doc.powerdns.com/authoritative/search.html?q={{ config['name'] }}"
target="_blank" class="btn btn-primary">
<i class="fa-solid fa-search"></i>&nbsp;{{ config['name'] }}
</a>
</td>
<td>
{{ config['value'] }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block extrascripts %}
<script>
// set up statistics data table
$("#tbl_statistics").DataTable({
"paging": true,
"lengthChange": true,
"searching": true,
"ordering": true,
"info": true,
"autoWidth": false
});
// set up configuration data table
$("#tbl_configuration").DataTable({
"paging": true,
"lengthChange": true,
"searching": true,
"ordering": true,
"info": true,
"autoWidth": false
});
</script>
{% endblock %}

View File

@ -0,0 +1,84 @@
{% extends "base.html" %}
{% set active_page = "server_configuration" %}
{% block title %}<title>Server Configuration - {{ SITE_NAME }}</title>{% endblock %}
{% block dashboard_stat %}
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">
Server Configuration
</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Server Configuration</li>
</ol>
</div>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card card-outline card-primary shadow">
<div class="card-header">
<h3 class="card-title">Server Configuration</h3>
</div>
<!-- /.card-header -->
<div class="card-body table-responsive">
<table id="tbl_configuration" class="table table-bordered table-striped table-hover table-sm">
<thead>
<tr>
<th>Configuration</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for config in configs %}
<tr class="odd gradeX">
<td>
<a href="https://doc.powerdns.com/authoritative/search.html?q={{ config['name'] }}"
target="_blank" class="btn btn-primary" title="Search Documentation">
<i class="fa-solid fa-search"></i>&nbsp;{{ config['name'] }}
</a>
</td>
<td>
{{ config['value'] }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<!-- /.container-fluid -->
</section>
{% endblock %}
{% block extrascripts %}
<script>
// Initialize DataTables
$("#tbl_configuration").DataTable({
"paging": true,
"lengthChange": true,
"searching": true,
"ordering": true,
"info": true,
"autoWidth": false
});
</script>
{% endblock %}

View File

@ -0,0 +1,82 @@
{% extends "base.html" %}
{% set active_page = "server_statistics" %}
{% block title %}<title>Server Statistics - {{ SITE_NAME }}</title>{% endblock %}
{% block dashboard_stat %}
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">
Server Statistics
</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Server Statistics</li>
</ol>
</div>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card card-outline card-primary shadow">
<div class="card-header">
<h3 class="card-title">Server Statistics</h3>
</div>
<!-- /.card-header -->
<div class="card-body table-responsive">
<table id="tbl_statistics" class="table table-bordered table-striped table-hover table-sm">
<thead>
<tr>
<th>Statistic</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for statistic in statistics %}
<tr class="odd gradeX">
<td>
<a href="https://doc.powerdns.com/authoritative/search.html?q={{ statistic['name'] }}"
target="_blank" class="btn btn-primary" title="Search Documentation">
<i class="fa fa-search"></i>&nbsp;{{ statistic['name'] }}
</a>
</td>
<td>{{ statistic['value'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<!-- /.container-fluid -->
</section>
{% endblock %}
{% block extrascripts %}
<script>
// Initialize DataTables
$("#tbl_statistics").DataTable({
"paging": true,
"lengthChange": true,
"searching": true,
"ordering": true,
"info": true,
"autoWidth": false
});
</script>
{% endblock %}

File diff suppressed because it is too large Load Diff

View File

@ -1,116 +1,114 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_settings" %} {% set active_page = "admin_settings" %}
{% block title %}<title>Basic Settings - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
Basic Settings - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">Basic Settings</h1>
Settings </div>
<small>Basic</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Basic Settings</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Settings - Basic</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="card"> <div class="card card-outline card-primary shadow">
<div class="card-header with-border"> <div class="card-header with-border">
<h3 class="card-title">Basic Settings</h3> <h3 class="card-title">Settings Editor</h3>
</div> </div>
<div class="card-body"> <!-- /.card-header -->
<table id="tbl_settings" class="table table-bordered table-striped"> <div class="card-body table-responsive">
<thead> <table id="tbl_settings" class="table table-bordered table-striped table-hover table-sm">
<tr> <thead>
<th>Setting Name</th> <tr>
<th>Current Value</th> <th>Setting Name</th>
<th>Action</th> <th>Current Value</th>
</tr> <th>Action</th>
</thead> </tr>
<tbody> </thead>
{% for setting in settings %} <tbody>
<tr class="odd"> {% for setting in settings %}
<td> <tr class="odd">
{{ setting }} <td>
</td> <label for="value{{ loop.index }}">{{ setting }}</label>
{% if SETTING.get(setting) in [False] %} </td>
<td><i class="fas fa-toggle-off"></i>&nbsp;Off</td> {% if SETTING.get(setting) in [False] %}
<td width="20%"> <td><i class="fas fa-toggle-off"></i>&nbsp;Off</td>
<button type="button" class="btn btn-success setting-toggle-button" id="{{ setting }}"> <td>
<i class="fa-solid fa-toggle-on"></i>&nbsp;Turn On <button type="button" class="btn btn-success setting-toggle-button"
</button> id="{{ setting }}">
</td> <i class="fa-solid fa-toggle-on"></i>&nbsp;Turn On
{% elif SETTING.get(setting) in [True] %} </button>
<td><i class="fas fa-toggle-on"></i>&nbsp;On</td> </td>
<td width="20%"> {% elif SETTING.get(setting) in [True] %}
<button type="button" class="btn btn-danger setting-toggle-button" id="{{ setting }}"> <td><i class="fas fa-toggle-on"></i>&nbsp;On</td>
<i class="fa-solid fa-toggle-off"></i>&nbsp;Turn Off <td>
</button> <button type="button" class="btn btn-danger setting-toggle-button"
</td> id="{{ setting }}">
{% else %} <i class="fa-solid fa-toggle-off"></i>&nbsp;Turn Off
<td> </button>
<input name="value" id="value" value="{{ SETTING.get(setting) }}"> </td>
</td> {% else %}
<td width="20%"> <td>
<button type="button" class="btn btn-primary setting-save-button" id="{{ setting }}"> <input name="value" id="value{{ loop.index }}" value="{{ SETTING.get(setting) }}">
<i class="fa-solid fa-save"></i>&nbsp;Save </td>
</button> <td>
</td> <button type="button" class="btn btn-primary setting-save-button"
{% endif %} id="{{ setting }}">
</tr> <i class="fa-solid fa-save"></i>&nbsp;Save
{% endfor %} </button>
</tbody> </td>
</table> {% endif %}
</div> </tr>
</div> {% endfor %}
</div> </tbody>
</section> </table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.container-fluid -->
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// set up settings table // set up settings table
$("#tbl_settings").DataTable({ $("#tbl_settings").DataTable({
"paging": false, "paging": false,
"lengthChange": false, "lengthChange": false,
"searching": true, "searching": true,
"ordering": true, "ordering": true,
"info": true, "info": true,
"autoWidth": false "autoWidth": false
}); });
$(document.body).on('click', '.setting-toggle-button', function () { $(document.body).on('click', '.setting-toggle-button', function () {
var setting = $(this).prop('id'); var setting = $(this).prop('id');
applyChanges({ applyChanges({
'_csrf_token': '{{ csrf_token() }}' '_csrf_token': '{{ csrf_token() }}'
}, $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/toggle', false, true) }, $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/toggle', false, true)
}); });
$(document.body).on('click', '.setting-save-button', function () { $(document.body).on('click', '.setting-save-button', function () {
var setting = $(this).prop('id'); var setting = $(this).prop('id');
var value = $(this).parents('tr').find('#value')[0].value; var value = $(this).parents('tr').find('#value')[0].value;
var postdata = { var postdata = {
'value': value, 'value': value,
'_csrf_token': '{{ csrf_token() }}' '_csrf_token': '{{ csrf_token() }}'
}; };
applyChanges(postdata, $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/edit', false, true) applyChanges(postdata, $SCRIPT_ROOT + '/admin/setting/basic/' + setting + '/edit', false, true)
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,110 +1,122 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_settings" %} {% set active_page = "admin_settings" %}
{% block title %}<title>Server Settings - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
PDNS Settings - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">Server Settings</h1>
Settings </div>
<small>PowerDNS Authoritative Server</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Server Settings</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Settings - PowerDNS Authoritative Server</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-4"> <div class="col-12 col-sm-6 col-lg-4">
<div class="card shadow card-outline card-secondary"> <form role="form" method="post" data-toggle="validator">
<div class="card-header"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<h3 class="card-title">PDNS Settings</h3> <div class="card card-outline card-primary shadow">
<div class="card-header">
<h3 class="card-title">Settings Editor</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
{% if not SETTING.get('pdns_api_url') or not SETTING.get('pdns_api_key') or not SETTING.get('pdns_version') %}
<div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">
&times;
</button>
<h4><i class="icon fa fa-ban"></i> Error!</h4>
Please complete your PowerDNS API configuration before continuing
</div>
{% endif %}
<div class="form-group has-feedback">
<label class="control-label" for="pdns_api_url">PowerDNS API URL</label>
<input type="url" class="form-control" placeholder="PowerDNS API URL"
name="pdns_api_url" id="pdns_api_url"
data-error="Please input a valid PowerDNS API URL" required
value="{{ pdns_api_url }}">
<span class="help-block with-errors"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="pdns_api_key">PowerDNS API Key</label>
<input type="password" class="form-control" placeholder="PowerDNS API Key"
name="pdns_api_key" id="pdns_api_key"
data-error="Please input a valid PowerDNS API key"
required
value="{{ pdns_api_key }}">
<span class="help-block with-errors"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="pdns_version">PowerDNS Version</label>
<input type="text" class="form-control" placeholder="PowerDNS Version"
name="pdns_version" id="pdns_version"
data-error="Please input PowerDNS version" required
value="{{ pdns_version }}">
<span class="help-block with-errors"></span>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="submit" class="btn btn-primary float-right" title="Save Settings">
<i class="fa-solid fa-save"></i>&nbsp;Save Settings
</button>
</div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form>
</div>
<!-- /.col -->
<div class="col-12 col-sm-6 col-lg-8">
<div class="card card-outline card-secondary shadow">
<div class="card-header">
<h3 class="card-title">Settings Editor</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<dl class="dl-horizontal">
<p>You must configure the API connection information before PowerDNS-Admin can query
your
PowerDNS data. Following fields are required:</p>
<dt>PowerDNS API URL</dt>
<dd>Your PowerDNS API URL (eg. http://127.0.0.1:8081/).</dd>
<dt>PowerDNS API Key</dt>
<dd>Your PowerDNS API key.</dd>
<dt>PowerDNS Version</dt>
<dd>Your PowerDNS version number (eg. 4.7.0).</dd>
</dl>
<p>Find more details at
<a href="https://doc.powerdns.com/md/httpapi/README/" target="_blank">https://doc.powerdns.com/md/httpapi/README/</a>
</p>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div> </div>
<form role="form" method="post" data-toggle="validator"> <!-- /.row -->
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="card-body">
{% if not SETTING.get('pdns_api_url') or not SETTING.get('pdns_api_key') or not SETTING.get('pdns_version') %}
<div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<h4><i class="icon fa fa-ban"></i> Error!</h4>
Please complete your PowerDNS API configuration before continuing
</div>
{% endif %}
<div class="form-group has-feedback">
<label class="control-label" for="pdns_api_url">PowerDNS API URL</label>
<input type="url" class="form-control" placeholder="PowerDNS API URL" name="pdns_api_url"
data-error="Please input a valid PowerDNS API URL" required value="{{ pdns_api_url }}">
<span class="help-block with-errors"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="pdns_api_key">PowerDNS API Key</label>
<input type="password" class="form-control" placeholder="PowerDNS API Key"
name="pdns_api_key" data-error="Please input a valid PowerDNS API key" required
value="{{ pdns_api_key }}">
<span class="help-block with-errors"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="pdns_version">PowerDNS Version</label>
<input type="text" class="form-control" placeholder="PowerDNS Version" name="pdns_version"
data-error="Please input PowerDNS version" required value="{{ pdns_version }}">
<span class="help-block with-errors"></span>
</div>
</div>
<div class="card-footer">
<button type="submit" class="btn btn-primary float-right">
<i class="fa-solid fa-save"></i>&nbsp;Save
</button>
</div>
</form>
</div>
</div> </div>
<div class="col-8"> <!-- /.container-fluid -->
<div class="card shadow card-outline card-secondary"> </section>
<div class="card-header">
<h3 class="card-title">Help</h3>
</div>
<div class="card-body">
<dl class="dl-horizontal">
<p>You must configure the API connection information before PowerDNS-Admin can query your
PowerDNS data. Following fields are required:</p>
<dt>PowerDNS API URL</dt>
<dd>Your PowerDNS API URL (eg. http://127.0.0.1:8081/).</dd>
<dt>PowerDNS API Key</dt>
<dd>Your PowerDNS API key.</dd>
<dt>PowerDNS Version</dt>
<dd>Your PowerDNS version number (eg. 4.7.0).</dd>
</dl>
<p>Find more details at
<a href="https://doc.powerdns.com/md/httpapi/README/">https://doc.powerdns.com/md/httpapi/README/</a>
</p>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
{% assets "js_validation" -%} {% assets "js_validation" -%}
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{%- endassets %} {%- endassets %}
{% endblock %} {% endblock %}

View File

@ -1,101 +1,109 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_settings" %} {% set active_page = "admin_settings" %}
{% block title %}<title>Zone Record Settings - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
DNS Records Settings - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">Zone Record Settings</h1>
Settings </div>
<small>Records</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Zone Record Settings</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Settings - Records </li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-5"> <div class="col-12 col-sm-6 col-lg-4">
<div class="card"> <form role="form" method="post">
<div class="card-header"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<h3 class="card-title">DNS record Settings</h3> <input type="hidden" name="create" value="{{ create }}">
</div> <div class="card card-outline card-primary shadow">
<form role="form" method="post"> <div class="card-header">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <h3 class="card-title">Settings Editor</h3>
<input type="hidden" name="create" value="{{ create }}"> </div>
<div class="card-body"> <!-- /.card-header -->
<table class="table table-bordered"> <div class="card-body">
<tr> <table class="table table-bordered">
<th style="width: 10px">#</th> <tr>
<th style="width: 40px">Record</th> <th>#</th>
<th>Forward Zone</th> <th>Record</th>
<th>Reverse Zone</th> <th>Forward Zone</th>
</tr> <th>Reverse Zone</th>
{% for record in f_records %} </tr>
<tr> {% for record in f_records %}
<td>{{ loop.index }}</td> <tr>
<td>{{ record }}</td> <td>{{ loop.index }}</td>
<td> <td>{{ record }}</td>
<input type="checkbox" id="fr_{{ record|lower }}" name="fr_{{ record|lower }}" <td>
class="checkbox" {% if f_records[record] %}checked{% endif %}> <input type="checkbox" id="fr_{{ record|lower }}"
</td> name="fr_{{ record|lower }}"
<td> class="checkbox" {% if f_records[record] %}checked{% endif %}>
<input type="checkbox" id="rr_{{ record|lower }}" name="rr_{{ record|lower }}" </td>
class="checkbox" {% if r_records[record] %}checked{% endif %}> <td>
</td> <input type="checkbox" id="rr_{{ record|lower }}"
</tr> name="rr_{{ record|lower }}"
{% endfor %} class="checkbox" {% if r_records[record] %}checked{% endif %}>
</table> </td>
</tr>
{% endfor %}
</table>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="submit" class="btn btn-primary float-right" title="Save Settings">
<i class="fa-solid fa-save"></i>&nbsp;Save Settings
</button>
</div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form>
</div>
<!-- /.col -->
<div class="col-12 col-sm-6 col-lg-8">
<div class="card card-outline card-secondary shadow">
<div class="card-header">
<h3 class="card-title">Settings Editor Help</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<p>Select record types you allow user to edit in the forward zone and reverse zone. Take a
look at
<a href="https://doc.powerdns.com/authoritative/appendices/types.html" target="_blank">PowerDNS
docs</a>
for
full list of supported record types.
</p>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div> </div>
<div class="card-footer"> <!-- /.row -->
<button type="submit" class="btn btn-primary float-right">
<i class="fa-solid fa-save"></i>&nbsp;Save
</button>
</div>
</form>
</div> </div>
</div> <!-- /.container-fluid -->
<div class="col-7"> </section>
<div class="card">
<div class="card-header">
<h3 class="card-title">Help</h3>
</div>
<div class="card-body">
<p>Select record types you allow user to edit in the forward zone and reverse zone. Take a look at
<a href="https://doc.powerdns.com/authoritative/appendices/types.html">PowerDNS docs</a> for
full list of supported record types.
</p>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
$('.checkbox').iCheck({ $('.checkbox').iCheck({
checkboxClass: 'icheckbox_square-blue', checkboxClass: 'icheckbox_square-blue',
increaseArea: '20%' increaseArea: '20%'
}) })
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,427 +1,424 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class> <html lang="en" class>
<head> <head>
{% block head %} {% block head %}
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="icon" href="{{ url_for('static', filename='img/favicon.png') }}"> <link rel="icon" href="{{ url_for('static', filename='img/favicon.png') }}">
{% block title %} {% block title %}<title>{{ SITE_NAME }}</title>{% endblock %}
<title> <link rel="stylesheet" href="{{ url_for('static', filename='assets/css/style.css') }}">
{{ SITE_NAME }} <link rel="stylesheet" href="{{ url_for('static', filename='assets/css/source_sans_pro.css') }}">
</title> <link rel="stylesheet" href="{{ url_for('static', filename='assets/css/roboto_mono.css') }}">
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Tell Safari to not recognise telephone numbers -->
<meta name="format-detection" content="telephone=no">
{% assets "css_main" -%}
<link rel="stylesheet" href="{{ ASSET_URL }}">{%- endassets %}
{% if SETTING.get('custom_css') %}
<link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}">
{% endif %}
{% block head_styles %}{% endblock %}
{% endblock %} {% endblock %}
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/style.css') }}">
<!-- Get Google Fonts we like -->
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/source_sans_pro.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/roboto_mono.css') }}">
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Tell Safari to not recognise telephone numbers -->
<meta name="format-detection" content="telephone=no">
{% assets "css_main" -%}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{%- endassets %}
{% if SETTING.get('custom_css') %}
<link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}">
{% endif %}
{% endblock %}
</head> </head>
<body class="hold-transition sidebar-mini {% if not SETTING.get('fullscreen_layout') %}layout-boxed{% endif %}"> <body class="hold-transition sidebar-mini {% if not SETTING.get('fullscreen_layout') %}layout-boxed{% endif %}">
{% set user_image_url = url_for('user.image', username=current_user.username) %} {% set user_image_url = url_for('user.image', username=current_user.username) %}
<div class="wrapper"> <div class="wrapper">
{% block pageheader %} {% block pageheader %}
<nav class="main-header navbar navbar-expand navbar-white navbar-light"> <nav class="main-header navbar navbar-expand navbar-white navbar-light">
<!-- Header Navbar: style can be found in header.less --> <!-- Header Navbar: style can be found in header.less -->
<!-- Sidebar toggle button--> <!-- Sidebar toggle button-->
<ul class="navbar-nav"> <ul class="navbar-nav">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" data-widget="pushmenu" href="#" role="button"> <a class="nav-link" data-widget="pushmenu" href="#" role="button">
<i class="fa-solid fa-bars"></i> <i class="fa-solid fa-bars"></i>
</a> </a>
</li> </li>
</ul> </ul>
<ul class="navbar-nav ml-auto"> <ul class="navbar-nav ml-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" data-widget="fullscreen" href="#" role="button"> <a class="nav-link" data-widget="fullscreen" href="#" role="button">
<i class="fa-solid fa-expand-arrows-alt"></i> <i class="fa-solid fa-expand-arrows-alt"></i>
</a> </a>
</li> </li>
</ul> </ul>
</nav> </nav>
{% endblock %} {% endblock %}
<!-- Left side column. contains the logo and sidebar --> <!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar sidebar-dark-primary"> <aside class="main-sidebar sidebar-dark-primary">
<!-- Logo --> <!-- Logo -->
<a href="{{ url_for('index.index') }}" class="brand-link"> <a href="{{ url_for('index.index') }}" class="brand-link">
<img src="{{ url_for('static', filename='img/favicon.png') }}" alt="PowerDNS-Admin FavIcon" class="brand-image img-circle elevation-3" style="opacity: .8"> <img src="{{ url_for('static', filename='img/favicon.png') }}" alt="PowerDNS-Admin FavIcon"
<span class="brand-text font-weight-light"> class="brand-image img-circle elevation-3" style="opacity: .8">
<span class="brand-text font-weight-light">
{% if SETTING.get('site_name') %} {% if SETTING.get('site_name') %}
<b>{{ SITE_NAME }}</b> <b>{{ SITE_NAME }}</b>
{% else %} {% else %}
<b>PowerDNS</b>-Admin <b>PowerDNS</b>-Admin
{% endif %} {% endif %}
</span> </span>
</a> </a>
<!-- sidebar: style can be found in sidebar.less --> <!-- sidebar: style can be found in sidebar.less -->
<div class="sidebar"> <div class="sidebar">
{% if current_user.id is defined %} {% if current_user.id is defined %}
<div class="user-panel mt-3 pb-3 mb-3 d-flex"> <div class="user-panel mt-3 pb-3 mb-3 d-flex">
<div class="image"> <div class="image">
<img src="{{ user_image_url }}" class="img-circle elevation-2" alt="User Image"> <img src="{{ user_image_url }}" class="img-circle elevation-2" alt="User Image">
</div> </div>
<div class="info"> <div class="info">
<a href="{{ url_for('user.profile') }}" class="d-block">{{ current_user.firstname }} {{ current_user.lastname }}</a> <p>{{ current_user.firstname }} {{ current_user.lastname }}</p>
</div> <span>
</div> <a href="{{ url_for('user.profile') }}"><i class="nav-icon fa-solid fa-user"></i> Edit Profile</a>
<!-- sidebar menu: : style can be found in sidebar.less --> &nbsp;|&nbsp;
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu"> <a href="{{ url_for('index.logout') }}"><i class="nav-icon fa-solid fa-sign-out-alt"></i> Logout</a>
<li class="{{ 'nav-item active' if active_page == 'user_profile' else 'nav-item' }}"> </span>
<a href="{{ url_for('user.profile') }}" class="nav-link"> </div>
<i class="nav-icon fa-solid fa-user"></i> </div>
<p>Profile</p> <!-- sidebar menu: : style can be found in sidebar.less -->
</a> <ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu">
</li> <li class="nav-header">Zone Management</li>
<li class="nav-item"> <li class="{{ 'nav-item active' if active_page == 'nav-item dashboard' else 'nav-item' }}">
<a href="{{ url_for('index.logout') }}" class="nav-link"> <a href="{{ url_for('dashboard.dashboard') }}" class="nav-link">
<i class="nav-icon fa-solid fa-sign-out-alt"></i> <i class="nav-icon fa-solid fa-tachometer-alt"></i>
<p>Logout</p> <p>Dashboard</p>
</a> </a>
</li> </li>
<li class="nav-header">Zone Management</li> {% if SETTING.get('allow_user_create_domain') or current_user.role.name in ['Administrator', 'Operator'] %}
<li class="{{ 'nav-item active' if active_page == 'nav-item dashboard' else 'nav-item' }}"> <li class="{{ 'nav-item active' if active_page == 'nav-item new_domain' else 'nav-item' }}">
<a href="{{ url_for('dashboard.dashboard') }}" class="nav-link"> <a href="{{ url_for('domain.add') }}" class="nav-link">
<i class="nav-icon fa-solid fa-tachometer-alt"></i> <i class="nav-icon fa-solid fa-plus"></i>
<p>Dashboard</p> <p>Create Zone</p>
</a> </a>
</li> </li>
{% if SETTING.get('allow_user_create_domain') or current_user.role.name in ['Administrator', 'Operator'] %} {% endif %}
<li class="{{ 'nav-item active' if active_page == 'nav-item new_domain' else 'nav-item' }}"> {% if SETTING.get('allow_user_remove_domain') or current_user.role.name in ['Administrator', 'Operator'] %}
<a href="{{ url_for('domain.add') }}" class="nav-link"> <li class="{{ 'nav-item active' if active_page == 'remove_domain' else 'nav-item' }}">
<i class="nav-icon fa-solid fa-plus"></i> <a href="{{ url_for('domain.remove') }}" class="nav-link">
<p>New Domain</p> <i class="nav-icon fa-solid fa-trash-alt"></i>
</a> <p>Remove Zone</p>
</li> </a>
{% endif %} </li>
{% if SETTING.get('allow_user_remove_domain') or current_user.role.name in ['Administrator', 'Operator'] %} {% endif %}
<li class="{{ 'nav-item active' if active_page == 'remove_domain' else 'nav-item' }}"> {% if current_user.role.name in ['Administrator', 'Operator'] %}
<a href="{{ url_for('domain.remove') }}" class="nav-link"> <li class="nav-header">Administration</li>
<i class="nav-icon fa-solid fa-trash-alt"></i> <li class="{{ 'nav-item active' if active_page == 'server_statistics' else 'nav-item' }}">
<p>Remove Domain</p> <a href="{{ url_for('admin.server_statistics') }}" class="nav-link">
</a> <i class="nav-icon fa-solid fa-chart-simple"></i>
</li> <p>Server Statistics</p>
{% endif %} </a>
{% if current_user.role.name in ['Administrator', 'Operator'] %} </li>
<li class="nav-header">Administration</li> <li class="{{ 'nav-item active' if active_page == 'server_configuration' else 'nav-item' }}">
<li class="{{ 'nav-item active' if active_page == 'admin_console' else 'nav-item' }}"> <a href="{{ url_for('admin.server_configuration') }}" class="nav-link">
<a href="{{ url_for('admin.pdns_stats') }}" class="nav-link"> <i class="nav-icon fa-solid fa-cog"></i>
<i class="nav-icon fa-solid fa-info-circle"></i> <p>Server Configuration</p>
<p>PowerDNS Info</p> </a>
</a> </li>
</li> <li class="{{ 'nav-item active' if active_page == 'admin_global_search' else 'nav-item' }}">
<li class="{{ 'nav-item active' if active_page == 'admin_global_search' else 'nav-item' }}"> <a href="{{ url_for('admin.global_search') }}" class="nav-link">
<a href="{{ url_for('admin.global_search') }}" class="nav-link"> <i class="nav-icon fa-solid fa-search"></i>
<i class="nav-icon fa-solid fa-search"></i> <p>Global Search</p>
<p>Global Search</p> </a>
</a> </li>
</li> <li class="{{ 'nav-item active' if active_page == 'admin_history' else 'nav-item' }}">
<li class="{{ 'nav-item active' if active_page == 'admin_history' else 'nav-item' }}"> <a href="{{ url_for('admin.history') }}" class="nav-link">
<a href="{{ url_for('admin.history') }}" class="nav-link"> <i class="nav-icon fa-solid fa-timeline"></i>
<i class="nav-icon fa-solid fa-calendar-alt"></i> <p>Activity</p>
<p>History</p> </a>
</a> </li>
</li> <li class="{{ 'nav-item active' if active_page == 'admin_domain_template' else 'nav-item' }}">
<li class="{{ 'nav-item active' if active_page == 'admin_domain_template' else 'nav-item' }}"> <a href="{{ url_for('admin.templates') }}" class="nav-link">
<a href="{{ url_for('admin.templates') }}" class="nav-link"> <i class="nav-icon fa-solid fa-clone"></i>
<i class="nav-icon fa-solid fa-clone"></i> <p>Zone Templates</p>
<p>Domain Templates</p> </a>
</a> </li>
</li> <li class="{{ 'nav-item active' if active_page == 'admin_accounts' else 'nav-item' }}">
<li class="{{ 'nav-item active' if active_page == 'admin_accounts' else 'nav-item' }}"> <a href="{{ url_for('admin.manage_account') }}" class="nav-link">
<a href="{{ url_for('admin.manage_account') }}" class="nav-link"> <i class="nav-icon fa-solid fa-users-rectangle"></i>
<i class="nav-icon fa-solid fa-industry"></i> <p>Accounts</p>
<p>Accounts</p> </a>
</a> </li>
</li> <li class="{{ 'nav-item active' if active_page == 'admin_users' else 'nav-item' }}">
<li class="{{ 'nav-item active' if active_page == 'admin_users' else 'nav-item' }}"> <a href="{{ url_for('admin.manage_user') }}" class="nav-link">
<a href="{{ url_for('admin.manage_user') }}" class="nav-link"> <i class="nav-icon fa-solid fa-users"></i>
<i class="nav-icon fa-solid fa-users"></i> <p>Users</p>
<p>Users</p> </a>
</a> </li>
</li> <li class="{{ 'nav-item active' if active_page == 'admin_keys' else 'nav-item' }}">
<li class="{{ 'nav-item active' if active_page == 'admin_keys' else 'nav-item' }}"> <a href="{{ url_for('admin.manage_keys') }}" class="nav-link">
<a href="{{ url_for('admin.manage_keys') }}" class="nav-link"> <i class="nav-icon fa-solid fa-key"></i>
<i class="nav-icon fa-solid fa-key"></i> <p>API Keys</p>
<p>API Keys</p> </a>
</a> </li>
</li> <li class="{{ 'nav-item active' if active_page == 'admin_settings' else 'nav-item' }}">
<li class="{{ 'nav-item active' if active_page == 'admin_settings' else 'nav-item' }}"> <a href="#" class="nav-link">
<a href="#" class="nav-link"> <i class="nav-icon fa-solid fa-cog"></i>
<i class="nav-icon fa-solid fa-cog"></i> <p>
<p> Settings
Settings <i class="right fa-solid fa-angle-left"></i>
<i class="right fa-solid fa-angle-left"></i> </p>
</p> </a>
</a> <ul class="nav nav-treeview"
<ul class="nav nav-treeview" {% if active_page == 'admin_settings' %}style="display: block;"{% endif %}> {% if active_page == 'admin_settings' %}style="display: block;"{% endif %}>
<li class="nav-item"> <li class="nav-item">
<a href="{{ url_for('admin.setting_basic') }}" class="nav-link"> <a href="{{ url_for('admin.setting_basic') }}" class="nav-link">
<i class="nav-icon fa-solid fa-circle"></i> <i class="nav-icon fa-solid fa-gears"></i>
<p>Basic</p> <p>Basic</p>
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a href="{{ url_for('admin.setting_records') }}" class="nav-link"> <a href="{{ url_for('admin.setting_records') }}" class="nav-link">
<i class="nav-icon fa-solid fa-circle"></i> <i class="nav-icon fa-regular fa-rectangle-list"></i>
<p>Records</p> <p>Zone Records</p>
</a> </a>
</li> </li>
{% if current_user.role.name == 'Administrator' %} {% if current_user.role.name == 'Administrator' %}
<li class="nav-item"> <li class="nav-item">
<a href="{{ url_for('admin.setting_pdns') }}" class="nav-link"> <a href="{{ url_for('admin.setting_pdns') }}" class="nav-link">
<i class="nav-icon fa-solid fa-circle"></i> <i class="nav-icon fa-solid fa-server"></i>
<p>PowerDNS Connection</p> <p>Server</p>
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a href="{{ url_for('admin.setting_authentication') }}" class="nav-link"> <a href="{{ url_for('admin.setting_authentication') }}" class="nav-link">
<i class="nav-icon fa-solid fa-circle"></i> <i class="nav-icon fa-solid fa-user-shield"></i>
<p>Authentication</p> <p>Authentication</p>
</a> </a>
</li> </li>
{% endif %}
</ul>
</li>
{% elif SETTING.get('allow_user_view_history') %}
<li class="nav-header">Administration</li>
<li class="{{ 'nav-item active' if active_page == 'admin_history' else 'nav-item' }}">
<a href="{{ url_for('admin.history') }}" class="nav-link">
<i class="nav-icon fa-solid fa-calendar-alt"></i>
<p>History</p>
</a>
</li>
{% endif %}
</ul>
{% endif %} {% endif %}
</ul>
</li>
{% elif SETTING.get('allow_user_view_history') %}
<li class="nav-header">Administration</li>
<li class="{{ 'nav-item active' if active_page == 'admin_history' else 'nav-item' }}">
<a href="{{ url_for('admin.history') }}" class="nav-link">
<i class="nav-icon fa-solid fa-calendar-alt"></i>
<p>History</p>
</a>
</li>
{% endif %}
</ul>
{% endif %}
</div>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
{% block dashboard_stat %}
<!-- Content Header (Page header) -->
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">
Dashboard
<small>Control panel</small>
</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
<li class="breadcrumb-item active">Dashboard</li>
</ol>
</div>
</div> </div>
</div> <!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
{% block dashboard_stat %}
<!-- Content Header (Page header) -->
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">
Dashboard
<small>Control panel</small>
</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
<li class="breadcrumb-item active">Dashboard</li>
</ol>
</div>
</div>
</div>
</div>
{% endblock %}
{% block content %}
{% endblock %}
</div> </div>
{% endblock %} <!-- /.content-wrapper -->
{% block content %} <footer class="main-footer">
{% endblock %} <strong><a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></strong> - A PowerDNS web
</div> interface with advanced features.
<!-- /.content-wrapper --> </footer>
<footer class="main-footer">
<strong><a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></strong> - A PowerDNS web interface with advanced features.
</footer>
</div> </div>
<!-- ./wrapper --> <!-- ./wrapper -->
<script type="text/javascript"> <script type="text/javascript">
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }}; $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script> var PDNS_VERSION = '{{ pdns_version }}';
<script type="text/javascript">
var PDNS_VERSION = '{{ pdns_version }}'
</script> </script>
{% block scripts %} {% block scripts %}
{% assets "js_main" -%} {% assets "js_main" -%}
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% if SETTING.get('warn_session_timeout') and current_user.is_authenticated %} {% if SETTING.get('warn_session_timeout') and current_user.is_authenticated %}
<script> <script>
// close the session warning popup when receive // close the session warning popup when receive
// a boradcast message // a boradcast message
var bc = new BroadcastChannel('powerdnsadmin'); var bc = new BroadcastChannel('powerdnsadmin');
bc.addEventListener('message', function (e) { bc.addEventListener('message', function (e) {
if (e.data == 'close_session_timeout_modal'){ if (e.data == 'close_session_timeout_modal') {
$("#modal_session_warning").modal('hide'); $("#modal_session_warning").modal('hide');
} }
}); });
// Stay Signed In button click event // Stay Signed In button click event
$(document.body).on("click", ".button_stay_signed_in", function (e) { $(document.body).on("click", ".button_stay_signed_in", function (e) {
$.get({ $.get({
url: $.jTimeout().options.extendUrl, url: $.jTimeout().options.extendUrl,
cache: false, cache: false,
success: function(){ success: function () {
$.jTimeout().resetExpiration(); $.jTimeout().resetExpiration();
} }
}); });
$.jTimeout().options.onClickExtend(); $.jTimeout().options.onClickExtend();
}); });
// Sign Out button click event // Sign Out button click event
$(document.body).on("click", ".button_sign_out", function (e) { $(document.body).on("click", ".button_sign_out", function (e) {
window.location.replace("{{ url_for('index.logout') }}"); window.location.replace("{{ url_for('index.logout') }}");
}); });
// Things happen when session warning popup shown // Things happen when session warning popup shown
$(document).on('show.bs.modal','#modal_session_warning', function () { $(document).on('show.bs.modal', '#modal_session_warning', function () {
var secondsLeft = jTimeout.getSecondsTillExpiration(); var secondsLeft = jTimeout.getSecondsTillExpiration();
var t = timer($('#modal-time'), secondsLeft); var t = timer($('#modal-time'), secondsLeft);
$(this).on('hidden.bs.modal', function () { $(this).on('hidden.bs.modal', function () {
clearInterval(t); clearInterval(t);
$('#modal-time').text(""); $('#modal-time').text("");
$(this).off('hidden.bs.modal'); $(this).off('hidden.bs.modal');
}); });
}); });
// jTimeout definition // jTimeout definition
$(function(){ $(function () {
$.jTimeout({ $.jTimeout({
flashTitle: true, flashTitle: true,
flashTitleSpeed: 500, flashTitleSpeed: 500,
flashingTitleText: '**WARNING**', flashingTitleText: '**WARNING**',
originalTitle: document.title, originalTitle: document.title,
timeoutAfter: {{ SETTING.get('session_timeout')|int * 60 }}, timeoutAfter: {{ SETTING.get('session_timeout')|int * 60 }},
secondsPrior: 60, secondsPrior: 60,
heartbeat: 1, heartbeat: 1,
extendOnMouseMove: true, extendOnMouseMove: true,
mouseDebounce: 30, mouseDebounce: 30,
extendUrl: '{{ url_for("index.ping") }}', extendUrl: '{{ url_for("index.ping") }}',
logoutUrl: '{{ url_for("index.logout") }}', logoutUrl: '{{ url_for("index.logout") }}',
loginUrl: '{{ url_for("index.login") }}', loginUrl: '{{ url_for("index.login") }}',
onClickExtend: function(){ onClickExtend: function () {
// broadcast a message to tell other tabes // broadcast a message to tell other tabes
// close the session warning popup // close the session warning popup
var bc = new BroadcastChannel('powerdnsadmin'); var bc = new BroadcastChannel('powerdnsadmin');
bc.postMessage('close_session_timeout_modal'); bc.postMessage('close_session_timeout_modal');
}, },
onMouseMove: function(){ onMouseMove: function () {
// if the mouse is moving while popup is present, we // if the mouse is moving while popup is present, we
// don't extend the session. // don't extend the session.
if (!$('#modal_session_warning').hasClass('in')) { if (!$('#modal_session_warning').hasClass('in')) {
$.get({ $.get({
url: $.jTimeout().options.extendUrl, url: $.jTimeout().options.extendUrl,
cache: false, cache: false,
success: function () { success: function () {
$.jTimeout().resetExpiration(); $.jTimeout().resetExpiration();
} }
}); });
} }
}, },
onTimeout: function(jTimeout){ onTimeout: function (jTimeout) {
window.location.replace("{{ url_for('index.logout') }}"); window.location.replace("{{ url_for('index.logout') }}");
}, },
onPriorCallback: function(jTimeout){ onPriorCallback: function (jTimeout) {
$("#modal_session_warning").modal('show');; $("#modal_session_warning").modal('show');
}, ;
},
onSessionExtended:function(jTimeout){ onSessionExtended: function (jTimeout) {
$("#modal_session_warning").modal('hide'); $("#modal_session_warning").modal('hide');
} }
}); });
}); });
function showErrorModal(message) { function showErrorModal(message) {
var modal = $('#modal_error'); var modal = $('#modal_error');
modal.find('.modal-body p').text(message); modal.find('.modal-body p').text(message);
modal.modal('show'); modal.modal('show');
} }
function showSuccessModal(message) { function showSuccessModal(message) {
var modal = $("#modal_success"); var modal = $("#modal_success");
modal.find('.modal-body p').text(message); modal.find('.modal-body p').text(message);
modal.modal('show'); modal.modal('show');
} }
</script> </script>
{% endif %} {% endif %}
{%- endassets %} {%- endassets %}
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
{% endblock %} {% endblock %}
{% block defaultmodals %} {% block defaultmodals %}
<div class="modal fade modal-danger" id="modal_error"> <div class="modal fade modal-danger" id="modal_error">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Error</h4> <h4 class="modal-title">Error</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p></p> <p></p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal"> <button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
Close Close
</button> </button>
</div> </div>
</div>
</div>
</div> </div>
</div> <!-- /.modal -->
</div> <div class="modal fade modal-success" id="modal_success">
<!-- /.modal --> <div class="modal-dialog">
<div class="modal fade modal-success" id="modal_success"> <div class="modal-content">
<div class="modal-dialog"> <div class="modal-header">
<div class="modal-content"> <h4 class="modal-title">Success</h4>
<div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<h4 class="modal-title">Success</h4> <span aria-hidden="true">&times;</span>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> </button>
<span aria-hidden="true">&times;</span> </div>
</button> <div class="modal-body">
</div> <p></p>
<div class="modal-body"> </div>
<p></p> <div class="modal-footer">
</div> <button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
<div class="modal-footer"> Close
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal"> </button>
Close </div>
</button> </div>
</div> </div>
</div> </div>
</div> <div class="modal fade modal-warning" data-backdrop="static" id="modal_session_warning">
</div> <div class="modal-dialog">
<div class="modal fade modal-warning" data-backdrop="static" id="modal_session_warning"> <div class="modal-content">
<div class="modal-dialog"> <div class="modal-header">
<div class="modal-content"> <h4 class="modal-title">Session timeout warning</h4>
<div class="modal-header"> </div>
<h4 class="modal-title">Session timeout warning</h4> <div class="modal-body">
</div> <p>Your session is about to expire. You will be automatically signed out in</p>
<div class="modal-body"> <h3><span id="modal-time"></span></h3>
<p>Your session is about to expire. You will be automatically signed out in</p> <p>To coninue your ssession, select <strong>Stay Signed In</strong></p>
<h3><span id="modal-time"></span></h3> </div>
<p>To coninue your ssession, select <strong>Stay Signed In</strong></p> <div class="modal-footer">
</div> <button type="button" class="btn btn-success float-right button_stay_signed_in"
<div class="modal-footer"> data-dismiss="modal">
<button type="button" class="btn btn-success float-right button_stay_signed_in" data-dismiss="modal"> Stay Signed In
Stay Signed In </button>
</button> <button type="button" class="btn btn-danger float-left button_sign_out" data-dismiss="modal">
<button type="button" class="btn btn-danger float-left button_sign_out" data-dismiss="modal"> Sign Out
Sign Out </button>
</button> </div>
</div> </div>
</div>
</div> </div>
</div>
</div>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
{% endblock %} {% endblock %}

View File

@ -1,418 +1,431 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}<title>Dashboard - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
Dashboard - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">
Dashboard Dashboard
<small>Info</small> </h1>
</h1> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% import 'applied_change_macro.html' as applied_change_macro %} {% import 'applied_change_macro.html' as applied_change_macro %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
<div class="row">
<div class="col-3">
<div class="card">
<div class="card-header">
<h3 class="card-title">Statistics</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-6">
<!-- small box -->
<div class="small-box bg-info">
<div class="inner">
<h3>{{ domain_count }}</h3>
<p>{% if domain_count > 1 %}Domains{% else %}Domain{% endif %}</p>
</div>
<div class="icon">
<i class="fa-solid fa-book"></i>
</div>
</div>
</div>
{% if current_user.role.name in ['Administrator', 'Operator'] %}
<div class="col-6">
<a href="{{ url_for('admin.manage_user') }}">
<div class="small-box bg-green">
<div class="inner">
<h3>{{ user_num }}</h3>
<p>{% if user_num > 1 %}Users{% else %}User{% endif %}</p>
</div>
<div class="icon">
<i class="fa-solid fa-users"></i>
</div>
</div>
</a>
</div>
{% endif %}
</div>
<div class="row">
<div class="col-6">
<a href="{{ url_for('admin.history') }}">
<div class="small-box bg-green">
<div class="inner">
<h3>{{ history_number }}</h3>
<p>{% if history_number > 1 %}Histories{% else %}History{% endif %}</p>
</div>
<div class="icon">
<i class="fa-solid fa-calendar"></i>
</div>
</div>
</a>
</div>
{% if current_user.role.name in ['Administrator', 'Operator'] %}
<div class="col-6">
<a href="{{ url_for('admin.pdns_stats') }}">
<div class="small-box bg-green">
<div class="inner">
<h3><span style="font-size: 18px">{{ uptime|display_second_to_time }}</span></h3>
<p>Uptime</p>
</div>
<div class="icon">
<i class="fa-solid fa-clock"></i>
</div>
</div>
</a>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<div class="col-9">
<div class="card">
<div class="card-header">
<h3 class="card-title">Recent History</h3>
</div>
<div class="card-body">
<table id="tbl_history" class="table table-bordered table-striped">
<thead>
<tr>
<th>Changed By</th>
<th>Content</th>
<th>Time</th>
<th>Detail</th>
</tr>
</thead>
<tbody>
{% for history in histories %}
<tr class="odd">
<td>{{ history.history.created_by }}</td>
<td>{{ history.history.msg }}</td>
<td>{{ history.history.created_on }}</td>
<td width="6%">
<div id="history-info-div-{{ loop.index0 }}" style="display: none;">
{{ history.detailed_msg | safe }}
{% if history.change_set %}
<div class="content">
<div id="change_index_definition"></div>
{% call applied_change_macro.applied_change_template(history.change_set) %}
{% endcall %}
</div>
{% endif %}
</div>
<button type="button" class="btn btn-sm btn-primary history-info-button"
{% if history.detailed_msg == "" and history.change_set is none %}
style="visibility: hidden;"
{% endif %} value="{{ loop.index0 }}">
<i class="fa-solid fa-info-circle"></i>&nbsp;Info
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endif %}
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs card-header-tabs" id="custom-content-below-tab" role="tablist">
<li class="nav-item">
<a class="nav-link active" href="#tab_{{custom_boxes.order[0]}}" data-toggle="pill" role="tab">
Hosted Domains <b>{{custom_boxes.boxes[custom_boxes.order[0]][0]}}</b>
</a>
</li>
{% for boxId in custom_boxes.order[1:] %}
<li class="nav-item">
<a class="nav-link" href="#tab_{{boxId}}" data-toggle="pill" role="tab">Hosted Domains <b>{{custom_boxes.boxes[boxId][0]}}</b></a>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="card-body">
<div class="tab-content">
{% for boxId in custom_boxes.order %}
<div class="tab-pane fade show" id='tab_{{boxId}}'>
<div class="card-header">
<h3 class="card-title">Hosted Domains <b>{{custom_boxes.boxes[boxId][0]}}</b></h3>
{% if show_bg_domain_button %}
<button type="button" class="btn btn-primary refresh-bg-button float-right">
<i class="fa-solid fa-sync"></i>
&nbsp;Sync Domains
</button>
{% endif %}
</div>
<div class="card-body">
<table id='tbl_domain_list_{{boxId}}' class="table table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>DNSSEC</th>
<th>Type</th>
<th>Serial</th>
<th>Primary</th>
<th>Account</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="card card-outline card-secondary shadow">
<div class="card-header">
<h3 class="card-title">Statistics</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="row">
<div class="col-6 col-sm-3">
<div class="small-box bg-info">
<div class="inner">
<h3>{{ domain_count }}</h3>
<p>{% if domain_count > 1 %}Domains{% else %}Domain{% endif %}</p>
</div>
<div class="icon">
<i class="fa-solid fa-book"></i>
</div>
</div>
</div>
<div class="col-6 col-sm-3">
<a href="{{ url_for('admin.history') }}">
<div class="small-box bg-green">
<div class="inner">
<h3>{{ history_number }}</h3>
<p>{% if history_number > 1 %}Histories{% else %}
History{% endif %}</p>
</div>
<div class="icon">
<i class="fa-solid fa-calendar"></i>
</div>
</div>
</a>
</div>
{% if current_user.role.name in ['Administrator', 'Operator'] %}
<div class="col-6 col-sm-3">
<a href="{{ url_for('admin.manage_user') }}">
<div class="small-box bg-green">
<div class="inner">
<h3>{{ user_num }}</h3>
<p>{% if user_num > 1 %}Users{% else %}User{% endif %}</p>
</div>
<div class="icon">
<i class="fa-solid fa-users"></i>
</div>
</div>
</a>
</div>
<div class="col-6 col-sm-3">
<a href="{{ url_for('admin.server_statistics') }}">
<div class="small-box bg-green">
<div class="inner">
<h3><span
style="font-size: 18px">{{ uptime|display_second_to_time }}</span>
</h3>
<p>Uptime</p>
</div>
<div class="icon">
<i class="fa-solid fa-clock"></i>
</div>
</div>
</a>
</div>
{% endif %}
</div>
<!-- /.row -->
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
<div class="row">
<div class="col-12">
<div class="card card-outline card-secondary shadow">
<div class="card-header">
<h3 class="card-title">Recent History</h3>
</div>
<!-- /.card-header -->
<div class="card-body table-responsive records p-0">
<table class="table table-striped table-hover table-sm records">
<thead>
<tr>
<th>Log Message</th>
<th>Timestamp</th>
<th>User</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for history in histories %}
<tr class="odd">
<td>{{ history.history.msg }}</td>
<td>{{ history.history.created_on | format_datetime_local }}</td>
<td>{{ history.history.created_by }}</td>
<td>
<div id="history-info-div-{{ loop.index0 }}" style="display: none;">
{{ history.detailed_msg | safe }}
{% if history.change_set %}
<div class="content">
<div id="change_index_definition"></div>
{% call applied_change_macro.applied_change_template(history.change_set) %}
{% endcall %}
</div>
{% endif %}
</div>
<button type="button" class="btn btn-sm btn-primary history-info-button"
title="View Additional Information"
{% if history.detailed_msg == "" and history.change_set is none %}
style="visibility: hidden;"
{% endif %} value="{{ loop.index0 }}">
<i class="fa-solid fa-info-circle"></i>
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
{% endif %}
<div class="row">
<div class="col-12">
<div class="card card-outline card-primary shadow">
<div class="card-header">
<h3 class="card-title mb-2">
Zones
{% if show_bg_domain_button %}
<button type="button" title="Synchronize Zones"
class="btn btn-tool refresh-bg-button">
<i class="fa-solid fa-sync"></i>
</button>
{% endif %}
</h3>
<div class="card-tools">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs card-header-tabs" id="custom-content-below-tab"
role="tablist">
<li class="nav-item">
<a class="nav-link active" href="#tab_{{ custom_boxes.order[0] }}"
data-toggle="pill" role="tab">
Zones <b>{{ custom_boxes.boxes[custom_boxes.order[0]][0] }}</b>
</a>
</li>
{% for boxId in custom_boxes.order[1:] %}
<li class="nav-item">
<a class="nav-link" href="#tab_{{ boxId }}" data-toggle="pill"
role="tab">
Zones <b>{{ custom_boxes.boxes[boxId][0] }}</b>
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
<!-- /.card-header -->
<div class="card-body p-0">
<div class="tab-content">
{% for boxId in custom_boxes.order %}
<div class="tab-pane show" id='tab_{{ boxId }}'>
<div class="card-body table-responsive records p-0 pt-2">
<table id='tbl_domain_list_{{ boxId }}'
class="table table-striped table-hover table-sm records">
<thead>
<tr>
<th>Name</th>
<th>DNSSEC</th>
<th>Type</th>
<th>Serial</th>
<th>Primary</th>
<th>Account</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.tab-pane -->
{% endfor %}
</div>
<!-- /.tab-content -->
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div> </div>
</div> <!-- /.container-fluid -->
</div> </section>
</section> {% endblock %}
{% block head_styles %}
<style>
/* Page Specific Overrides */
table.records tbody td:first-of-type { text-align: left; }
</style>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
//SYBPATCH START// //SYBPATCH START//
function setUpDomainList(id ,url){ function setUpDomainList(id, url) {
$(id).DataTable({ $(id).DataTable({
"paging" : true, "paging": true,
"lengthChange" : true, "lengthChange": true,
language: { language: {
searchPlaceholder: "Use ^ and $ for start and end", searchPlaceholder: "Use ^ and $ for start and end",
},
"searching" : true,
"ordering" : true,
"columnDefs": [
{ "orderable": false, "targets": [-1] }
{% if current_user.role.name not in ['Administrator', 'Operator'] %},{ "visible": false, "targets": [-2] }{% endif %}
],
"processing" : true,
"serverSide" : true,
"ajax" : url,
"info" : false,
"autoWidth" : false,
{% if SETTING.get('default_domain_table_size')|string in ['10','25','50','100'] %}
"lengthMenu": [ [10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"]],
{% else %}
"lengthMenu": [ [10, 25, 50, 100, {{ SETTING.get('default_domain_table_size') }}, -1],
[10, 25, 50, 100, {{ SETTING.get('default_domain_table_size') }}, "All"]],
{% endif %}
"pageLength": {{ SETTING.get('default_domain_table_size') }}
});
}
$('#tab_{{custom_boxes.order[0]}}').addClass( "active" );
{% for boxId in custom_boxes.order %}
setUpDomainList("#tbl_domain_list_{{boxId}}", "{{url_for('dashboard.domains_custom',boxId=boxId)}}");
{% endfor %}
//SYBPATCH END//
// set up history data table
$("#tbl_history").DataTable({
"paging" : false,
"lengthChange" : false,
"searching" : false,
"ordering" : false,
"info" : false,
"autoWidth" : true,
"columnDefs": [
{
"render": function ( data, type, row ) {
return moment.utc(data).local().format('YYYY-MM-DD HH:mm:ss');
}, },
"targets": 2 "searching": true,
} "ordering": true,
] "columnDefs": [
}); {"orderable": false, "targets": [-1]}
],
"processing": true,
"serverSide": true,
"ajax": url,
"info": false,
"autoWidth": false,
{% if SETTING.get('default_domain_table_size')|string in ['10','25','50','100'] %}
"lengthMenu": [[10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"]],
{% else %}
"lengthMenu": [[10, 25, 50, 100, {{ SETTING.get('default_domain_table_size') }}, -1],
[10, 25, 50, 100, {{ SETTING.get('default_domain_table_size') }}, "All"]],
{% endif %}
"pageLength": {{ SETTING.get('default_domain_table_size') }}
});
}
$(document.body).on('click', '.history-info-button', function () { $('#tab_{{custom_boxes.order[0]}}').addClass("active");
var modal = $("#modal_history_info"); {% for boxId in custom_boxes.order %}
var history_id = $(this).val(); setUpDomainList("#tbl_domain_list_{{boxId}}", "{{url_for('dashboard.domains_custom',boxId=boxId)}}");
var info = $("#history-info-div-" + history_id).html(); {% endfor %}
$('#modal-info-content').html(info); //SYBPATCH END//
modal.modal('show');
});
$(document.body).on('click', '.refresh-bg-button', function() { $(document.body).on('click', '.history-info-button', function () {
var modal = $("#modal_bg_reload"); var modal = $("#modal_history_info");
modal.modal('show'); var history_id = $(this).val();
reload_domains($SCRIPT_ROOT + '/dashboard/domains-updater'); var info = $("#history-info-div-" + history_id).html();
}); $('#modal-info-content').html(info);
modal.modal('show');
});
$(document.body).on("click", ".button_template", function (e) { $(document.body).on('click', '.refresh-bg-button', function () {
var modal = $("#modal_template"); var modal = $("#modal_bg_reload");
var domain = $(this).prop('id'); modal.modal('show');
var form = " <label for=\"template_name\">Template name</label> \ reload_domains($SCRIPT_ROOT + '/dashboard/domains-updater');
});
$(document.body).on("click", ".button_template", function (e) {
var modal = $("#modal_template");
var domain = $(this).prop('id');
var form = " <label for=\"template_name\">Template name</label> \
<input type=\"text\" class=\"form-control\" name=\"template_name\" id=\"template_name\" placeholder=\"Enter a valid template name (required)\"> \ <input type=\"text\" class=\"form-control\" name=\"template_name\" id=\"template_name\" placeholder=\"Enter a valid template name (required)\"> \
<label for=\"template_description\">Template description</label> \ <label for=\"template_description\">Template description</label> \
<input type=\"text\" class=\"form-control\" name=\"template_description\" id=\"template_description\" placeholder=\"Enter a template description (optional)\"> \ <input type=\"text\" class=\"form-control\" name=\"template_description\" id=\"template_description\" placeholder=\"Enter a template description (optional)\"> \
<input id=\"domain\" name=\"domain\" type=\"hidden\" value=\""+domain+"\"> \ <input id=\"domain\" name=\"domain\" type=\"hidden\" value=\"" + domain + "\"> \
"; ";
modal.find('.modal-body p').html(form); modal.find('.modal-body p').html(form);
modal.find('#button_save').click(function() { modal.find('#button_save').click(function () {
var data = {'_csrf_token': '{{ csrf_token() }}'}; var data = {'_csrf_token': '{{ csrf_token() }}'};
data['name'] = modal.find('#template_name').val(); data['name'] = modal.find('#template_name').val();
data['description'] = modal.find('#template_description').val(); data['description'] = modal.find('#template_description').val();
data['domain'] = modal.find('#domain').val(); data['domain'] = modal.find('#domain').val();
applyChanges(data, "{{ url_for('admin.create_template_from_zone') }}", true); applyChanges(data, "{{ url_for('admin.create_template_from_zone') }}", true);
modal.modal('hide'); modal.modal('hide');
}) })
modal.find('#button_close').click(function() { modal.find('#button_close').click(function () {
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
}); });
{% if current_user.role.name in ['Administrator', 'Operator'] or not SETTING.get('dnssec_admins_only') %} {% if current_user.role.name in ['Administrator', 'Operator'] or not SETTING.get('dnssec_admins_only') %}
$(document.body).on("click", ".button_dnssec", function() { $(document.body).on("click", ".button_dnssec", function () {
var domain = $(this).prop('id'); var domain = $(this).prop('id');
getdnssec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec', domain); getdnssec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec', domain);
}); });
$(document.body).on("click", ".button_dnssec_enable", function() { $(document.body).on("click", ".button_dnssec_enable", function () {
var domain = $(this).prop('id'); var domain = $(this).prop('id');
enable_dns_sec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec/enable', '{{ csrf_token() }}'); enable_dns_sec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec/enable', '{{ csrf_token() }}');
}); });
$(document.body).on("click", ".button_dnssec_disable", function() { $(document.body).on("click", ".button_dnssec_disable", function () {
var domain = $(this).prop('id'); var domain = $(this).prop('id');
enable_dns_sec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec/disable', '{{ csrf_token() }}'); enable_dns_sec($SCRIPT_ROOT + '/domain/' + domain + '/dnssec/disable', '{{ csrf_token() }}');
}); });
{% endif %} {% endif %}
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<div class="modal fade" id="modal_history_info"> <div class="modal fade" id="modal_history_info">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">History Details</h4> <h4 class="modal-title">History Details</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div id="modal-info-content"> <div id="modal-info-content">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
Close
</button>
</div>
</div>
</div> </div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
Close
</button>
</div>
</div> </div>
</div>
</div>
<div class="modal fade modal-primary" id="modal_template"> <div class="modal fade modal-primary" id="modal_template">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Clone to template</h4> <h4 class="modal-title">Clone to template</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p></p> <p></p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary float-right" id="button_close" data-dismiss="modal"> <button type="button" class="btn btn-secondary float-right" id="button_close" data-dismiss="modal">
Close Close
</button> </button>
<button type="button" class="btn btn-primary float-right" id="button_save"> <button type="button" class="btn btn-primary float-right" id="button_save">
Save Save
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" id="modal_dnssec_info"> <div class="modal fade" id="modal_dnssec_info">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">DNSSEC</h4> <h4 class="modal-title">DNSSEC</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p></p> <p></p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal"> <button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
Close Close
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" id="modal_bg_reload"> <div class="modal fade" id="modal_bg_reload">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Sync Domains from backend</h4> <h4 class="modal-title">Sync Domains from backend</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div id="modal_bg_reload_content"> <div id="modal_bg_reload_content">
<i class="fa fa-refresh fa-spin"></i> Update in progress .. <i class="fa fa-refresh fa-spin"></i> Update in progress ..
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
Close
</button>
</div>
</div>
</div> </div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary float-right" data-dismiss="modal">
Close
</button>
</div>
</div> </div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,17 +1,23 @@
{% macro name(domain) %} {% macro name(domain) %}
<a href="{{ url_for('domain.domain', domain_name=domain.name) }}"><strong>{{ domain.name | pretty_domain_name }}</strong></a> <a href="{{ url_for('domain.domain', domain_name=domain.name) }}"><strong>{{ domain.name | pretty_domain_name }}</strong></a>
{% endmacro %} {% endmacro %}
{% macro dnssec(domain) %} {% macro dnssec(domain) %}
{% if domain.dnssec %} {% if domain.dnssec %}
<td><span style="cursor:pointer" class="badge badge-success button_dnssec" id="{{ domain.name }}"><i class="fa-solid fa-lock"></i>&nbsp;Enabled</span></td> <td>
{% else %} <button class="btn badge btn-success button_dnssec" title="Edit DNSSEC" id="{{ domain.name }}"><i
<td><span style="cursor:pointer" class="badge badge-danger button_dnssec" id="{{ domain.name }}"><i class="fa-solid fa-lock-open"></i>&nbsp;Disabled</span></td> class="fa-solid fa-lock"></i></button>
{% endif %} </td>
{% else %}
<td>
<button class="btn badge btn-danger button_dnssec" title="Enable DNSSEC" id="{{ domain.name }}"><i
class="fa-solid fa-lock-open"></i></button>
</td>
{% endif %}
{% endmacro %} {% endmacro %}
{% macro type(domain) %} {% macro type(domain) %}
{{ domain.type }} {{ domain.type | format_zone_type }}
{% endmacro %} {% endmacro %}
{% macro serial(domain) %} {% macro serial(domain) %}
@ -19,61 +25,55 @@
{% endmacro %} {% endmacro %}
{% macro master(domain) %} {% macro master(domain) %}
{% if domain.master == '[]'%}-{% else %}{{ domain.master | display_master_name }}{% endif %} {% if domain.master == '[]' %}N/A{% else %}{{ domain.master | display_master_name }}{% endif %}
{% endmacro %} {% endmacro %}
{% macro account(domain) %} {% macro account(domain) %}
{% if current_user.role.name in ['Administrator', 'Operator'] %} {% if current_user.role.name in ['Administrator', 'Operator'] %}
{{ domain.account.name if domain.account else '-' }} {{ domain.account.name if domain.account else 'None' }}
{% else %}
None
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}
{% macro actions(domain) %} {% macro actions(domain) %}
<td width="6%"> <td>
<div class="dropdown"> <div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown"
<i class="fa-solid fa-bars"></i>&nbsp;Actions aria-haspopup="true" aria-expanded="false">
</button> <i class="fa-solid fa-bars"></i>
<div class="dropdown-menu" aria-labelledby="dropdownMenu"> </button>
{% if current_user.role.name in ['Administrator', 'Operator'] %} <div class="dropdown-menu" aria-labelledby="dropdownMenu">
<button class="dropdown-item btn-success button_template" id="{{ domain.name }}" type="button"> <button class="dropdown-item btn-success" type="button"
<i class="fa-solid fa-clone"></i>&nbsp;Clone to Template onclick="window.location.href='{{ url_for('domain.domain', domain_name=domain.name) }}'">
</button> <i class="fa-solid fa-pencil"></i>&nbsp;Edit Records
<button class="dropdown-item btn-success" type="button" onclick="window.location.href='{{ url_for('domain.domain', domain_name=domain.name) }}'"> </button>
<i class="fa-solid fa-cog"></i>&nbsp;Manage Domain
</button> {% if current_user.role.name in ['Administrator', 'Operator'] %}
<button class="dropdown-item btn-danger" type="button" onclick="window.location.href='{{ url_for('domain.setting', domain_name=domain.name) }}'"> <button class="dropdown-item btn-danger" type="button"
<i class="fa-solid fa-cog"></i>&nbsp;Admin Settings onclick="window.location.href='{{ url_for('domain.setting', domain_name=domain.name) }}'">
</button> <i class="fa-solid fa-cog"></i>&nbsp;Zone Settings
<button class="dropdown-item btn-primary" type="button" onclick="window.location.href='{{ url_for('domain.changelog', domain_name=domain.name) }}'"> </button>
<i class="fa-solid fa-history" aria-hidden="true"></i>&nbsp;Domain Changelog <button class="dropdown-item btn-success button_template" id="{{ domain.name }}" type="button">
</button> <i class="fa-solid fa-clone"></i>&nbsp;Create Template
<div class="dropdown-divider"></div> </button>
<button type="button"class="dropdown-item btn-secondary button_delete" onclick="window.location.href='{{ url_for('domain.remove') }}'"> {% endif %}
<font color="red">
<i class="fa-solid fa-trash"></i>&nbsp;Remove Domain {% if current_user.role.name in ['Administrator', 'Operator'] or allow_user_view_history %}
</font> <button class="dropdown-item btn-primary" type="button"
</button> onclick="window.location.href='{{ url_for('domain.changelog', domain_name=domain.name) }}'">
</div> <i class="fa-solid fa-history" aria-hidden="true"></i>&nbsp;Zone Changelog
{% else %} </button>
<button class="dropdown-item btn-success" type="button" onclick="window.location.href='{{ url_for('domain.domain', domain_name=domain.name) }}'"> {% endif %}
<i class="fa-solid fa-cog"></i>&nbsp;Manage Domain
</button> {% if current_user.role.name in ['Administrator', 'Operator'] or allow_user_remove_domain %}
{% if allow_user_view_history %} <div class="dropdown-divider"></div>
<button class="dropdown-item btn-primary" type="button" onclick="window.location.href='{{ url_for('domain.changelog', domain_name=domain.name) }}'"> <button type="button" class="dropdown-item btn-secondary button_delete"
<i class="fa-solid fa-history" aria-hidden="true"></i>&nbsp;Domain Changelog onclick="window.location.href='{{ url_for('domain.remove') }}'">
</button> <span style="color: red;"><i class="fa-solid fa-trash"></i>&nbsp;Remove Zone</span>
{% endif %} </button>
{% if allow_user_remove_domain %} {% endif %}
<div class="dropdown-divider"></div> </div>
<button type="button"class="dropdown-item btn-secondary button_delete" onclick="window.location.href='{{ url_for('domain.remove') }}'"> </div>
<font color="red"> </td>
<i class="fa-solid fa-trash"></i>&nbsp;Remove Domain
</font>
</button>
{% endif %}
{% endif %}
</div>
</div>
</td>
{% endmacro %} {% endmacro %}

File diff suppressed because it is too large Load Diff

View File

@ -1,239 +1,276 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "new_domain" %} {% set active_page = "new_domain" %}
{% block title %}<title>Create Zone - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
Add Domain - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">
Domain Create Zone
<small>New Domain</small> </h1>
</h1> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">New Zone</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Domain - New Domain</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-4"> <div class="col-12 col-sm-6 col-lg-4">
<div class="card"> <form role="form" method="post" action="{{ url_for('domain.add') }}">
<div class="card-header"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<h3 class="card-title">Create new domain</h3> <div class="card card-outline card-primary shadow">
</div> <div class="card-header">
<form role="form" method="post" action="{{ url_for('domain.add') }}"> <h3 class="card-title">Zone Editor</h3>
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> </div>
<div class="card-body"> <!-- /.card-header -->
<div class="form-group"> <div class="card-body">
<input type="text" class="form-control" name="domain_name" id="domain_name" placeholder="Enter a valid domain name (required)"> <div class="form-group">
</div> <label for="domain_name">Zone Name</label>
{% if domain_override_toggle == True %} <input type="text" class="form-control" name="domain_name" id="domain_name"
<div class="form-group"> placeholder="Enter a valid zone name (required)">
<label>Domain Override Record</label> </div>
<input type="checkbox" id="domain_override" name="domain_override" class="checkbox"> {% if domain_override_toggle == True %}
<div class="form-group">
<input type="checkbox" id="domain_override" name="domain_override"
class="checkbox">
&nbsp;
<label for="domain_override">Zone Override Record</label>
</div>
{% endif %}
<div class="form-group">
<label for="selAccount">Account</label>
<select name="accountid" id="selAccount" class="form-control">
<option value="0">- No Account -</option>
{% for account in accounts %}
<option value="{{ account.id }}">{{ account.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label>Zone Type</label>
<div class="radio">
<label>
<input type="radio" name="radio_type" id="radio_type_native" value="native"
checked>
Native
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio_type" id="radio_type_primary"
value="primary">
Primary
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio_type" id="radio_type_secondary"
value="secondary">
Secondary
</label>
</div>
</div>
<div class="form-group">
<label for="domain_template">Zone Template</label>
<select class="form-control" id="domain_template" name="domain_template">
<option value="0">No template</option>
{% for template in templates %}
<option value="{{ template.id }}">{{ template.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group" style="display: none;" id="domain_primary_address_div">
<input type="text" class="form-control" name="domain_primary_address"
id="domain_primary_address"
placeholder="Enter valid Primary Server IP addresses (separated by commas)">
</div>
<div class="form-group">
<label>SOA-EDIT-API</label>
<div class="radio">
<label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_default"
value="DEFAULT" checked>
DEFAULT
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_increase"
value="INCREASE">
INCREASE
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_epoch"
value="EPOCH">
EPOCH
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_off"
value="OFF"> OFF
</label>
</div>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="button" title="Cancel" class="btn btn-secondary"
onclick="window.location.href='{{ url_for('dashboard.dashboard') }}'">
<i class="fa-solid fa-window-close"></i>&nbsp;Cancel
</button>
<button type="submit" title="Create Zone" class="btn btn-primary float-right">
<i class="fa-solid fa-save"></i>&nbsp;Create Zone
</button>
</div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form>
</div> </div>
{% endif %} <!-- /.col -->
<select name="accountid" class="form-control" style="width:15em;"> <div class="col-12 col-sm-6 col-lg-8">
<option value="0">- No Account -</option> <div class="card card-outline card-secondary shadow">
{% for account in accounts %} <div class="card-header">
<option value="{{ account.id }}">{{ account.name }}</option> <h3 class="card-title">Zone Editor Help</h3>
{% endfor %} </div>
</select> <!-- /.card-header -->
<br /> <div class="card-body">
<div class="form-group"> <dl class="dl-horizontal">
<label>Type</label> <dt>Zone Name</dt>
<div class="radio"> <dd>Enter your zone name in the format of name.tld (eg. powerdns-admin.com). You can
<label> also enter sublevel zones to create delegate zones (eg. sub.powerdns-admin.com)
<input type="radio" name="radio_type" id="radio_type_native" value="native" checked> which can be useful for delegating control to other users.
Native </dd>
</label> <dt>Zone Override Record</dt>
<dd>When enabled, this will allow the user to by-pass validation errors if the user
doesn't have administration rights to a parent zone.
</dd>
<dt>Account</dt>
<dd>Specifies the PowerDNS account value to use for the zone.</dd>
<dt>Zone Type</dt>
<dd>The type decides how the domain will be replicated across multiple DNS servers.
<ul>
<li>
<strong>Native</strong> - The server will not perform any Primary or Secondary
zone functions.
</li>
<li>
<strong>Primary</strong> - The server will serve as the Primary and will send
zone transfers (AXFRs) to other servers configured as secondaries.
</li>
<li>
<strong>Secondary</strong> - The server will serve as the Secondary and will
request and receive zone transfers (AXFRs) from other servers configured as
Primaries.
</li>
</ul>
</dd>
<dt>Zone Template</dt>
<dd>Specifies the existing zone template which this zone should initially be replicated
from.
</dd>
<dt>SOA-EDIT-API</dt>
<dd>The SOA-EDIT-API setting defines how the SOA serial number will be updated after a
change is
made to the domain.
<ul>
<li>
<strong>DEFAULT</strong> - Generate a soa serial of YYYYMMDD01. If the current serial
is lower than
the generated serial, use the generated serial. If the current serial is
higher or
equal to the generated serial, increase the current serial by 1.
</li>
<li>
<strong>INCREASE</strong> - Increase the current serial by 1.
</li>
<li>
<strong>EPOCH</strong> - Change the serial to the number of seconds since the EPOCH,
AKA UNIX timestamps
</li>
<li>
<strong>OFF</strong> - Disable automatic updates of the SOA serial.
</li>
</ul>
</dd>
</dl>
<p>
Find more details at <a href="https://docs.powerdns.com/md/" target="_blank">https://docs.powerdns.com/md/</a>
</p>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div> </div>
<div class="radio"> <!-- /.col -->
<label>
<input type="radio" name="radio_type" id="radio_type_primary" value="primary">
Primary
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio_type" id="radio_type_secondary" value="secondary">
Secondary
</label>
</div>
</div>
<div class="form-group">
<label>Select a template</label>
<select class="form-control" id="domain_template" name="domain_template">
<option value="0">No template</option>
{% for template in templates %}
<option value="{{ template.id }}">{{ template.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group" style="display: none;" id="domain_primary_address_div">
<input type="text" class="form-control" name="domain_primary_address" id="domain_primary_address" placeholder="Enter valid Primary Server IP addresses (separated by commas)">
</div>
<div class="form-group">
<label>SOA-EDIT-API</label>
<div class="radio">
<label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_default" value="DEFAULT" checked>
DEFAULT
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_increase" value="INCREASE">
INCREASE
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_epoch" value="EPOCH">
EPOCH
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_off" value="OFF"> OFF
</label>
</div>
</div>
</div> </div>
<div class="card-footer"> <!-- /.row -->
<button type="button" class="btn btn-secondary" onclick="window.location.href='{{ url_for('dashboard.dashboard') }}'">
<i class="fa-solid fa-window-close"></i>&nbsp;Cancel
</button>
<button type="submit" class="btn btn-primary float-right">
<i class="fa-solid fa-save"></i>&nbsp;Create
</button>
</div>
</form>
</div> </div>
</div> <!-- /.container-fluid -->
<div class="col-8"> </section>
<div class="card">
<div class="card-header">
<h3 class="card-title">Help with creating a new domain</h3>
</div>
<div class="card-body">
<dl class="dl-horizontal">
<dt>Domain name</dt>
<dd>Enter your domain name in the format of name.tld (eg. powerdns-admin.com). You can also
enter sub-domains to create a sub-root zone (eg. sub.powerdns-admin.com) in case you want to
delegate sub-domain management to specific users.
</dd>
<dt>Type</dt>
<dd>The type decides how the domain will be replicated across multiple DNS servers.
<ul>
<li>
<b>Native - </b>{{ SITE_NAME }} will not perform any Primary or Secondary zone functions.
</li>
<li>
<b>Primary - </b>{{ SITE_NAME }} will serve as the Primary and will send zone transfers
(AXFRs) to other servers configured as secondaries.
</li>
<li>
<b>Secondary - </b>{{ SITE_NAME }} will serve as the Secondary and will request and receive
zone transfers (AXFRs) from other servers configured as Primaries.
</li>
</ul>
</dd>
<dt>SOA-EDIT-API</dt>
<dd>The SOA-EDIT-API setting defines how the SOA serial number will be updated after a change is
made to the domain.
<ul>
<li>
<b>DEFAULT - </b>Generate a soa serial of YYYYMMDD01. If the current serial is lower than
the generated serial, use the generated serial. If the current serial is higher or
equal to the generated serial, increase the current serial by 1.
</li>
<li>
<b>INCREASE - </b>Increase the current serial by 1.
</li>
<li>
<b>EPOCH - </b>Change the serial to the number of seconds since the EPOCH, aka unixtime.
</li>
<li>
<b>OFF - </b>Disable automatic updates of the SOA serial.
</li>
</ul>
</dd>
</dl>
<p>
Find more details at <a href="https://docs.powerdns.com/md/">https://docs.powerdns.com/md/</a>
</p>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
$("input[name=radio_type]").change(function () { $("input[name=radio_type]").change(function () {
var type = $(this).val(); var type = $(this).val();
if (type == "secondary") { if (type == "secondary") {
$("#domain_primary_address_div").show(); $("#domain_primary_address_div").show();
} else { } else {
$("#domain_primary_address_div").hide(); $("#domain_primary_address_div").hide();
} }
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<script> <script>
{% if domain_override_message %} {% if domain_override_message %}
$(document.body).ready(function () { $(document.body).ready(function () {
var modal = $("#modal_warning"); var modal = $("#modal_warning");
var info = "{{ domain_override_message }}"; var info = "{{ domain_override_message }}";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.modal('show'); modal.modal('show');
}); });
{% endif %} {% endif %}
</script> </script>
<div class="modal fade" id="modal_warning"> <div class="modal fade" id="modal_warning">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content modal-sm"> <div class="modal-content modal-sm">
<div class="modal-header alert-danger"> <div class="modal-header alert-danger">
<h4 class="modal-title"> <h4 class="modal-title">
WARNING WARNING
</h4> </h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="button_close_warn_modal"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"
<span aria-hidden="true">&times;</span> id="button_close_warn_modal">
</button> <span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary center-block" data-dismiss="modal"
id="button_confirm_warn_modal">
CLOSE
</button>
</div>
</div>
</div> </div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary center-block" data-dismiss="modal" id="button_confirm_warn_modal">
CLOSE
</button>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,137 +1,137 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "remove_domain" %} {% set active_page = "remove_domain" %}
{% block title %}<title>Remove Zone - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
Remove Domain - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">
Domain Remove Zone
<small>Remove</small> </h1>
</h1> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Remove Zone</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Domain - Remove Domain</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-4"> <div class="col-12 col-sm-6 col-lg-4">
<div class="card"> <div class="card card-outline card-primary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Remove Domain</h3> <h3 class="card-title">Zone Selector</h3>
</div> </div>
<form role="form" method="post" action="{{ url_for('domain.remove') }}"> <form role="form" method="post" action="{{ url_for('domain.remove') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="card-body"> <div class="card-body">
<select id=domainid class="form-control" style="width:15em;"> <div class="form-group">
<option value="0">- Select Domain -</option> <label for="domainid">Zone</label>
{% for domain in domainss %} <select id=domainid class="form-control" style="width:15em;">
<option value="{{ domain.id }}">{{ domain.name }}</option> <option value="0">- Select Domain -</option>
{% endfor %} {% for domain in domainss %}
</select> <option value="{{ domain.id }}">{{ domain.name }}</option>
<br /> {% endfor %}
</select>
</div>
</div>
<div class="card-footer">
<button type="button" title="Cancel" class="btn btn-secondary"
onclick="window.location.href='{{ url_for('dashboard.dashboard') }}'">
<i class="fa-solid fa-window-close"></i>&nbsp;Cancel
</button>
<button type="button" title="Remove Zone"
class="btn btn-danger button_delete float-right">
<i class="fa-solid fa-trash-alt"></i>&nbsp;Remove Zone
</button>
</div>
</form>
</div>
</div>
<div class="col-12 col-sm-6 col-lg-8">
<div class="card card-outline card-secondary shadow">
<div class="card-header">
<h3 class="card-title">Zone Selector Help</h3>
</div>
<div class="card-body">
<dl class="dl-horizontal">
<dt>Zone</dt>
<dd>Select the zone you wish to remove from the system.</dd>
</dl>
<p>Find more details at <a href="https://docs.powerdns.com/md/" target="_blank">https://docs.powerdns.com/md/</a>
</p>
</div>
</div>
</div>
</div> </div>
<div class="card-footer">
<button type="button" class="btn btn-secondary" onclick="window.location.href='{{ url_for('dashboard.dashboard') }}'">
<i class="fa-solid fa-window-close"></i>&nbsp;Cancel
</button>
<button type="button" class="btn btn-danger button_delete float-right">
<i class="fa-solid fa-trash-alt"></i>&nbsp;Remove
</button>
</div>
</form>
</div> </div>
</div> </section>
<div class="col-8">
<div class="card">
<div class="card-header">
<h3 class="card-title">Help with removing a domain</h3>
</div>
<div class="card-body">
<dl class="dl-horizontal">
<dt>Domain Name</dt>
<dd>Select the domain you wish to remove from the system.</dd>
</dl>
<p>Find more details at <a href="https://docs.powerdns.com/md/">https://docs.powerdns.com/md/</a></p>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// handle delete button // handle delete button
$(document.body).on("click", ".button_delete", function(e) { $(document.body).on("click", ".button_delete", function (e) {
e.stopPropagation(); e.stopPropagation();
if ( $("#domainid").val() == 0 ){ if ($("#domainid").val() == 0) {
showErrorModal("Please select domain to remove."); showErrorModal("Please select domain to remove.");
return; return;
} }
var modal = $("#modal_delete"); var modal = $("#modal_delete");
var domain = $("#domainid option:selected").text(); var domain = $("#domainid option:selected").text();
var info = "Are you sure you want to delete " + domain + "?"; var info = "Are you sure you want to delete the zone " + domain + "?";
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.find('#button_delete_confirm').click(function () { modal.find('#button_delete_confirm').click(function () {
$.post($SCRIPT_ROOT + '/domain/remove' , { $.post($SCRIPT_ROOT + '/domain/remove', {
'_csrf_token': '{{ csrf_token() }}', '_csrf_token': '{{ csrf_token() }}',
'domainid': domain, 'domainid': domain,
}, function () { }, function () {
window.location.href = '{{ url_for('dashboard.dashboard') }}'; window.location.href = '{{ url_for('dashboard.dashboard') }}';
}); });
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
$("#button_delete_cancel").unbind().one('click', function(e) { $("#button_delete_cancel").unbind().one('click', function (e) {
modal.modal('hide'); modal.modal('hide');
}); });
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<div class="modal fade modal-warning" id="modal_delete"> <div class="modal fade modal-warning" id="modal_delete">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Confirmation</h4> <h4 class="modal-title">Zone Removal Confirmation</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p></p> <p></p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" id="button_delete_cancel" data-dismiss="modal"> <button type="button" title="Cancel" class="btn btn-secondary" id="button_delete_cancel"
<i class="fa-solid fa-window-close"></i>&nbsp;Cancel data-dismiss="modal">
</button> <i class="fa-solid fa-window-close"></i>&nbsp;Cancel
<button type="button" class="btn btn-danger float-right" id="button_delete_confirm"> </button>
<i class="fa-solid fa-trash-alt"></i>&nbsp;Remove <button type="button" title="Remove Zone" class="btn btn-danger float-right"
</button> id="button_delete_confirm">
</div> <i class="fa-solid fa-trash-alt"></i>&nbsp;Remove Zone
</button>
</div>
</div>
</div>
</div> </div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,395 +1,453 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}<title>Edit Zone - {{ domain.name | pretty_domain_name }} - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
Domain Management - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
{% if status %}
{% if status %} {% if status.get('status') == 'ok' %}
{% if status.get('status') == 'ok' %} <div class="alert alert-success">
<div class="alert alert-success"> <strong>Success!</strong> {{ status.get('msg') }}
<strong>Success!</strong> {{ status.get('msg') }} </div>
{% elif status.get('status') == 'error' %}
<div class="alert alert-danger">
{% if status.get('msg') != None %}
<strong>Error!</strong>
{{ status.get('msg') }}
{% else %}
<strong>Error!</strong>
An undefined error occurred.
{% endif %}
</div>
{% endif %}
{% endif %}
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">
Edit Zone - {{ domain.name | pretty_domain_name }}
</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Edit Zone - {{ domain.name | pretty_domain_name }}</li>
</ol>
</div>
</div>
</div>
</div> </div>
{% elif status.get('status') == 'error' %}
<div class="alert alert-danger">
{% if status.get('msg') != None %}
<strong>Error!</strong>
{{ status.get('msg') }}
{% else %}
<strong>Error!</strong>
An undefined error occurred.
{% endif %}
</div>
{% endif %}
{% endif %}
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">
Domain Settings
<small>{{ domain.name | pretty_domain_name }}</small>
</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Domain Settings: {{ domain.name | pretty_domain_name }}</li>
</ol>
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row">
<div class="col-12"> <div class="row">
<div class="card">
<form method="post" action="{{ url_for('domain.setting', domain_name=domain.name) }}"> <div class="col-12 col-sm-4">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <form method="post"
<div class="card-header"> action="{{ url_for('domain.change_account', domain_name=domain.name) }}">
<h3 class="card-title">Domain Access Control</h3> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
</div> <div class="card card-outline card-secondary shadow">
<div class="card-body"> <div class="card-header">
<div class="row"> <h3 class="card-title">Change Zone Account</h3>
<div class="col-2">
<p>Users on the right have access to manage the records in
the {{ domain.name | pretty_domain_name }} domain.</p>
<p>Click on users to move from between columns.</p>
<p>
Users in <font style="color: red;">red</font> are Administrators
and already have access to <b>ALL</b> domains.
</p>
</div> </div>
<div class="form-group col-2"> <!-- /.card-header -->
<select multiple="multiple" class="form-control" id="domain_multi_user" <div class="card-body">
name="domain_multi_user[]">
{% for user in users %}
<option {% if user.id in
domain_user_ids %}selected{% endif %} value="{{ user.username }}"
{% if user.role.name== 'Administrator' %}style="color: red" {% endif %}>{{
user.username}}</option> {% endfor %}
</select>
</div>
</div>
<div class="card-body">
<div class="col-offset-2">
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary"> <label for="selAccountId">Account</label>
<i class="fa-solid fa-check"></i> <select id="selAccountId" name="accountid" class="form-control" style="width:15em;">
Save <option value="0">- No Account -</option>
</button> {% for account in accounts %}
<option value="{{ account.id }}"
{% if domain_account.id == account.id %}selected{% endif %}>{{ account.name }}
</option>
{% endfor %}
</select>
</div> </div>
<!-- /.form-group -->
</div> </div>
<!-- /.card-body -->
<div class="card-footer">
<button type="submit" title="Update Account" class="btn btn-primary"
id="change_soa_edit_api">
<i class="fa-solid fa-floppy-disk"></i>&nbsp;Update Account
</button>
</div>
<!-- /.card-footer -->
</div> </div>
</div> <!-- /.card -->
</form> </form>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Account</h3>
</div> </div>
<div class="card-body"> <!-- /.col -->
<div class="col-12">
<div class="form-group"> <div class="col-12 col-sm-4">
<form method="post" <div class="card card-outline card-secondary shadow">
action="{{ url_for('domain.change_account', domain_name=domain.name) }}"> <div class="card-header">
<h3 class="card-title">Auto PTR creation</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="form-group">
<input type="checkbox" id="chkAutoPtr" class="auto_ptr_toggle"
{% for setting in domain.settings %}{% if setting.setting=='auto_ptr' and setting.value=='True' %}checked
{% endif %}{% endfor %}
{% if SETTING.get('auto_ptr') %}disabled="True" {% endif %}>
&nbsp;
<label for="chkAutoPtr">Allow automatic reverse pointer creation on record updates?</label>
{% if SETTING.get('auto_ptr') %}
<p><code>Auto-ptr is enabled globally on the PDA system!</code></p>
{% endif %}
</div>
<!-- /.form-group -->
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
<div class="col-12 col-sm-4">
<div class="card card-outline card-secondary shadow">
<div class="card-header">
<h3 class="card-title">DynDNS 2 Settings</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="form-group">
<input type="checkbox" id="chkDynDns" class="dyndns_on_demand_toggle"
{% for setting in domain.settings %}{% if setting.setting=='create_via_dyndns' and setting.value=='True' %}checked
{% endif %}{% endfor %}>
&nbsp;
<label for="chkDynDns">Allow on-demand creation of records via DynDNS updates?</label>
</div>
<!-- /.form-group -->
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-12 col-sm-4">
<form method="post" action="{{ url_for('domain.setting', domain_name=domain.name) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="card card-outline card-secondary shadow">
<div class="card-header">
<h3 class="card-title">Zone Access Control</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="row">
<div class="col-12">
<p>Users on the right have access to manage the records in
the {{ domain.name | pretty_domain_name }} domain.</p>
<p>Click on users to move from between columns.</p>
<p>
Users in <font style="color: red;">red</font> are Administrators
and already have access to <b>ALL</b> domains.
</p>
</div>
</div>
<!-- /.row -->
<div class="row">
<div class="col-12">
<div class="form-group">
<select multiple="multiple" class="form-control" id="domain_multi_user"
name="domain_multi_user[]">
{% for user in users %}
<option {% if user.id in
domain_user_ids %}selected{% endif %} value="{{ user.username }}"
{% if user.role.name== 'Administrator' %}style="color: red" {% endif %}>{{ user.username }}</option> {% endfor %}
</select>
</div>
<!-- /.form-group -->
</div>
</div>
<!-- /.row -->
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="submit" title="Save Changes" class="btn btn-primary">
<i class="fa-solid fa-floppy-disk"></i>
Save Changes
</button>
</div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form>
</div>
<!-- /.col -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-12">
<div class="card card-outline card-secondary shadow">
<div class="card-header">
<h3 class="card-title">Change Zone Type</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<p>The type decides how the domain will be replicated across multiple DNS servers.</p>
<ul>
<li>
Native - PowerDNS will not perform any replication. Use this if you only have one
PowerDNS server or you handle replication via your backend.
</li>
<li>
Primary - This PowerDNS server will serve as the primary and will send zone
transfers
(AXFRs) to other servers configured as secondaries.
</li>
<li>
Secondary - This PowerDNS server will serve as the secondaries and will request and
receive
zone transfers (AXFRs) from other servers configured as primaries.
</li>
</ul>
<b>New Domain Type Setting:</b>
<form method="post" action="{{ url_for('domain.change_type', domain_name=domain.name) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<select name="accountid" class="form-control" style="width:15em;"> <select name="domain_type" class="form-control" style="width:15em;">
<option value="0">- No Account -</option> <option selected value="0">- Unchanged -</option>
{% for account in accounts %} <option value="native">Native</option>
<option value="{{ account.id }}" <option value="priamry">Primary</option>
{% if domain_account.id == account.id %}selected{% endif %}>{{ account.name }} <option value="secondary">Secondary</option>
</option> </select><br/>
{% endfor %} <div class="form-group" style="display: none;" id="domain_primary_address_div">
</select><br /> <input type="text" class="form-control" name="domain_primary_address"
<button type="submit" class="btn btn-flat btn-primary" id="change_soa_edit_api"> id="domain_primary_address"
<i class="fa-solid fa-check"></i>&nbsp;Change account for {{ domain.name | pretty_domain_name }} placeholder="Enter valid Primary Server IP addresses (separated by commas)">
</div>
<button type="submit" title="Update Zone Type" class="btn btn-primary" id="change_type">
<i class="fa-solid fa-floppy-disk"></i>&nbsp;Update Zone Type
</button> </button>
</form> </form>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
</div> <!-- /.row -->
</div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Auto PTR creation</h3> <h3 class="card-title">Change SOA-EDIT-API</h3>
</div>
<div class="card-body">
<p><input type="checkbox" id="{{ domain.name }}" class="auto_ptr_toggle"
{% for setting in domain.settings %}{% if setting.setting=='auto_ptr' and setting.value=='True' %}checked{% endif %}{% endfor %}
{% if SETTING.get('auto_ptr') %}disabled="True" {% endif %}>
&nbsp;Allow automatic reverse pointer creation on record updates?
{% if SETTING.get('auto_ptr') %}
<br/><code>Auto-ptr is enabled globally on the PDA system!</code>
{% endif %}
</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">DynDNS 2 Settings</h3>
</div>
<div class="card-body">
<p><input type="checkbox" id="{{ domain.name }}" class="dyndns_on_demand_toggle"
{% for setting in domain.settings %}{% if setting.setting=='create_via_dyndns' and setting.value=='True' %}checked{% endif %}{% endfor %}>
&nbsp;Allow on-demand creation of records via DynDNS updates?</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Change Type</h3>
</div>
<div class="card-body">
<p>The type decides how the domain will be replicated across multiple DNS servers.</p>
<ul>
<li>
Native - PowerDNS will not perform any replication. Use this if you only have one
PowerDNS server or you handle replication via your backend.
</li>
<li>
Primary - This PowerDNS server will serve as the primary and will send zone transfers
(AXFRs) to other servers configured as secondaries.
</li>
<li>
Secondary - This PowerDNS server will serve as the secondaries and will request and receive
zone transfers (AXFRs) from other servers configured as primaries.
</li>
</ul>
<b>New Domain Type Setting:</b>
<form method="post" action="{{ url_for('domain.change_type', domain_name=domain.name) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<select name="domain_type" class="form-control" style="width:15em;">
<option selected value="0">- Unchanged -</option>
<option value="native">Native</option>
<option value="priamry">Primary</option>
<option value="secondary">Secondary</option>
</select><br />
<div class="form-group" style="display: none;" id="domain_primary_address_div">
<input type="text" class="form-control" name="domain_primary_address"
id="domain_primary_address"
placeholder="Enter valid Primary Server IP addresses (separated by commas)">
</div> </div>
<button type="submit" class="btn btn-primary" id="change_type"> <!-- /.card-header -->
<i class="fa-solid fa-check"></i>&nbsp;Change type for {{ domain.name | pretty_domain_name }} <div class="card-body">
</button> <p>The SOA-EDIT-API setting defines how the SOA serial number will be updated after a change
</form> is made
to the domain.</p>
<ul>
<li>
DEFAULT - Generate a soa serial of YYYYMMDD01. If the current serial is lower than
the
generated serial, use the generated serial. If the current serial is higher or equal
to the
generated serial, increase the current serial by 1.
</li>
<li>
INCREASE - Increase the current serial by 1.
</li>
<li>
EPOCH - Change the serial to the number of seconds since the EPOCH, aka unixtime.
</li>
<li>
OFF - Disable automatic updates of the SOA serial.
</li>
</ul>
<b>New SOA-EDIT-API Setting:</b>
<form method="post"
action="{{ url_for('domain.change_soa_edit_api', domain_name=domain.name) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<select name="soa_edit_api" class="form-control" style="width:15em;">
<option selected value="0">- Unchanged -</option>
<option>DEFAULT</option>
<option>INCREASE</option>
<option>EPOCH</option>
<option>OFF</option>
</select><br/>
<button type="submit" title="Update SOA-EDIT-API" class="btn btn-primary" id="change_soa_edit_api">
<i class="fa-solid fa-floppy-disk"></i>&nbsp;Update SOA-EDIT-API
</button>
</form>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
</div> <!-- /.row -->
</div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card card-outline card-danger shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Change SOA-EDIT-API</h3> <h3 class="card-title">Remove Zone</h3>
</div> </div>
<div class="card-body"> <!-- /.card-header -->
<p>The SOA-EDIT-API setting defines how the SOA serial number will be updated after a change is made <div class="card-body">
to the domain.</p> <p>This function is used to remove a domain from PowerDNS-Admin <b>AND</b> PowerDNS. All
<ul> records and
<li> user privileges associated with this domain will also be removed. This change cannot be
DEFAULT - Generate a soa serial of YYYYMMDD01. If the current serial is lower than the reverted.</p>
generated serial, use the generated serial. If the current serial is higher or equal to the <button type="button" title="Delete Zone" class="btn btn-danger float-left delete_domain"
generated serial, increase the current serial by 1. id="{{ domain.name }}">
</li> <i class="fa-solid fa-trash"></i>&nbsp;Delete Zone
<li> </button>
INCREASE - Increase the current serial by 1. </div>
</li> <!-- /.card-body -->
<li> </div>
EPOCH - Change the serial to the number of seconds since the EPOCH, aka unixtime. <!-- /.card -->
</li>
<li>
OFF - Disable automatic updates of the SOA serial.
</li>
</ul>
<b>New SOA-EDIT-API Setting:</b>
<form method="post" action="{{ url_for('domain.change_soa_edit_api', domain_name=domain.name) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<select name="soa_edit_api" class="form-control" style="width:15em;">
<option selected value="0">- Unchanged -</option>
<option>DEFAULT</option>
<option>INCREASE</option>
<option>EPOCH</option>
<option>OFF</option>
</select><br />
<button type="submit" class="btn btn-primary" id="change_soa_edit_api">
<i class="fa-solid fa-check"></i>&nbsp;Change SOA-EDIT-API setting for {{ domain.name | pretty_domain_name }}
</button>
</form>
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</div> <!-- /.container-fluid -->
<div class="row"> </div>
<div class="col-12"> <!-- /.content -->
<div class="card"> </section>
<div class="card-header"> {% endblock %}
<h3 class="card-title">Domain Deletion</h3>
{% block extrascripts %}
<script>
//initialize pretty checkboxes
$('.dyndns_on_demand_toggle').iCheck({
checkboxClass: 'icheckbox_square-blue',
increaseArea: '20%' // optional
});
$('.auto_ptr_toggle').iCheck({
checkboxClass: 'icheckbox_square-blue',
increaseArea: '20%' // optional
});
$("#domain_multi_user").multiSelect({
selectableHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Username'>",
selectionHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Username'>",
afterInit: function (ms) {
var that = this,
$selectableSearch = that.$selectableUl.prev(),
$selectionSearch = that.$selectionUl.prev(),
selectableSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selectable:not(.ms-selected)',
selectionSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selection.ms-selected';
that.qs1 = $selectableSearch.quicksearch(selectableSearchString)
.on('keydown', function (e) {
if (e.which === 40) {
that.$selectableUl.focus();
return false;
}
});
that.qs2 = $selectionSearch.quicksearch(selectionSearchString)
.on('keydown', function (e) {
if (e.which == 40) {
that.$selectionUl.focus();
return false;
}
});
},
afterSelect: function () {
this.qs1.cache();
this.qs2.cache();
},
afterDeselect: function () {
this.qs1.cache();
this.qs2.cache();
}
});
//handle checkbox toggling
$('.dyndns_on_demand_toggle').on('ifToggled', function (event) {
var is_checked = $(this).prop('checked');
var domain = $(this).prop('id');
postdata = {
'action': 'set_setting',
'data': {
'setting': 'create_via_dyndns',
'value': is_checked
},
'_csrf_token': '{{ csrf_token() }}'
};
applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/manage-setting', true);
});
$('.auto_ptr_toggle').on('ifToggled', function (event) {
var is_checked = $(this).prop('checked');
var domain = $(this).prop('id');
postdata = {
'action': 'set_setting',
'data': {
'setting': 'auto_ptr',
'value': is_checked
},
'_csrf_token': '{{ csrf_token() }}'
};
applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/manage-setting', true);
});
// handle deletion of domain
$(document.body).on('click', '.delete_domain', function () {
var modal = $("#modal_delete_domain");
var domain = $(this).prop('id');
var info = "Are you sure you want to delete " + domain + "?";
modal.find('.modal-body p').text(info);
modal.find('#button_delete_confirm').click(function () {
$.post($SCRIPT_ROOT + '/domain/setting/' + domain + '/delete', {
'_csrf_token': '{{ csrf_token() }}'
}, function () {
window.location.href = '{{ url_for('dashboard.dashboard') }}';
});
modal.modal('hide');
})
modal.modal('show');
});
// domain primary address input handeling
$("select[name=domain_type]").change(function () {
var type = $(this).val();
if (type == "secondary") {
$("#domain_primary_address_div").show();
} else {
$("#domain_primary_address_div").hide();
}
});
</script>
{% endblock %}
{% block modals %}
<div class="modal fade" id="modal_delete_domain">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Confirmation</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div> </div>
<div class="card-body"> <div class="modal-body">
<p>This function is used to remove a domain from PowerDNS-Admin <b>AND</b> PowerDNS. All records and <p></p>
user privileges associated with this domain will also be removed. This change cannot be </div>
reverted.</p> <div class="modal-footer">
<button type="button" class="btn btn-danger float-left delete_domain" <button type="button" class="btn btn-secondary float-left" data-dismiss="modal">Close</button>
id="{{ domain.name }}"> <button type="button" class="btn btn-danger" id="button_delete_confirm">
<i class="fa-solid fa-trash"></i>&nbsp;DELETE DOMAIN {{ domain.name | pretty_domain_name }} Delete
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
</section>
{% endblock %}
{% block extrascripts %}
<script>
//initialize pretty checkboxes
$('.dyndns_on_demand_toggle').iCheck({
checkboxClass: 'icheckbox_square-blue',
increaseArea: '20%' // optional
});
$('.auto_ptr_toggle').iCheck({
checkboxClass: 'icheckbox_square-blue',
increaseArea: '20%' // optional
});
$("#domain_multi_user").multiSelect({
selectableHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Username'>",
selectionHeader: "<input type='text' class='search-input' autocomplete='off' placeholder='Username'>",
afterInit: function (ms) {
var that = this,
$selectableSearch = that.$selectableUl.prev(),
$selectionSearch = that.$selectionUl.prev(),
selectableSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selectable:not(.ms-selected)',
selectionSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selection.ms-selected';
that.qs1 = $selectableSearch.quicksearch(selectableSearchString)
.on('keydown', function (e) {
if (e.which === 40) {
that.$selectableUl.focus();
return false;
}
});
that.qs2 = $selectionSearch.quicksearch(selectionSearchString)
.on('keydown', function (e) {
if (e.which == 40) {
that.$selectionUl.focus();
return false;
}
});
},
afterSelect: function () {
this.qs1.cache();
this.qs2.cache();
},
afterDeselect: function () {
this.qs1.cache();
this.qs2.cache();
}
});
//handle checkbox toggling
$('.dyndns_on_demand_toggle').on('ifToggled', function (event) {
var is_checked = $(this).prop('checked');
var domain = $(this).prop('id');
postdata = {
'action': 'set_setting',
'data': {
'setting': 'create_via_dyndns',
'value': is_checked
},
'_csrf_token': '{{ csrf_token() }}'
};
applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/manage-setting', true);
});
$('.auto_ptr_toggle').on('ifToggled', function (event) {
var is_checked = $(this).prop('checked');
var domain = $(this).prop('id');
postdata = {
'action': 'set_setting',
'data': {
'setting': 'auto_ptr',
'value': is_checked
},
'_csrf_token': '{{ csrf_token() }}'
};
applyChanges(postdata, $SCRIPT_ROOT + '/domain/' + domain + '/manage-setting', true);
});
// handle deletion of domain
$(document.body).on('click', '.delete_domain', function () {
var modal = $("#modal_delete_domain");
var domain = $(this).prop('id');
var info = "Are you sure you want to delete " + domain + "?";
modal.find('.modal-body p').text(info);
modal.find('#button_delete_confirm').click(function () {
$.post($SCRIPT_ROOT + '/domain/setting/' + domain + '/delete', {
'_csrf_token': '{{ csrf_token() }}'
}, function () {
window.location.href = '{{ url_for('dashboard.dashboard') }}';
});
modal.modal('hide');
})
modal.modal('show');
});
// domain primary address input handeling
$("select[name=domain_type]").change(function () {
var type = $(this).val();
if (type == "secondary") {
$("#domain_primary_address_div").show();
} else {
$("#domain_primary_address_div").hide();
}
});
</script>
{% endblock %}
{% block modals %}
<div class="modal fade" id="modal_delete_domain">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Confirmation</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary float-left" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-danger" id="button_delete_confirm">
Delete</button>
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,50 +1,51 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Email confirmation - {{ SITE_NAME }}</title> <title>Email Confirmation - {{ SITE_NAME }}</title>
<link rel="icon" href="{{ url_for('static', filename='img/favicon.png') }}"> <link rel="icon" href="{{ url_for('static', filename='img/favicon.png') }}">
<!-- Tell the browser to be responsive to screen width --> <!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
{% assets "css_login" -%} {% assets "css_login" -%}
<link rel="stylesheet" href="{{ ASSET_URL }}"> <link rel="stylesheet" href="{{ ASSET_URL }}">
{%- endassets %} {%- endassets %}
<![endif]--> <![endif]-->
</head> </head>
<body class="hold-transition register-page"> <body class="hold-transition register-page">
<section class="content"> <section class="content">
<div class="error-page"> <div class="error-page">
<div class="error-content"> <div class="error-content">
{% if status == 1 %} {% if status == 1 %}
<h3> <h3>
<i class="fa fa-thumbs-o-up text-success"></i> Email verification successful! <i class="fa fa-thumbs-o-up text-success"></i> Email verification successful!
</h3> </h3>
<p> <p>
You have confirmed your account. <a href="{{ url_for('index.login') }}">Click here</a> to login. You have confirmed your account. <a href="{{ url_for('index.login') }}">Click here</a> to login.
</p> </p>
{% elif status == 2 %} {% elif status == 2 %}
<h3> <h3>
<i class="fa fa-hand-stop-o text-info"></i> Already verified! <i class="fa fa-hand-stop-o text-info"></i> Already verified!
</h3> </h3>
<p> <p>
You have confirmed your account already. <a href="{{ url_for('index.login') }}">Click here</a> to login. You have confirmed your account already. <a href="{{ url_for('index.login') }}">Click here</a> to
login.
</p> </p>
{% else %} {% else %}
<h3> <h3>
<i class="fa fa-warning text-yellow"></i> Email verification failed! <i class="fa fa-warning text-yellow"></i> Email verification failed!
</h3> </h3>
<p> <p>
The confirmation link is invalid or has expired. <a href="{{ url_for('index.resend_confirmation_email') }}">Click here</a> if you want to resend a new link. The confirmation link is invalid or has expired. <a
href="{{ url_for('index.resend_confirmation_email') }}">Click here</a> if you want to resend a new
link.
</p> </p>
{% endif %} {% endif %}
</div>
<!-- /.error-content -->
</div> </div>
<!-- /.error-page --> <!-- /.error-content -->
</section> </div>
<!-- /.error-page -->
</section>
</body> </body>
</html> </html>

View File

@ -1,10 +1,9 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>{{ SITE_NAME }} user verification email</title> <title>{{ SITE_NAME }} User Verification Email</title>
<style> <style>
@media only screen and (max-width: 620px) { @media only screen and (max-width: 620px) {
table[class=body] h1 { table[class=body] h1 {
@ -99,122 +98,122 @@
} }
</style> </style>
</head> </head>
<body class="" <body class=""
style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"> style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
<span class="preheader" <span class="preheader"
style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This
is preheader text. Some clients will show this text as a preview.</span> is preheader text. Some clients will show this text as a preview.</span>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f6f6f6; width: 100%;" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f6f6f6; width: 100%;"
width="100%" bgcolor="#f6f6f6"> width="100%" bgcolor="#f6f6f6">
<tr> <tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td> <td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
<td class="container" <td class="container"
style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; max-width: 580px; padding: 10px; width: 580px; margin: 0 auto;" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; max-width: 580px; padding: 10px; width: 580px; margin: 0 auto;"
width="580" valign="top"> width="580" valign="top">
<div class="content" <div class="content"
style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 580px; padding: 10px;"> style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 580px; padding: 10px;">
<!-- START CENTERED WHITE CONTAINER --> <!-- START CENTERED WHITE CONTAINER -->
<table role="presentation" class="main" <table role="presentation" class="main"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: #ffffff; border-radius: 3px; width: 100%;" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background: #ffffff; border-radius: 3px; width: 100%;"
width="100%"> width="100%">
<!-- START MAIN CONTENT AREA --> <!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper"
style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;"
valign="top">
<table role="presentation" border="0" cellpadding="0" cellspacing="0"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;"
width="100%">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;"
valign="top">
<p
style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Hello!</p>
<p
style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Please click the button below to verify your email address.</p>
<table role="presentation" border="0" cellpadding="0" cellspacing="0"
class="btn btn-primary"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; box-sizing: border-box; width: 100%;"
width="100%">
<tbody>
<tr>
<td align="left"
style="font-family: sans-serif; font-size: 14px; vertical-align: top; padding-bottom: 15px;"
valign="top">
<table role="presentation" border="0" cellpadding="0"
cellspacing="0"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
<tbody>
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; border-radius: 5px; text-align: center; background-color: #3498db;"
valign="top" align="center"
bgcolor="#3498db"><a
href="{{ verification_link }}"
target="_blank"
rel="noopener noreferrer"
style="border: solid 1px #3498db; border-radius: 5px; box-sizing: border-box; cursor: pointer; display: inline-block; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-decoration: none; text-transform: capitalize; background-color: #3498db; border-color: #3498db; color: #ffffff;">Verify
Email Address</a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p
style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Your confirmation link will be expired in 24 hours. If you did not create an
account, no further action is required.</p>
<p
style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Cheers!</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- END CENTERED WHITE CONTAINER -->
<!-- START FOOTER -->
<div class="footer" style="clear: both; margin-top: 10px; text-align: center; width: 100%;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;"
width="100%">
<tr> <tr>
<td class="wrapper" <td class="content-block"
style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;"
valign="top"> valign="top" align="center">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" <span class="apple-link"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" style="color: #999999; font-size: 12px; text-align: center;">This email was sent
width="100%"> from <a href="{{ SETTING.get('site_url') }}"
<tr> target="_blank" rel="noopener noreferrer">{{ SITE_NAME }}</a></span>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" </td>
valign="top"> </tr>
<p <tr>
style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;"> <td class="content-block powered-by"
Hello!</p> style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;"
<p valign="top" align="center">
style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;"> Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin"
Please click the button below to verify your email address.</p> style="color: #999999; font-size: 12px; text-align: center; text-decoration: none;">PowerDNS-Admin</a>.
<table role="presentation" border="0" cellpadding="0" cellspacing="0"
class="btn btn-primary"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; box-sizing: border-box; width: 100%;"
width="100%">
<tbody>
<tr>
<td align="left"
style="font-family: sans-serif; font-size: 14px; vertical-align: top; padding-bottom: 15px;"
valign="top">
<table role="presentation" border="0" cellpadding="0"
cellspacing="0"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
<tbody>
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; border-radius: 5px; text-align: center; background-color: #3498db;"
valign="top" align="center"
bgcolor="#3498db"> <a
href="{{ verification_link }}"
target="_blank"
rel="noopener noreferrer"
style="border: solid 1px #3498db; border-radius: 5px; box-sizing: border-box; cursor: pointer; display: inline-block; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-decoration: none; text-transform: capitalize; background-color: #3498db; border-color: #3498db; color: #ffffff;">Verify
Email Address</a> </td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p
style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Your confirmation link will be expired in 24 hours. If you did not create an account, no further action is required.</p>
<p
style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Cheers!</p>
</td>
</tr>
</table>
</td> </td>
</tr> </tr>
<!-- END MAIN CONTENT AREA -->
</table> </table>
<!-- END CENTERED WHITE CONTAINER -->
<!-- START FOOTER -->
<div class="footer" style="clear: both; margin-top: 10px; text-align: center; width: 100%;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0"
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;"
width="100%">
<tr>
<td class="content-block"
style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;"
valign="top" align="center">
<span class="apple-link"
style="color: #999999; font-size: 12px; text-align: center;">This email was sent
from <a href="{{ SETTING.get('site_url') }}"
target="_blank" rel="noopener noreferrer">{{ SITE_NAME }}</a></span>
</td>
</tr>
<tr>
<td class="content-block powered-by"
style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; color: #999999; font-size: 12px; text-align: center;"
valign="top" align="center">
Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin"
style="color: #999999; font-size: 12px; text-align: center; text-decoration: none;">PowerDNS-Admin</a>.
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
</div> </div>
</td> <!-- END FOOTER -->
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
</tr> </div>
</table> </td>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
</tr>
</table>
</body> </body>
</html> </html>

View File

@ -1,52 +1,42 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}<title>HTTP 400 Error - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
400 Error - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">HTTP 400 Error</h1>
400 </div>
<small>Error</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">HTTP 400 Error</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">400 Error</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="error-page"> <div class="error-page">
<h2 class="headline text-yellow"> <h2 class="headline text-yellow">HTTP 400 Error</h2>
400 <div class="error-content">
</h2> <h3>
<div class="error-content"> <i class="fa-solid fa-exclamation-triangle text-yellow"></i>
<h3> Oops! Bad Request
<i class="fa-solid fa-exclamation-triangle text-yellow"></i> </h3>
Oops! Bad request <p>
</h3> {% if msg %}
<p> {{ msg }}
{% if msg %} {% else %}
{{ msg }} The server refused to process your request and return a 400 error.
{% else %} {% endif %}
The server refused to process your request and return a 400 error. <br/>You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
{% endif %} </p>
<br/>You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>. </div>
</p> </div>
</div> </section>
</div>
</section>
{% endblock %} {% endblock %}

View File

@ -1,48 +1,41 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %} {% block title %}<title>HTTP 403 Error - {{ SITE_NAME }}</title>{% endblock %}
<title>
403 Error - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">HTTP 403 Error</h1>
403 </div>
<small>Error</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">HTTP 403 Error</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">403 Error</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="error-page"> <div class="error-page">
<h2 class="headline text-yellow"> <h2 class="headline text-yellow">
403 HTTP 403 Error
</h2> </h2>
<div class="error-content"> <div class="error-content">
<h3> <h3>
<i class="fa-solid fa-exclamation-triangle text-yellow"></i> <i class="fa-solid fa-exclamation-triangle text-yellow"></i>
Oops! Access denied Oops! Access Denied
</h3> </h3>
<p> <p>
You don't have permission to access this page You don't have permission to access this page
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>. You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
</p> </p>
</div> </div>
</div> </div>
</section> </section>
{% endblock %} {% endblock %}

View File

@ -1,48 +1,38 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}<title>HTTP 404 Error - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
404 Error - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">HTTP 404 Error</h1>
404 </div>
<small>Error</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">HTTP 404 Error</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">404 Error</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="error-page"> <div class="error-page">
<h2 class="headline text-yellow"> <h2 class="headline text-yellow">HTTP 404 Error</h2>
404 <div class="error-content">
</h2> <h3>
<div class="error-content"> <i class="fa-solid fa-exclamation-triangle text-yellow"></i>
<h3> Oops! Page not found.
<i class="fa-solid fa-exclamation-triangle text-yellow"></i> </h3>
Oops! You're lost <p>
</h3> The page you requested could not be found.
<p> You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
The page you requested could not be found. </p>
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>. </div>
</p> </div>
</div> </section>
</div>
</section>
{% endblock %} {% endblock %}

View File

@ -1,48 +1,38 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}<title>HTTP 500 Error - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
500 Error - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">HTTP 500 Error</h1>
500 </div>
<small>Error</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">HTTP 500 Error</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">500 Error</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="error-page"> <div class="error-page">
<h2 class="headline text-yellow"> <h2 class="headline text-yellow">HTTP 500 Error</h2>
500 <div class="error-content">
</h2> <h3>
<div class="error-content"> <i class="fa-solid fa-exclamation-triangle text-yellow"></i>
<h3> Oops! Something went wrong.
<i class="fa-solid fa-exclamation-triangle text-yellow"></i> </h3>
Oops! Something went wrong <p>
</h3> Try again later.
<p> You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
Try again later. </p>
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>. </div>
</p> </div>
</div> </section>
</div>
</section>
{% endblock %} {% endblock %}

View File

@ -1,61 +1,51 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}<title>SAML Authentication Error - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
SAML Authentication Error - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">SAML Authentication Error</h1>
SAML </div>
<small>Error</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">SAML Authentication Error</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">SAML Authentication Error</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="error-page"> <div class="error-page">
<div> <div>
<h1 class="headline text-yellow" style="font-size:46px;"> <h1 class="headline text-yellow" style="font-size:46px;">SAML Authentication Error</h1>
SAML Authentication Error </div>
</h1> <br/>
</div> <br/>
<br/> <div class="error-content">
<br/> <h3>
<div class="error-content"> <i class="fa-solid fa-exclamation-triangle text-yellow"></i>
<h3> Oops! Something went wrong with your authentication.
<i class="fa-solid fa-exclamation-triangle text-yellow"></i> </h3>
Oops! Something went wrong <br>
</h3> <p>
<br> Login failed.
<p> <br>
Login failed. Error(s) when processing SAML Response:
<br> <br>
Error(s) when processing SAML Response: <ul>
<br> {% for error in errors %}
<ul> <li>{{ error }}</li>
{% for error in errors %} {% endfor %}
<li>{{ error }}</li> </ul>
{% endfor %} You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
</ul> </p>
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>. </div>
</p> </div>
</div> </section>
</div>
</section>
{% endblock %} {% endblock %}

View File

@ -1,6 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Log In - {{ SITE_NAME }}</title> <title>Log In - {{ SITE_NAME }}</title>
@ -9,162 +9,172 @@
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<META HTTP-EQUIV="REFRESH" CONTENT="{{ 60 * SETTING.get('session_timeout') }}"> <META HTTP-EQUIV="REFRESH" CONTENT="{{ 60 * SETTING.get('session_timeout') }}">
{% assets "css_login" -%} {% assets "css_login" -%}
<link rel="stylesheet" href="{{ ASSET_URL }}"> <link rel="stylesheet" href="{{ ASSET_URL }}">
{%- endassets %} {%- endassets %}
{% if SETTING.get('custom_css') %} {% if SETTING.get('custom_css') %}
<link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}"> <link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}">
{% endif %} {% endif %}
</head> </head>
<body class="hold-transition login-page"> <body class="hold-transition login-page">
<div class="login-box"> <div class="login-box">
<div class="card card-outline card-primary"> <div class="card card-outline card-primary shadow">
<div class="card-header text-center"> <div class="card-header text-center">
<a href="{{ url_for('index.index') }}" class="h3"> <a href="{{ url_for('index.index') }}" class="h3">
{% if SETTING.get('site_name') %} {% if SETTING.get('site_name') %}
{{ SITE_NAME }} {{ SITE_NAME }}
{% else %} {% else %}
<b>PowerDNS</b>-Admin <b>PowerDNS</b>-Admin
{% endif %} {% endif %}
</a> </a>
</div> </div>
<div class="card-body login-card-body"> <div class="card-body login-card-body">
{% if error %} {% if error %}
<div class="alert alert-danger alert-dismissible"> <div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
{{ error }} {{ error }}
</div>
{% endif %}
{% if SETTING.get('ldap_enabled') or SETTING.get('local_db_enabled') %}
<form action="" method="post" data-toggle="validator">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group">
<input type="text" class="form-control" placeholder="Username" name="username" data-error="Please input your username" required {% if username %}value="{{ username }}" {% endif %}>
<span class="help-block with-errors"></span>
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="Password" name="password" data-error="Please input your password" required {% if password %}value="{{ password }}" {% endif %}>
<span class="help-block with-errors"></span>
</div>
{% if SETTING.get('otp_field_enabled') %}
<div class="form-group">
<input type="otptoken" class="form-control" placeholder="OTP Token" name="otptoken" autocomplete="off">
</div> </div>
{% endif %}
{% if SETTING.get('ldap_enabled') and SETTING.get('local_db_enabled') %}
<div class="form-group">
<select class="form-control" name="auth_method">
<option value="LOCAL">
LOCAL Authentication
</option>
{% if SETTING.get('login_ldap_first') %}
<option value="LDAP" selected="selected">
LDAP Authentication
</option>
{% else %}
<option value="LDAP">
LDAP Authentication
</option>
{% endif %}
</select>
</div>
{% elif SETTING.get('ldap_enabled') and not SETTING.get('local_db_enabled') %}
<div class="form-group">
<input type="hidden" name="auth_method" value="LDAP">
</div>
{% elif SETTING.get('local_db_enabled') and not SETTING.get('ldap_enabled') %}
<div class="form-group">
<input type="hidden" name="auth_method" value="LOCAL">
</div>
{% else %}
<div class="form-group">
<input type="hidden" name="auth_method" value="LOCAL">
</div>
{% endif %}
<div class="row">
<div class="col-8">
<div class="icheck-primary">
<input type="checkbox" id="remember">
<label for="remember">
Remember Me
</label>
</div>
</div>
<div class="col-4">
<button type="submit" class="btn btn-primary btn-block">Sign In</button>
</div>
</div>
</form>
{% endif %}
{% if SETTING.get('google_oauth_enabled') or SETTING.get('github_oauth_enabled') or SETTING.get('oidc_oauth_enabled') or SETTING.get('azure_oauth_enabled') %}
<div class="social-auth-links text-center">
{% if SETTING.get('ldap_enabled') or SETTING.get('local_db_enabled') %}
<p>- OR -</p>
{% endif %}
{% if SETTING.get('oidc_oauth_enabled') %}
<a href="{{ url_for('index.oidc_login') }}" class="btn btn-block btn-social btn-openid btn-flat">
<i class="fa-brands fa-openid"></i>
Sign in using OpenID Connect
</a>
{% endif %}
{% if SETTING.get('github_oauth_enabled') %}
<a href="{{ url_for('index.github_login') }}" class="btn btn-block btn-social btn-github btn-flat">
<i class="fa-brands fa-github"></i>
Sign in using Github
</a>
{% endif %}
{% if SETTING.get('google_oauth_enabled') %}
<a href="{{ url_for('index.google_login') }}" class="btn btn-block btn-social btn-google btn-flat">
<i class="fa-brands fa-google"></i>
Sign in using Google
</a>
{% endif %}
{% if SETTING.get('azure_oauth_enabled') %}
<a href="{{ url_for('index.azure_login') }}" class="btn btn-block btn-social btn-microsoft btn-flat">
<i class="fa-brands fa-windows"></i>
Sign in using Microsoft Azure
</a>
{% endif %}
</div>
{% endif %}
{% if saml_enabled %}
<a href="{{ url_for('index.saml_login') }}">
SAML login
</a>
{% endif %}
{% if SETTING.get('signup_enabled') %}
<br>
<a href="{{ url_for('index.register') }}" class="text-center">
Create an account
</a>
{% if SETTING.get('verify_user_email') %}
<br/>
<a href="{{ url_for('index.resend_confirmation_email') }}" class="text-center">
Resend confirmation email
</a>
{% endif %} {% endif %}
{% endif %} {% if SETTING.get('ldap_enabled') or SETTING.get('local_db_enabled') %}
<form action="" method="post" data-toggle="validator">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group">
<input type="text" class="form-control" placeholder="Username" name="username"
data-error="Please input your username" required
{% if username %}value="{{ username }}" {% endif %}>
<span class="help-block with-errors"></span>
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="Password" name="password"
data-error="Please input your password" required
{% if password %}value="{{ password }}" {% endif %}>
<span class="help-block with-errors"></span>
</div>
{% if SETTING.get('otp_field_enabled') %}
<div class="form-group">
<input type="otptoken" class="form-control" placeholder="OTP Token" name="otptoken"
autocomplete="off">
</div>
{% endif %}
{% if SETTING.get('ldap_enabled') and SETTING.get('local_db_enabled') %}
<div class="form-group">
<select class="form-control" name="auth_method">
<option value="LOCAL">
LOCAL Authentication
</option>
{% if SETTING.get('login_ldap_first') %}
<option value="LDAP" selected="selected">
LDAP Authentication
</option>
{% else %}
<option value="LDAP">
LDAP Authentication
</option>
{% endif %}
</select>
</div>
{% elif SETTING.get('ldap_enabled') and not SETTING.get('local_db_enabled') %}
<div class="form-group">
<input type="hidden" name="auth_method" value="LDAP">
</div>
{% elif SETTING.get('local_db_enabled') and not SETTING.get('ldap_enabled') %}
<div class="form-group">
<input type="hidden" name="auth_method" value="LOCAL">
</div>
{% else %}
<div class="form-group">
<input type="hidden" name="auth_method" value="LOCAL">
</div>
{% endif %}
<div class="row">
<div class="col-8">
<div class="icheck-primary">
<input type="checkbox" id="remember">
<label for="remember">
Remember Me
</label>
</div>
</div>
<div class="col-4">
<button type="submit" class="btn btn-primary btn-block">Sign In</button>
</div>
</div>
</form>
{% endif %}
{% if SETTING.get('google_oauth_enabled') or SETTING.get('github_oauth_enabled') or SETTING.get('oidc_oauth_enabled') or SETTING.get('azure_oauth_enabled') %}
<div class="social-auth-links text-center">
{% if SETTING.get('ldap_enabled') or SETTING.get('local_db_enabled') %}
<p>- OR -</p>
{% endif %}
{% if SETTING.get('oidc_oauth_enabled') %}
<a href="{{ url_for('index.oidc_login') }}"
class="btn btn-block btn-social btn-openid btn-flat">
<i class="fa-brands fa-openid"></i>
Sign in using OpenID Connect
</a>
{% endif %}
{% if SETTING.get('github_oauth_enabled') %}
<a href="{{ url_for('index.github_login') }}"
class="btn btn-block btn-social btn-github btn-flat">
<i class="fa-brands fa-github"></i>
Sign in using Github
</a>
{% endif %}
{% if SETTING.get('google_oauth_enabled') %}
<a href="{{ url_for('index.google_login') }}"
class="btn btn-block btn-social btn-google btn-flat">
<i class="fa-brands fa-google"></i>
Sign in using Google
</a>
{% endif %}
{% if SETTING.get('azure_oauth_enabled') %}
<a href="{{ url_for('index.azure_login') }}"
class="btn btn-block btn-social btn-microsoft btn-flat">
<i class="fa-brands fa-windows"></i>
Sign in using Microsoft Azure
</a>
{% endif %}
</div>
{% endif %}
{% if saml_enabled %}
<a href="{{ url_for('index.saml_login') }}">
SAML login
</a>
{% endif %}
{% if SETTING.get('signup_enabled') %}
<br>
<a href="{{ url_for('index.register') }}" class="text-center">
Create an account
</a>
{% if SETTING.get('verify_user_email') %}
<br/>
<a href="{{ url_for('index.resend_confirmation_email') }}" class="text-center">
Resend confirmation email
</a>
{% endif %}
{% endif %}
</div> </div>
</div> <!-- /.card-body -->
<div class="login-box-footer">
<center>
<p>Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></p>
</center>
</div>
</div> </div>
{% assets "js_login" -%} <!-- /.card -->
<script type="text/javascript" src="{{ ASSET_URL }}"></script> </div>
{%- endassets %} <!-- /.login-box -->
{% assets "js_validation" -%} <div class="text-center pt-3">
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <p>Powered by <a href="https://powerdnsadmin.org" target="_blank">PowerDNS-Admin</a></p>
{%- endassets %} </div>
<script> {% assets "js_login" -%}
$(function () { <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{%- endassets %}
{% assets "js_validation" -%}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{%- endassets %}
<script>
$(function () {
$('input').iCheck({ $('input').iCheck({
checkboxClass: 'icheckbox_square-blue', checkboxClass: 'icheckbox_square-blue',
radioClass: 'iradio_square-blue', radioClass: 'iradio_square-blue',
increaseArea: '20%' // optional increaseArea: '20%' // optional
}); });
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,43 +1,49 @@
<!doctype html> <!DOCTYPE html>
<title>Site Maintenance</title> <html lang="en" class>
<style> <head>
body { <title>Site Maintenance</title>
text-align: center; <style>
padding: 150px; body {
} text-align: center;
padding: 150px;
}
h1 { h1 {
font-size: 50px; font-size: 50px;
} }
body { body {
font: 20px Helvetica, sans-serif; font: 20px Helvetica, sans-serif;
color: #333; color: #333;
} }
article { article {
display: block; display: block;
text-align: left; text-align: left;
width: 650px; width: 650px;
margin: 0 auto; margin: 0 auto;
} }
a { a {
color: #dc8100; color: #dc8100;
text-decoration: none; text-decoration: none;
} }
a:hover {
color: #333;
text-decoration: none;
}
</style>
a:hover {
color: #333;
text-decoration: none;
}
</style>
</head>
<body>
<article> <article>
<h1>We&rsquo;ll be back soon!</h1> <h1>We&rsquo;ll be back soon!</h1>
<div> <div>
<p>Sorry for the inconvenience but we&rsquo;re performing some maintenance at the moment. Please contact the System <p>Sorry for the inconvenience but we&rsquo;re performing some maintenance at the moment. Please contact the
Administrator if you need more information, otherwise we&rsquo;ll be back online shortly!</p> System
<p>&mdash; Team</p> Administrator if you need more information, otherwise we&rsquo;ll be back online shortly!</p>
</div> <p>&mdash; Team</p>
</div>
</article> </article>
</body>
</html>

View File

@ -1,7 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Register - {{ SITE_NAME }}</title> <title>Register - {{ SITE_NAME }}</title>
@ -10,183 +9,201 @@
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css">
{% assets "css_login" -%} {% assets "css_login" -%}
<link rel="stylesheet" href="{{ ASSET_URL }}"> <link rel="stylesheet" href="{{ ASSET_URL }}">
{%- endassets %} {%- endassets %}
</head> </head>
<body class="hold-transition register-page"> <body class="hold-transition register-page">
<div class="register-box"> <div class="register-box">
<div class="card card-outline card-primary"> <div class="card card-outline card-primary shadow">
<div class="card-header text-center"> <div class="card-header text-center">
<a href="{{ url_for('index.index') }}" class="h3"> <a href="{{ url_for('index.index') }}" class="h3">
{% if SETTING.get('site_name') %} {% if SETTING.get('site_name') %}
{{ SITE_NAME }} {{ SITE_NAME }}
{% else %} {% else %}
<b>PowerDNS</b>-Admin <b>PowerDNS</b>-Admin
{% endif %} {% endif %}
</a> </a>
</div> </div>
<div class="card-body"> <div class="card-body">
{% if error %} {% if error %}
<div class="alert alert-danger alert-dismissible"> <div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
{{ error }} {{ error }}
</div> </div>
{% endif %} {% endif %}
<p class="login-box-msg">Enter your personal details below</p> <p class="login-box-msg">Enter your personal details below</p>
<form action="{{ url_for('index.register') }}" method="post" validator> <form action="{{ url_for('index.register') }}" method="post" validator>
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"> <span class="input-group-text">
<i class="fas fa-user"></i> <i class="fas fa-user"></i>
</span> </span>
</div>
<input type="text"
class="form-control {{ 'is-invalid' if 'firstname' in error_messages else '' }}"
placeholder="First Name" name="firstname" id="firstname"
value="{{ request.form.firstname }}" required>
{% if 'firstname' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['firstname'] }}
</div>
{% endif %}
</div>
</div> </div>
<input type="text" class="form-control {{ 'is-invalid' if 'firstname' in error_messages else '' }}" placeholder="First Name" name="firstname" id="firstname" value="{{ request.form.firstname }}" required>
{% if 'firstname' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['firstname'] }}
</div>
{% endif %}
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"> <span class="input-group-text">
<i class="fas fa-user"></i> <i class="fas fa-user"></i>
</span> </span>
</div>
<input type="text"
class="form-control {{ 'is-invalid' if 'lastname' in error_messages else '' }}"
placeholder="Last name" name="lastname" id="lastname" value="{{ request.form.lastname }}"
required>
{% if 'lastname' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['lastname'] }}
</div>
{% endif %}
</div>
</div> </div>
<input type="text" class="form-control {{ 'is-invalid' if 'lastname' in error_messages else '' }}" placeholder="Last name" name="lastname" id="lastname" value="{{ request.form.lastname }}" required>
{% if 'lastname' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['lastname'] }}
</div>
{% endif %}
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"> <span class="input-group-text">
<i class="fas fa-envelope"></i> <i class="fas fa-envelope"></i>
</span> </span>
</div>
<input type="email" class="form-control {{ 'is-invalid' if 'email' in error_messages else '' }}"
placeholder="Email" name="email" id="email" value="{{ request.form.email }}" required>
{% if 'email' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['email'] }}
</div>
{% endif %}
</div>
</div> </div>
<input type="email" class="form-control {{ 'is-invalid' if 'email' in error_messages else '' }}" placeholder="Email" name="email" id="email" value="{{ request.form.email }}" required>
{% if 'email' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['email'] }}
</div>
{% endif %}
</div>
</div>
<p class="login-box-msg">Enter your account details below</p> <p class="login-box-msg">Enter your account details below</p>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"> <span class="input-group-text">
<i class="fas fa-user"></i> <i class="fas fa-user"></i>
</span> </span>
</div>
<input type="text"
class="form-control {{ 'is-invalid' if 'username' in error_messages else '' }}"
placeholder="Username" name="username" id="username" value="{{ request.form.username }}"
required>
{% if 'username' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['username'] }}
</div>
{% endif %}
</div>
</div> </div>
<input type="text" class="form-control {{ 'is-invalid' if 'username' in error_messages else '' }}" placeholder="Username" name="username" id="username" value="{{ request.form.username }}" required>
{% if 'username' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['username'] }}
</div>
{% endif %}
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"> <span class="input-group-text">
<i class="fas fa-lock"></i> <i class="fas fa-lock"></i>
</span> </span>
</div>
<input type="password"
class="form-control {{ 'is-invalid' if 'password' in error_messages else '' }}"
placeholder="Password" id="password" name="password" required>
{% if 'password' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['password'] }}
</div>
{% endif %}
</div>
</div> </div>
<input type="password" class="form-control {{ 'is-invalid' if 'password' in error_messages else '' }}" placeholder="Password" id="password" name="password" required>
{% if 'password' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['password'] }}
</div>
{% endif %}
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"> <span class="input-group-text">
<i class="fas fa-lock"></i> <i class="fas fa-lock"></i>
</span> </span>
</div>
<input type="password"
class="form-control {{ 'is-invalid' if 'rpassword' in error_messages else '' }}"
placeholder="Retype password" id="rpassword" name="rpassword" required>
{% if 'rpassword' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['rpassword'] }}
</div>
{% endif %}
</div>
</div> </div>
<input type="password" class="form-control {{ 'is-invalid' if 'rpassword' in error_messages else '' }}" placeholder="Retype password" id="rpassword" name="rpassword" required>
{% if 'rpassword' in error_messages %}
<div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i>
{{ error_messages['rpassword'] }}
</div>
{% endif %}
</div>
</div>
{% if captcha_enable %} {% if captcha_enable %}
<p class="login-box-msg">Please complete the CAPTCHA below</p> <p class="login-box-msg">Please complete the CAPTCHA below</p>
<div class="form-group"> <div class="form-group">
{{ captcha() }} {{ captcha() }}
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"> <span class="input-group-text">
<i class="fas fa-shield-alt"></i> <i class="fas fa-shield-alt"></i>
</span> </span>
</div> </div>
<input type="text" class="form-control {{ 'is-invalid' if 'captcha_result' in error_messages else '' }}" placeholder="CAPTCHA" id="captcha" name="captcha" required> <input type="text"
{% if 'captcha_result' in error_messages %} class="form-control {{ 'is-invalid' if 'captcha_result' in error_messages else '' }}"
<div class="invalid-feedback"> placeholder="CAPTCHA" id="captcha" name="captcha" required>
<i class="fas fa-exclamation-triangle"></i> {% if 'captcha_result' in error_messages %}
{{ error_messages['captcha_result'] }} <div class="invalid-feedback">
</div> <i class="fas fa-exclamation-triangle"></i>
{% endif %} {{ error_messages['captcha_result'] }}
</div> </div>
{% endif %} {% endif %}
</div>
{% endif %}
<div class="row"> <div class="row">
<div class="d-flex justify-content-between mx-auto mt-3"> <div class="d-flex justify-content-between mx-auto mt-3">
<button type="button" class="btn btn-secondary" id="button_back">Back</button> <button type="button" class="btn btn-secondary" id="button_back">Back</button>
<button type="submit" class="btn btn-primary btn-block mx-3">Register</button> <button type="submit" class="btn btn-primary btn-block mx-3">Register</button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
<div class="login-box-footer"> <!-- /.card-body -->
<center>
<p>Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></p>
</center>
</div>
</div> </div>
{% assets "js_login" -%} <!-- /.card -->
<script type="text/javascript" src="{{ ASSET_URL }}"></script> </div>
{%- endassets %} <!-- /.register-box -->
{% assets "js_validation" -%} <div class="text-center pt-3">
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <p>Powered by <a href="https://powerdnsadmin.org" target="_blank">PowerDNS-Admin</a></p>
{%- endassets %} </div>
<script> {% assets "js_login" -%}
$(function () { <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{%- endassets %}
{% assets "js_validation" -%}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{%- endassets %}
<script>
$(function () {
$('#button_back').click(function () { $('#button_back').click(function () {
window.location.href = '{{ url_for('index.login') }}'; window.location.href = '{{ url_for('index.login') }}';
}) })
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,7 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Welcome - {{ SITE_NAME }}</title> <title>Welcome - {{ SITE_NAME }}</title>
@ -9,82 +8,82 @@
<!-- Tell the browser to be responsive to screen width --> <!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
{% assets "css_login" -%} {% assets "css_login" -%}
<link rel="stylesheet" href="{{ ASSET_URL }}"> <link rel="stylesheet" href="{{ ASSET_URL }}">
{%- endassets %} {%- endassets %}
{% if SETTING.get('custom_css') %} {% if SETTING.get('custom_css') %}
<link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}"> <link rel="stylesheet" href="/static/custom/{{ SETTING.get('custom_css') }}">
{% endif %} {% endif %}
</head> </head>
<body class="hold-transition register-page">
<body class="hold-transition register-page"> <div class="register-box">
<div class="register-box"> <div class="register-logo">
<div class="register-logo">
<a><b>PowerDNS</b>-Admin</a> <a><b>PowerDNS</b>-Admin</a>
</div> </div>
<div class="register-box-body"> <div class="register-box-body">
{% if error %} {% if error %}
<div class="alert alert-danger alert-dismissible"> <div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
{{ error }} {{ error }}
</div> </div>
{% endif %} {% endif %}
Welcome, {{user.firstname}}! <br /> Welcome, {{ user.firstname }}! <br/>
You will need a Token on login. <br /> You will need a Token on login. <br/>
Your QR code is: Your QR code is:
<div id="token_information"> <div id="token_information">
{% if qrcode_image == None %} {% if qrcode_image == None %}
<p><img id="qrcode" src="{{ url_for('user.qrcode') }}"></p> <p><img id="qrcode" src="{{ url_for('user.qrcode') }}"></p>
{% else %} {% else %}
<p><img id="qrcode" src="data:image/svg+xml;utf8;base64, {{qrcode_image}}"></p> <p><img id="qrcode" src="data:image/svg+xml;utf8;base64, {{ qrcode_image }}"></p>
{% endif %} {% endif %}
<p> <p>
Your secret key is: <br /> Your secret key is: <br/>
<form> <form>
<input type=text id="otp_secret" value={{user.otp_secret}} readonly> <input type=text id="otp_secret" value={{ user.otp_secret }} readonly>
<button type=button style="position:relative; right:28px" onclick="copy_otp_secret_to_clipboard()"> <i class="fa fa-clipboard"></i> </button> <button type=button style="position:relative; right:28px" onclick="copy_otp_secret_to_clipboard()"><i
<br /><font color="red" id="copy_tooltip" style="visibility:collapse">Copied.</font> class="fa fa-clipboard"></i></button>
<br/><font color="red" id="copy_tooltip" style="visibility:collapse">Copied.</font>
</form> </form>
</p> </p>
You can use Google Authenticator (<a target="_blank" You can use Google Authenticator (<a target="_blank"
href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Android</a> href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Android</a>
- <a target="_blank" - <a target="_blank"
href="https://apps.apple.com/us/app/google-authenticator/id388497605">iOS</a>) href="https://apps.apple.com/us/app/google-authenticator/id388497605">iOS</a>)
<br /> <br/>
or FreeOTP (<a target="_blank" or FreeOTP (<a target="_blank"
href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp&hl=en">Android</a> href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp&hl=en">Android</a>
- <a target="_blank" - <a target="_blank"
href="https://itunes.apple.com/en/app/freeotp-authenticator/id872559395?mt=8">iOS</a>) href="https://itunes.apple.com/en/app/freeotp-authenticator/id872559395?mt=8">iOS</a>)
on your smartphone <br /> to scan the QR code or type the secret key. on your smartphone <br/> to scan the QR code or type the secret key.
<br /> <br /> <br/> <br/>
<font color="red"><strong><i>Make sure only you can see this QR Code <br /> <font color="red"><strong><i>Make sure only you can see this QR Code <br/>
and secret key, and nobody can capture them.</i></strong></font> and secret key, and nobody can capture them.</i></strong></font>
</div> </div>
</br> </br>
Please input your OTP token to continue, to ensure the seed has been scanned correctly. Please input your OTP token to continue, to ensure the seed has been scanned correctly.
<form action="" method="post" data-toggle="validator"> <form action="" method="post" data-toggle="validator">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" placeholder="OTP Token" name="otptoken" <input type="text" class="form-control" placeholder="OTP Token" name="otptoken"
data-error="Please input your OTP token" required> data-error="Please input your OTP token" required>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-4"> <div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block">Continue</button> <button type="submit" class="btn btn-primary btn-block">Continue</button>
</div>
</div> </div>
</div>
</form> </form>
</div>
<div class="login-box-footer">
<center>
<p>Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></p>
</center>
</div>
</div> </div>
</body> <!-- /.register-box-body -->
{% assets "js_login" -%} <div class="text-center">
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <p>Powered by <a href="https://powerdnsadmin.org" target="_blank">PowerDNS-Admin</a></p>
{%- endassets %} </div>
{% assets "js_validation" -%} </div>
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <!-- /.register-box -->
{%- endassets %} </body>
{% assets "js_login" -%}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{%- endassets %}
{% assets "js_validation" -%}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{%- endassets %}
</html> </html>

View File

@ -1,86 +1,82 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Resend a confirmation email - {{ SITE_NAME }}</title> <title>Resend Confirmation Email - {{ SITE_NAME }}</title>
<link rel="icon" href="/static/img/favicon.png"> <link rel="icon" href="/static/img/favicon.png">
<!-- Tell the browser to be responsive to screen width --> <!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
{% assets "css_login" -%} {% assets "css_login" -%}
<link rel="stylesheet" href="{{ ASSET_URL }}"> <link rel="stylesheet" href="{{ ASSET_URL }}">
{%- endassets %} {%- endassets %}
<![endif]--> <![endif]-->
</head> </head>
<body class="hold-transition register-page"> <body class="hold-transition register-page">
<div class="register-box"> <div class="register-box">
<div class="register-logo"> <div class="register-logo">
<a href="{{ url_for('index.index') }}"><b>PowerDNS</b>-Admin</a> <a href="{{ url_for('index.index') }}"><b>PowerDNS</b>-Admin</a>
</div> </div>
<div class="register-box-body"> <div class="register-box-body">
{% if error %} {% if error %}
<div class="alert alert-danger alert-dismissible"> <div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
{{ error }} {{ error }}
</div> </div>
{% endif %} {% endif %}
<p> <p>
Enter your email address to get account confirmation link. Enter your email address to get account confirmation link.
</p> </p>
<form method="post" data-toggle="validator"> <form method="post" data-toggle="validator">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group has-feedback">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="email" name="email" class="form-control" placeholder="Email address" data-error="Please input your email" required> <div class="form-group has-feedback">
<span class="glyphicon glyphicon-envelope form-control-feedback"></span> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<span class="help-block with-errors"></span> <input type="email" name="email" class="form-control" placeholder="Email address"
</div> data-error="Please input your email" required>
<div class="row"> <span class="glyphicon glyphicon-envelope form-control-feedback"></span>
<div class="col-xs-4 pull-left"> <span class="help-block with-errors"></span>
<button type="button" class="btn btn-flat btn-block" id="button_back">Back</button> </div>
</div> <div class="row">
<div class="col-xs-4 pull-right"> <div class="col-xs-4 pull-left">
<button type="submit" class="btn btn-flat btn-primary btn-block">Resend</button> <button type="button" class="btn btn-flat btn-block" id="button_back">Back</button>
</div> </div>
<!-- /.col --> <div class="col-xs-4 pull-right">
</div> <button type="submit" class="btn btn-flat btn-primary btn-block">Resend</button>
<div class="form-group"> </div>
<p> <!-- /.col -->
{% if status == 0 %} </div>
<font color="red">Email not found!</font> <div class="form-group">
{% elif status == 1 %} <p>
<font color="red">Email already confirmed!</font> {% if status == 0 %}
{% elif status == 2 %} <font color="red">Email not found!</font>
<font color="green">Confirmation email sent!</font> {% elif status == 1 %}
{% endif %} <font color="red">Email already confirmed!</font>
</p> {% elif status == 2 %}
</div> <font color="green">Confirmation email sent!</font>
</form> {% endif %}
</p>
</div>
</form>
</div> </div>
<!-- /.form-box --> <!-- /.register-box-body -->
<div class="login-box-footer"> </div>
<center> <!-- /.register-box -->
<p>Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></p> <div class="text-center pt-3">
</center> <p>Powered by <a href="https://powerdnsadmin.org" target="_blank">PowerDNS-Admin</a></p>
</div> </div>
</div>
<!-- /.login-box -->
{% assets "js_login" -%} {% assets "js_login" -%}
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{%- endassets %} {%- endassets %}
{% assets "js_validation" -%} {% assets "js_validation" -%}
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{%- endassets %} {%- endassets %}
<script> <script>
$(function () { $(function () {
$('#button_back').click(function () { $('#button_back').click(function () {
window.location.href = '{{ url_for('index.login') }}'; window.location.href = '{{ url_for('index.login') }}';
}) })
}); });
</script> </script>
</body> </body>
</html>
</html>

View File

@ -1,148 +1,157 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_domain_template" %} {% set active_page = "admin_domain_template" %}
{% block title %}<title>Zone Templates - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
Templates - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header">
<div class="content-header"> <div class="container-fluid">
<div class="container-fluid"> <div class="row mb-2">
<div class="row mb-2"> <div class="col-sm-6">
<div class="col-sm-6"> <h1 class="m-0 text-dark">Zone Templates</h1>
<h1 class="m-0 text-dark"> </div>
Templates <div class="col-sm-6">
<small>List</small> <ol class="breadcrumb float-sm-right">
</h1> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
</div> <li class="breadcrumb-item active">Zone Templates</li>
<div class="col-sm-6"> </ol>
<ol class="breadcrumb float-sm-right"> </div>
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li> </div>
<li class="breadcrumb-item active">Templates - List</li> </div>
</ol>
</div>
</div> </div>
</div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
{% with errors = get_flashed_messages(category_filter=["error"]) %} {% with errors = get_flashed_messages(category_filter=["error"]) %}
{% if errors %} {% if errors %}
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="alert alert-danger alert-dismissible"> <div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;
<h4> </button>
<i class="fa-solid fa-ban"></i> Error! <h4>
</h4> <i class="fa-solid fa-ban"></i> Error!
<div class="alert-message block-message error"> </h4>
<a class="close" href="#">x</a> <div class="alert-message block-message error">
<ul> <a class="close" href="#">x</a>
{%- for msg in errors %} <ul>
<li>{{ msg }}</li> {%- for msg in errors %}
{% endfor -%} <li>{{ msg }}</li>
</ul> {% endfor -%}
</div> </ul>
</div>
</div>
</div>
</div>
{% endif %}
{% endwith %}
<div class="row">
<div class="col-12">
<div class="card card-outline card-primary shadow">
<div class="card-header">
<h3 class="card-title">Templates</h3>
<div class="card-tools">
<a href="{{ url_for('admin.create_template') }}">
<button type="button" class="btn btn-primary">
<i class="fa-solid fa-plus"></i> Create Template
</button>
</a>
</div>
</div>
<!-- /.card-header -->
<div class="card-body table-responsive">
<table id="tbl_template_list"
class="table table-bordered table-striped table-hover table-sm records">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Total Records</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for template in templates %}
<tr>
<td>
<a href="{{ url_for('admin.edit_template', template=template.name) }}">
<strong>{{ template.name }}</strong>
</a>
</td>
<td>{{ template.description }}</td>
<td>{{ template.records|count }}</td>
<td>
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button"
id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<i class="fa-solid fa-bars"></i>
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu">
<button type="button" class="dropdown-item btn-warning"
onclick="window.location.href='{{ url_for('admin.edit_template', template=template.name) }}'">
<i class="fa-solid fa-edit"></i>&nbsp;Edit Template
</button>
<div class="dropdown-divider"></div>
<button type="button"
class="dropdown-item btn-secondary button_delete"
id="{{ template.name }}">
<span style="color: red;">
<i class="fa-solid fa-trash"></i>&nbsp;Delete Template
</span>
</button>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div> </div>
</div> <!-- /.row -->
</div> </div>
{% endif %} <!-- /.container-fluid -->
{% endwith %} </section>
<div class="row"> {% endblock %}
<div class="col-12">
<div class="card"> {% block head_styles %}
<div class="card-header"> <style>
<h3 class="card-title">Templates</h3> /* Page Specific Overrides */
<a href="{{ url_for('admin.create_template') }}"> table.records tbody td:first-of-type,
<button type="button" class="btn btn-primary float-right"> table.records tbody td:nth-child(0n+2) { text-align: left; }
<i class="fa-solid fa-plus"></i>&nbsp;Create Template </style>
</button>
</a>
</div>
<div class="card-body">
<table id="tbl_template_list" class="table table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Number of Records</th>
<th width="20%">Actions</th>
</tr>
</thead>
<tbody>
{% for template in templates %}
<tr>
<td>
<a href="{{ url_for('admin.edit_template', template=template.name) }}">
<strong>{{ template.name }}</strong>
</a>
</td>
<td>
{{ template.description }}
</td>
<td>
{{ template.records|count }}
</td>
<td>
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa-solid fa-bars"></i>&nbsp;Actions
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu">
<button type="button" class="dropdown-item btn-warning" onclick="window.location.href='{{ url_for('admin.edit_template', template=template.name) }}'">
<i class="fa-solid fa-edit"></i>&nbsp;Edit Template
</button>
<div class="dropdown-divider"></div>
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{template.name}}">
<font color="red">
<i class="fa-solid fa-trash"></i>&nbsp;Delete Template
</font>
</button>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// set up template data table // Initialize DataTables
$("#tbl_template_list").DataTable({ $("#tbl_template_list").DataTable({
"paging": true, "paging": true,
"lengthChange": true, "lengthChange": true,
"searching": true, "searching": true,
"ordering": true, "ordering": true,
"info": false, "info": false,
"autoWidth": false "autoWidth": false
}); });
// handle delete button // handle delete button
$(document.body).on("click", ".button_delete", function (e) { $(document.body).on("click", ".button_delete", function (e) {
var template = $(this).prop('id'); var template = $(this).prop('id');
$.post($SCRIPT_ROOT + '/admin/template/' + template + '/delete', { $.post($SCRIPT_ROOT + '/admin/template/' + template + '/delete', {
'_csrf_token': '{{ csrf_token() }}' '_csrf_token': '{{ csrf_token() }}'
}, function () { }, function () {
window.location.href = '{{ url_for('admin.templates') }}'; window.location.href = '{{ url_for('admin.templates') }}';
}); });
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}

View File

@ -1,118 +1,135 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_domain_template" %} {% set active_page = "admin_domain_template" %}
{% block title %}<title>Create Zone Template - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
Create Template - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">
Template Create Zone Template
<small>Create</small> </h1>
</h1> </div>
</div> <div class="col-sm-6">
<div class="col-sm-6"> <ol class="breadcrumb float-sm-right">
<ol class="breadcrumb float-sm-right"> <li class="breadcrumb-item"><a href="{{ url_for('admin.templates') }}">Templates</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('admin.templates') }}">Templates</a></li> <li class="breadcrumb-item active">Create Zone Template</li>
<li class="breadcrumb-item active">Create</li> </ol>
</ol> </div>
</div> </div>
</div>
</div> </div>
</div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
{% with errors = get_flashed_messages(category_filter=["error"]) %} {% with errors = get_flashed_messages(category_filter=["error"]) %}
{% if errors %} {% if errors %}
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="alert alert-danger alert-dismissible"> <div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;
<h4> </button>
<i class="fa-solid fa-ban"></i> Error! <h4>
</h4> <i class="fa-solid fa-ban"></i> Error!
<div class="alert-message block-message error"> </h4>
<a class="close" href="#">x</a> <div class="alert-message block-message error">
<ul> <a class="close" href="#">x</a>
{%- for msg in errors %} <ul>
<li>{{ msg }}</li> {%- for msg in errors %}
{% endfor -%} <li>{{ msg }}</li>
</ul> {% endfor -%}
</ul>
</div>
<!-- /.alert-message -->
</div>
<!-- /.alert -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
{% endif %}
{% endwith %}
<div class="row">
<div class="col-12 col-sm-6 col-lg-4">
<form role="form" method="post" action="{{ url_for('admin.create_template') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="card card-outline card-primary shadow">
<div class="card-header">
<h3 class="card-title">Zone Template Editor</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="form-group">
<label for="name">Template Name</label>
<input type="text" class="form-control" name="name" id="name"
placeholder="Enter a valid template name (required)">
</div>
<div class="form-group">
<label for="description">Template Description</label>
<input type="text" class="form-control" name="description" id="description"
placeholder="Enter a template description (optional)">
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="button" class="btn btn-secondary float-left" title="Cancel"
onclick="window.location.href='{{ url_for('admin.templates') }}'">
Cancel
</button>
<button type="submit" class="btn btn-primary float-right" title="Save Template">
<i class="fa-solid fa-save"></i>&nbsp;Save Template
</button>
</div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form>
</div>
<!-- /.col -->
<div class="col-12 col-sm-6 col-lg-8">
<div class="card card-outline card-secondary shadow">
<div class="card-header">
<h3 class="card-title">Zone Template Editor Help</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<dl class="dl-horizontal">
<dt>Template Name</dt>
<dd>Enter your template name, this is the name of the template that
will be shown to users. The name should not have any spaces but
can have symbols.
</dd>
<dt>Template Description</dt>
<dd>Enter your template description, this is to help better
identify the template.
</dd>
</dl>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div> </div>
</div> <!-- /.row -->
</div> </div>
{% endif %} <!-- /.container-fluid -->
{% endwith %} </section>
<div class="row">
<div class="col-4">
<div class="card">
<div class="card-header">
<h3 class="card-title">Create new template</h3>
</div>
<form role="form" method="post" action="{{ url_for('admin.create_template') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="card-body">
<div class="form-group">
<input type="text" class="form-control" name="name" id="name"
placeholder="Enter a valid template name (required)">
</div>
<div class="form-group">
<input type="text" class="form-control" name="description" id="description"
placeholder="Enter a template description (optional)">
</div>
</div>
<div class="card-footer">
<button type="button" class="btn btn-secondary float-left" onclick="window.location.href='{{ url_for('admin.templates') }}'">
Cancel
</button>
<button type="submit" class="btn btn-primary float-right">
<i class="fa-solid fa-save"></i>&nbsp;Save
</button>
</div>
</form>
</div>
</div>
<div class="col-8">
<div class="card">
<div class="card-header">
<h3 class="card-title">Help with creating a new template</h3>
</div>
<div class="card-body">
<dl class="dl-horizontal">
<dt>Template name</dt>
<dd>Enter your template name, this is the name of the template that
will be shown to users. The name should not have any spaces but
can have symbols.</dd>
<dt>Template description</dt>
<dd>Enter your template description, this is to help better
identify the template.</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
$("input[name=radio_type]").change(function () { $("input[name=radio_type]").change(function () {
var type = $(this).val(); var type = $(this).val();
if (type == "secondary") { if (type == "secondary") {
$("#domain_primary_address_div").show(); $("#domain_primary_address_div").show();
} else { } else {
$("#domain_primary_address_div").hide(); $("#domain_primary_address_div").hide();
} }
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,234 +1,232 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_domain_template" %} {% set active_page = "admin_domain_template" %}
{% block title %}<title>Edit Zone Template - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
Edit Template - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">Edit Zone Template</h1>
Template Edit
<small>{{ template }}</small>
</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('admin.templates') }}">Templates</a></li>
<li class="breadcrumb-item active">Template Edit: {{ template }}</li>
</ol>
</div>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Manage Template Records for {{ template }}</h3>
</div> </div>
<div class="card-body"> <div class="col-sm-6">
<button type="button" class="btn btn-primary float-left button_add_record" id="{{ template }}"> <ol class="breadcrumb float-sm-right">
<i class="fa-solid fa-plus"></i>&nbsp;Add Record <li class="breadcrumb-item"><a href="{{ url_for('admin.templates') }}">Templates</a></li>
</button> <li class="breadcrumb-item active">Edit Zone Template</li>
<button type="button" class="btn btn-primary float-right button_apply_changes" id="{{ template }}"> </ol>
<i class="fa-solid fa-save"></i>&nbsp;Save Changes
</button>
</div>
<div class="card-body">
<table id="tbl_records" class="table table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Status</th>
<th>TTL</th>
<th>Data</th>
<th>Comment</th>
<th>Edit</th>
<th>Delete</th>
<th>ID</th>
</tr>
</thead>
<tbody>
{% for record in records %}
<tr class="odd row_record">
<td>
{{ record.name }}
</td>
<td>
{{ record.type }}
</td>
<td>
{{ record.status }}
</td>
<td>
{{ record.ttl }}
</td>
<td>
{{ record.data }}
</td>
<td>
{{ record.comment }}
</td>
<td width="6%">
<button type="button" class="btn btn-warning button_edit">
<i class="fa-solid fa-edit"></i>&nbsp;Edit
</button>
</td>
<td width="6%">
<button type="button" class="btn btn-danger button_delete">
<i class="fa-solid fa-trash"></i>&nbsp;Delete
</button>
</td>
<td>
{{ record.id }}
</td>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
</section>
{% endblock %} {% endblock %}
{% block content %}
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card card-outline card-primary shadow">
<div class="card-header">
<h3 class="card-title">Zone Template Editor</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary float-left button_add_record"
title="Add Record" id="{{ template }}">
<i class="fa-solid fa-plus"></i>&nbsp;Add Record
</button>
<button type="button" class="btn btn-primary float-right button_apply_changes ml-2"
title="Save Template" id="{{ template }}">
<i class="fa-solid fa-save"></i>&nbsp;Save Template
</button>
</div>
</div>
<div class="card-body table-responsive">
<table id="tbl_records"
class="table table-bordered table-striped table-hover table-sm records">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Status</th>
<th>TTL</th>
<th>Data</th>
<th>Comment</th>
<th>Edit</th>
<th>Delete</th>
<th>ID</th>
</tr>
</thead>
<tbody>
{% for record in records %}
<tr class="odd row_record">
<td>
{{ record.name }}
</td>
<td>
{{ record.type }}
</td>
<td>
{{ record.status }}
</td>
<td>
{{ record.ttl }}
</td>
<td>
{{ record.data }}
</td>
<td>
{{ record.comment }}
</td>
<td>
<button type="button" class="btn btn-sm btn-warning button_edit"
title="Edit Record">
<i class="fa-solid fa-edit"></i>
</button>
</td>
<td>
<button type="button" class="btn btn-sm btn-danger button_delete"
title="Delete Record">
<i class="fa-solid fa-trash"></i>
</button>
</td>
<td>{{ id }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// superglobals // superglobals
window.records_allow_edit = {{ editable_records | tojson }}; window.records_allow_edit = {{ editable_records | tojson }};
window.ttl_options = {{ ttl_options | tojson }}; window.ttl_options = {{ ttl_options | tojson }};
window.nEditing = null; window.nEditing = null;
window.nNew = false; window.nNew = false;
// set up user data table // Initialize DataTables
$("#tbl_records").DataTable({ $("#tbl_records").DataTable({
"paging" : true, "paging": true,
"lengthChange" : true, "lengthChange": true,
"searching" : true, "searching": true,
"ordering" : true, "ordering": true,
"info" : true, "info": true,
"autoWidth" : false, "autoWidth": false,
{% if SETTING.get('default_record_table_size')|string in ['5','15','20'] %} {% if SETTING.get('default_record_table_size')|string in ['5','15','20'] %}
"lengthMenu": [ [5, 15, 20, -1], "lengthMenu": [[5, 15, 20, -1],
[5, 15, 20, "All"]], [5, 15, 20, "All"]],
{% else %} {% else %}
"lengthMenu": [ [5, 15, 20, {{ SETTING.get('default_record_table_size') }}, -1], "lengthMenu": [[5, 15, 20, {{ SETTING.get('default_record_table_size') }}, -1],
[5, 15, 20, {{ SETTING.get('default_record_table_size') }}, "All"]], [5, 15, 20, {{ SETTING.get('default_record_table_size') }}, "All"]],
{% endif %} {% endif %}
"pageLength": {{ SETTING.get('default_record_table_size') }}, "pageLength": {{ SETTING.get('default_record_table_size') }},
"language": { "language": {
"lengthMenu": " _MENU_ records" "lengthMenu": " _MENU_ records"
},
"retrieve" : true,
"columnDefs": [
{
type: 'natural',
targets: [0, 4]
}, },
{ "retrieve": true,
// hidden column so that we can add new records on top "columnDefs": [
// regardless of whatever sorting is done. See orderFixed {
visible: false, type: 'natural',
targets: [ 8 ] targets: [0, 4]
}, },
{ {
className: "length-break", // hidden column so that we can add new records on top
targets: [ 4, 5 ] // regardless of whatever sorting is done. See orderFixed
} visible: false,
], targets: [8]
"orderFixed": [[8, 'asc']] },
}); {
className: "length-break",
// handle delete button targets: [4, 5]
$(document.body).on("click", ".button_delete", function(e) { }
e.stopPropagation(); ],
var modal = $("#modal_delete"); "orderFixed": [[8, 'asc']]
var table = $("#tbl_records").DataTable();
var record = $(this).prop('id');
var nRow = $(this).parents('tr')[0];
var info = "Are you sure you want to delete " + record + "?";
modal.find('.modal-body p').text(info);
modal.modal('show');
$("#button_delete_confirm").unbind().one('click', function(e) {
table.row(nRow).remove().draw();
modal.modal('hide');
}); });
$("#button_delete_cancel").unbind().one('click', function(e) { // handle delete button
modal.modal('hide'); $(document.body).on("click", ".button_delete", function (e) {
}); e.stopPropagation();
}); var modal = $("#modal_delete");
// handle edit button and record click var table = $("#tbl_records").DataTable();
$(document.body).on("click", ".button_edit{% if quick_edit %}, .row_record{% endif %}", function(e) { var record = $(this).prop('id');
e.stopPropagation();
if ($(this).is('tr')) {
var nRow = $(this)[0];
} else {
var nRow = $(this).parents('tr')[0]; var nRow = $(this).parents('tr')[0];
} var info = "Are you sure you want to delete " + record + "?";
var table = $("#tbl_records").DataTable(); modal.find('.modal-body p').text(info);
modal.modal('show');
if (nEditing == nRow) { $("#button_delete_confirm").unbind().one('click', function (e) {
/* click on row already being edited, do nothing */ table.row(nRow).remove().draw();
} else if (nEditing !== null && nEditing != nRow && nNew == false) { modal.modal('hide');
/* Currently editing - but not this row - restore the old before continuing to edit mode */ });
restoreRow(table, nEditing);
editRow(table, nRow);
nEditing = nRow;
} else if (nNew == true) {
/* adding a new row, delete it and start editing */
table.row(nEditing).remove().draw();
nNew = false;
editRow(table, nRow);
nEditing = nRow;
} else {
/* No edit in progress - let's start one */
editRow(table, nRow);
nEditing = nRow;
}
});
// handle apply changes button $("#button_delete_cancel").unbind().one('click', function (e) {
$(document.body).on("click",".button_apply_changes", function() { modal.modal('hide');
if (nNew || nEditing) { });
showErrorModal("Previous record not saved. Please save it before applying the changes."); });
return;
}
var modal = $("#modal_apply_changes"); // handle edit button and record click
var table = $("#tbl_records").DataTable(); $(document.body).on("click", ".button_edit{% if quick_edit %}, .row_record{% endif %}", function (e) {
var template = $(this).prop('id'); e.stopPropagation();
var info = "Are you sure you want to apply your changes?"; if ($(this).is('tr')) {
modal.find('.modal-body p').text(info); var nRow = $(this)[0];
} else {
var nRow = $(this).parents('tr')[0];
}
var table = $("#tbl_records").DataTable();
// following unbind("click") is to avoid multiple times execution if (nEditing == nRow) {
modal.find('#button_apply_confirm').unbind("click").click(function() { /* click on row already being edited, do nothing */
var data = getTableData(table); } else if (nEditing !== null && nEditing != nRow && nNew == false) {
applyChanges( {'_csrf_token': '{{ csrf_token() }}', 'records': data}, $SCRIPT_ROOT + '/admin/template/' + template + '/apply', true); /* Currently editing - but not this row - restore the old before continuing to edit mode */
modal.modal('hide'); restoreRow(table, nEditing);
}) editRow(table, nRow);
modal.modal('show'); nEditing = nRow;
} else if (nNew == true) {
/* adding a new row, delete it and start editing */
table.row(nEditing).remove().draw();
nNew = false;
editRow(table, nRow);
nEditing = nRow;
} else {
/* No edit in progress - let's start one */
editRow(table, nRow);
nEditing = nRow;
}
});
}); // handle apply changes button
$(document.body).on("click", ".button_apply_changes", function () {
if (nNew || nEditing) {
showErrorModal("Previous record not saved. Please save it before applying the changes.");
return;
}
// handle add record button var modal = $("#modal_apply_changes");
$(document.body).on("click", ".button_add_record", function (e) { var table = $("#tbl_records").DataTable();
var template = $(this).prop('id');
var info = "Are you sure you want to apply your changes?";
modal.find('.modal-body p').text(info);
// following unbind("click") is to avoid multiple times execution
modal.find('#button_apply_confirm').unbind("click").click(function () {
var data = getTableData(table);
applyChanges({
'_csrf_token': '{{ csrf_token() }}',
'records': data
}, $SCRIPT_ROOT + '/admin/template/' + template + '/apply', true);
modal.modal('hide');
})
modal.modal('show');
});
// handle add record button
$(document.body).on("click", ".button_add_record", function (e) {
if (nNew || nEditing) { if (nNew || nEditing) {
showErrorModal("Previous record not saved. Please save it before adding more record."); showErrorModal("Previous record not saved. Please save it before adding more record.");
return; return;
@ -245,8 +243,8 @@
nNew = true; nNew = true;
}); });
//handle cancel button //handle cancel button
$(document.body).on("click", ".button_cancel", function (e) { $(document.body).on("click", ".button_cancel", function (e) {
e.stopPropagation(); e.stopPropagation();
var oTable = $("#tbl_records").DataTable(); var oTable = $("#tbl_records").DataTable();
if (nNew) { if (nNew) {
@ -259,8 +257,8 @@
} }
}); });
//handle save button //handle save button
$(document.body).on("click", ".button_save", function (e) { $(document.body).on("click", ".button_save", function (e) {
e.stopPropagation(); e.stopPropagation();
var table = $("#tbl_records").DataTable(); var table = $("#tbl_records").DataTable();
saveRow(table, nEditing); saveRow(table, nEditing);
@ -268,73 +266,73 @@
nNew = false; nNew = false;
}); });
{% if SETTING.get('record_helper') %} {% if SETTING.get('record_helper') %}
//handle wacky record types //handle wacky record types
$(document.body).on("focus", "#current_edit_record_data", function (e) { $(document.body).on("focus", "#current_edit_record_data", function (e) {
var record_type = $(this).parents("tr").find('#record_type').val(); var record_type = $(this).parents("tr").find('#record_type').val();
var record_data = $(this); var record_data = $(this);
if (record_type == "CAA") { if (record_type == "CAA") {
var modal = $("#modal_custom_record"); var modal = $("#modal_custom_record");
if (record_data.val() == "") { if (record_data.val() == "") {
var form = " <label for=\"caa_flag\">CAA Flag</label> \ var form = " <label for=\"caa_flag\">CAA Flag</label> \
<input type=\"text\" class=\"form-control\" name=\"caa_flag\" id=\"caa_flag\" placeholder=\"0\"> \ <input type=\"text\" class=\"form-control\" name=\"caa_flag\" id=\"caa_flag\" placeholder=\"0\"> \
<label for=\"caa_tag\">CAA Tag</label> \ <label for=\"caa_tag\">CAA Tag</label> \
<input type=\"text\" class=\"form-control\" name=\"caa_tag\" id=\"caa_tag\" placeholder=\"issue\"> \ <input type=\"text\" class=\"form-control\" name=\"caa_tag\" id=\"caa_tag\" placeholder=\"issue\"> \
<label for=\"caa_value\">CAA Value</label> \ <label for=\"caa_value\">CAA Value</label> \
<input type=\"text\" class=\"form-control\" name=\"caa_value\" id=\"caa_value\" placeholder=\"eg. letsencrypt.org\"> \ <input type=\"text\" class=\"form-control\" name=\"caa_value\" id=\"caa_value\" placeholder=\"eg. letsencrypt.org\"> \
"; ";
} else { } else {
var parts = record_data.val().split(" "); var parts = record_data.val().split(" ");
var form = " <label for=\"caa_flag\">CAA Flag</label> \ var form = " <label for=\"caa_flag\">CAA Flag</label> \
<input type=\"text\" class=\"form-control\" name=\"caa_flag\" id=\"caa_flag\" placeholder=\"0\" value=\"" + parts[0] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"caa_flag\" id=\"caa_flag\" placeholder=\"0\" value=\"" + parts[0] + "\"> \
<label for=\"caa_tag\">CAA Tag</label> \ <label for=\"caa_tag\">CAA Tag</label> \
<input type=\"text\" class=\"form-control\" name=\"caa_tag\" id=\"caa_tag\" placeholder=\"issue\" value=\"" + parts[1] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"caa_tag\" id=\"caa_tag\" placeholder=\"issue\" value=\"" + parts[1] + "\"> \
<label for=\"caa_value\">CAA Value</label> \ <label for=\"caa_value\">CAA Value</label> \
<input type=\"text\" class=\"form-control\" name=\"caa_value\" id=\"caa_value\" placeholder=\"eg. letsencrypt.org\" value=\"" + parts[2] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"caa_value\" id=\"caa_value\" placeholder=\"eg. letsencrypt.org\" value=\"" + parts[2] + "\"> \
"; ";
} }
modal.find('.modal-body p').html(form); modal.find('.modal-body p').html(form);
modal.find('#button_save').click(function() { modal.find('#button_save').click(function () {
caa_flag = modal.find('#caa_flag').val(); caa_flag = modal.find('#caa_flag').val();
caa_tag = modal.find('#caa_tag').val(); caa_tag = modal.find('#caa_tag').val();
caa_value = modal.find('#caa_value').val(); caa_value = modal.find('#caa_value').val();
data = caa_flag + " " + caa_tag + " " + '"' + caa_value + '"'; data = caa_flag + " " + caa_tag + " " + '"' + caa_value + '"';
record_data.val(data); record_data.val(data);
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
} else if (record_type == "MX") { } else if (record_type == "MX") {
var modal = $("#modal_custom_record"); var modal = $("#modal_custom_record");
if (record_data.val() == "") { if (record_data.val() == "") {
var form = " <label for=\"mx_priority\">MX Priority</label> \ var form = " <label for=\"mx_priority\">MX Priority</label> \
<input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"eg. 10\"> \ <input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"eg. 10\"> \
<label for=\"mx_server\">MX Server</label> \ <label for=\"mx_server\">MX Server</label> \
<input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"eg. postfix.example.com\"> \ <input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"eg. postfix.example.com\"> \
"; ";
} else { } else {
var parts = record_data.val().split(" "); var parts = record_data.val().split(" ");
var form = " <label for=\"mx_priority\">MX Priority</label> \ var form = " <label for=\"mx_priority\">MX Priority</label> \
<input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"eg. 10\" value=\"" + parts[0] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"mx_priority\" id=\"mx_priority\" placeholder=\"eg. 10\" value=\"" + parts[0] + "\"> \
<label for=\"mx_server\">MX Server</label> \ <label for=\"mx_server\">MX Server</label> \
<input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"eg. postfix.example.com\" value=\"" + parts[1] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"mx_server\" id=\"mx_server\" placeholder=\"eg. postfix.example.com\" value=\"" + parts[1] + "\"> \
"; ";
} }
modal.find('.modal-body p').html(form); modal.find('.modal-body p').html(form);
modal.find('#button_save').click(function() { modal.find('#button_save').click(function () {
mx_server = modal.find('#mx_server').val(); mx_server = modal.find('#mx_server').val();
mx_priority = modal.find('#mx_priority').val(); mx_priority = modal.find('#mx_priority').val();
data = mx_priority + " " + mx_server; data = mx_priority + " " + mx_server;
if (data && !data.endsWith('.')) { if (data && !data.endsWith('.')) {
data = data + '.' data = data + '.'
} }
record_data.val(data); record_data.val(data);
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
} else if (record_type == "SRV") { } else if (record_type == "SRV") {
var modal = $("#modal_custom_record"); var modal = $("#modal_custom_record");
if (record_data.val() == "") { if (record_data.val() == "") {
var form = " <label for=\"srv_priority\">SRV Priority</label> \ var form = " <label for=\"srv_priority\">SRV Priority</label> \
<input type=\"text\" class=\"form-control\" name=\"srv_priority\" id=\"srv_priority\" placeholder=\"0\"> \ <input type=\"text\" class=\"form-control\" name=\"srv_priority\" id=\"srv_priority\" placeholder=\"0\"> \
<label for=\"srv_weight\">SRV Weight</label> \ <label for=\"srv_weight\">SRV Weight</label> \
<input type=\"text\" class=\"form-control\" name=\"srv_weight\" id=\"srv_weight\" placeholder=\"10\"> \ <input type=\"text\" class=\"form-control\" name=\"srv_weight\" id=\"srv_weight\" placeholder=\"10\"> \
@ -343,9 +341,9 @@
<label for=\"srv_target\">SRV Target</label> \ <label for=\"srv_target\">SRV Target</label> \
<input type=\"text\" class=\"form-control\" name=\"srv_target\" id=\"srv_target\" placeholder=\"sip.example.com\"> \ <input type=\"text\" class=\"form-control\" name=\"srv_target\" id=\"srv_target\" placeholder=\"sip.example.com\"> \
"; ";
} else { } else {
var parts = record_data.val().split(" "); var parts = record_data.val().split(" ");
var form = " <label for=\"srv_priority\">SRV Priority</label> \ var form = " <label for=\"srv_priority\">SRV Priority</label> \
<input type=\"text\" class=\"form-control\" name=\"srv_priority\" id=\"srv_priority\" placeholder=\"0\" value=\"" + parts[0] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"srv_priority\" id=\"srv_priority\" placeholder=\"0\" value=\"" + parts[0] + "\"> \
<label for=\"srv_weight\">SRV Weight</label> \ <label for=\"srv_weight\">SRV Weight</label> \
<input type=\"text\" class=\"form-control\" name=\"srv_weight\" id=\"srv_weight\" placeholder=\"10\" value=\"" + parts[1] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"srv_weight\" id=\"srv_weight\" placeholder=\"10\" value=\"" + parts[1] + "\"> \
@ -354,25 +352,25 @@
<label for=\"srv_target\">SRV Target</label> \ <label for=\"srv_target\">SRV Target</label> \
<input type=\"text\" class=\"form-control\" name=\"srv_target\" id=\"srv_target\" placeholder=\"sip.example.com\" value=\"" + parts[3] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"srv_target\" id=\"srv_target\" placeholder=\"sip.example.com\" value=\"" + parts[3] + "\"> \
"; ";
} }
modal.find('.modal-body p').html(form); modal.find('.modal-body p').html(form);
modal.find('#button_save').click(function() { modal.find('#button_save').click(function () {
srv_priority = modal.find('#srv_priority').val(); srv_priority = modal.find('#srv_priority').val();
srv_weight = modal.find('#srv_weight').val(); srv_weight = modal.find('#srv_weight').val();
srv_port = modal.find('#srv_port').val(); srv_port = modal.find('#srv_port').val();
srv_target = modal.find('#srv_target').val(); srv_target = modal.find('#srv_target').val();
data = srv_priority + " " + srv_weight + " " + srv_port + " " + srv_target; data = srv_priority + " " + srv_weight + " " + srv_port + " " + srv_target;
if (data && !data.endsWith('.')) { if (data && !data.endsWith('.')) {
data = data + '.' data = data + '.'
} }
record_data.val(data); record_data.val(data);
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
} else if (record_type == "SOA") { } else if (record_type == "SOA") {
var modal = $("#modal_custom_record"); var modal = $("#modal_custom_record");
if (record_data.val() == "") { if (record_data.val() == "") {
var form = " <label for=\"soa_primaryns\">Primary Name Server</label> \ var form = " <label for=\"soa_primaryns\">Primary Name Server</label> \
<input type=\"text\" class=\"form-control\" name=\"soa_primaryns\" id=\"soa_primaryns\" placeholder=\"ns1.example.com\"> \ <input type=\"text\" class=\"form-control\" name=\"soa_primaryns\" id=\"soa_primaryns\" placeholder=\"ns1.example.com\"> \
<label for=\"soa_adminemail\">Primary Contact</label> \ <label for=\"soa_adminemail\">Primary Contact</label> \
<input type=\"text\" class=\"form-control\" name=\"soa_adminemail\" id=\"soa_adminemail\" placeholder=\"admin.example.com\"> \ <input type=\"text\" class=\"form-control\" name=\"soa_adminemail\" id=\"soa_adminemail\" placeholder=\"admin.example.com\"> \
@ -387,9 +385,9 @@
<label for=\"soa_minimumttl\">Minimum TTL</label> \ <label for=\"soa_minimumttl\">Minimum TTL</label> \
<input type=\"text\" class=\"form-control\" name=\"soa_minimumttl\" id=\"soa_minimumttl\" placeholder=\"300\"> \ <input type=\"text\" class=\"form-control\" name=\"soa_minimumttl\" id=\"soa_minimumttl\" placeholder=\"300\"> \
"; ";
} else { } else {
var parts = record_data.val().split(" "); var parts = record_data.val().split(" ");
var form = " <label for=\"soa_primaryns\">Primary Name Server</label> \ var form = " <label for=\"soa_primaryns\">Primary Name Server</label> \
<input type=\"text\" class=\"form-control\" name=\"soa_primaryns\" id=\"soa_primaryns\" value=\"" + parts[0] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"soa_primaryns\" id=\"soa_primaryns\" value=\"" + parts[0] + "\"> \
<label for=\"soa_adminemail\">Primary Contact</label> \ <label for=\"soa_adminemail\">Primary Contact</label> \
<input type=\"text\" class=\"form-control\" name=\"soa_adminemail\" id=\"soa_adminemail\" value=\"" + parts[1] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"soa_adminemail\" id=\"soa_adminemail\" value=\"" + parts[1] + "\"> \
@ -404,93 +402,95 @@
<label for=\"soa_minimumttl\">Minimum TTL</label> \ <label for=\"soa_minimumttl\">Minimum TTL</label> \
<input type=\"text\" class=\"form-control\" name=\"soa_minimumttl\" id=\"soa_minimumttl\" value=\"" + parts[6] + "\"> \ <input type=\"text\" class=\"form-control\" name=\"soa_minimumttl\" id=\"soa_minimumttl\" value=\"" + parts[6] + "\"> \
"; ";
} }
modal.find('.modal-body p').html(form); modal.find('.modal-body p').html(form);
modal.find('#button_save').click(function() { modal.find('#button_save').click(function () {
soa_primaryns = modal.find('#soa_primaryns').val(); soa_primaryns = modal.find('#soa_primaryns').val();
soa_adminemail = modal.find('#soa_adminemail').val(); soa_adminemail = modal.find('#soa_adminemail').val();
soa_serial = modal.find('#soa_serial').val(); soa_serial = modal.find('#soa_serial').val();
soa_zonerefresh = modal.find('#soa_zonerefresh').val(); soa_zonerefresh = modal.find('#soa_zonerefresh').val();
soa_failedzonerefresh = modal.find('#soa_failedzonerefresh').val(); soa_failedzonerefresh = modal.find('#soa_failedzonerefresh').val();
soa_zoneexpiry = modal.find('#soa_zoneexpiry').val(); soa_zoneexpiry = modal.find('#soa_zoneexpiry').val();
soa_minimumttl = modal.find('#soa_minimumttl').val(); soa_minimumttl = modal.find('#soa_minimumttl').val();
data = soa_primaryns + " " + soa_adminemail + " " + soa_serial + " " + soa_zonerefresh + " " + soa_failedzonerefresh + " " + soa_zoneexpiry + " " + soa_minimumttl; data = soa_primaryns + " " + soa_adminemail + " " + soa_serial + " " + soa_zonerefresh + " " + soa_failedzonerefresh + " " + soa_zoneexpiry + " " + soa_minimumttl;
record_data.val(data); record_data.val(data);
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
} }
}); });
{% endif %} {% endif %}
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<div class="modal fade modal-warning" id="modal_delete"> <div class="modal fade modal-warning" id="modal_delete">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Confirmation</h4> <h4 class="modal-title">Confirmation</h4>
<button type="button" class="close" data-dismiss="modal" <button type="button" class="close" data-dismiss="modal"
aria-label="Close"> aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p></p> <p></p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary float-left" id="button_delete_cancel" data-dismiss="modal"> <button type="button" class="btn btn-secondary float-left" id="button_delete_cancel"
Close data-dismiss="modal">
</button> Close
<button type="button" class="btn btn-danger" id="button_delete_confirm"> </button>
Delete <button type="button" class="btn btn-danger" id="button_delete_confirm">
</button> Delete
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="modal fade modal-primary" id="modal_apply_changes">
<div class="modal fade modal-primary" id="modal_apply_changes"> <div class="modal-dialog">
<div class="modal-dialog"> <div class="modal-content">
<div class="modal-content"> <div class="modal-header">
<div class="modal-header"> <h4 class="modal-title">Confirmation</h4>
<h4 class="modal-title">Confirmation</h4> <button type="button" class="close" data-dismiss="modal"
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
aria-label="Close"> <span aria-hidden="true">&times;</span>
<span aria-hidden="true">&times;</span> </button>
</button> </div>
</div> <div class="modal-body">
<div class="modal-body"> <p></p>
<p></p> </div>
</div> <div class="modal-footer">
<div class="modal-footer"> <button type="button" class="btn btn-secondary float-left" data-dismiss="modal">
<button type="button" class="btn btn-secondary float-left" data-dismiss="modal"> Close
Close </button>
</button> <button type="button" class="btn btn-primary" id="button_apply_confirm">
<button type="button" class="btn btn-primary" id="button_apply_confirm"> Apply
Apply </button>
</button> </div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="modal fade modal-primary" id="modal_custom_record">
<div class="modal fade modal-primary" id="modal_custom_record"> <div class="modal-dialog">
<div class="modal-dialog"> <div class="modal-content">
<div class="modal-content"> <div class="modal-header">
<div class="modal-header"> <h4 class="modal-title">Custom Record</h4>
<h4 class="modal-title">Custom Record</h4> <button type="button" class="close" data-dismiss="modal"
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
aria-label="Close"> <span aria-hidden="true">&times;</span>
<span aria-hidden="true">&times;</span> </button>
</button> </div>
</div> <div class="modal-body">
<div class="modal-body"> <p></p>
<p></p> </div>
</div> <div class="modal-footer">
<div class="modal-footer"> <button type="button" class="btn btn-primary" id="button_save">Save</button>
<button type="button" class="btn btn-primary" id="button_save">Save</button> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,190 +1,219 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "user_profile" %} {% set active_page = "user_profile" %}
{% block title %}<title>Edit Profile - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
My Profile - {{ SITE_NAME }}
</title>
{% endblock %}
{% block dashboard_stat %} {% block dashboard_stat %}
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<h1 class="m-0 text-dark"> <h1 class="m-0 text-dark">Edit Profile</h1>
User </div>
<small>My Profile</small> <div class="col-sm-6">
</h1> <ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Edit Profile</li>
</ol>
</div>
</div>
</div> </div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">My Profile</li>
</ol>
</div>
</div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<section class="content"> <section class="content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-6"> <div class="col-12 col-sm-6">
<div class="card"> <div class="card card-outline card-primary shadow">
<div class="card-header"> <div class="card-header">
<div class="nav-tabs-custom"> <h3 class="card-title">Profile Editor</h3>
<ul class="nav nav-tabs" role="tablist"> </div>
<li class="nav-item"> <!-- /.card-header -->
<a class="nav-link active" href="#tabs-personal" data-toggle="tab"> <div class="card-body">
Personal Info <div class="nav-tabs-custom mb-2">
</a> <ul class="nav nav-tabs" role="tablist">
</li> <li class="nav-item">
{% if session['authentication_type'] == 'LOCAL' %} <a class="nav-link active" href="#tabs-personal" data-toggle="tab">
<li class="nav-item"> Personal Info
<a class="nav-link" href="#tabs-password" data-toggle="tab"> </a>
Change Password </li>
</a> {% if session['authentication_type'] == 'LOCAL' %}
</li> <li class="nav-item">
{% endif %} <a class="nav-link" href="#tabs-password" data-toggle="tab">
{% if session['authentication_type'] in ['LOCAL', 'LDAP'] %} Change Password
<li class="nav-item"> </a>
<a class="nav-link" href="#tabs-authentication" data-toggle="tab"> </li>
Authentication {% endif %}
</a> {% if session['authentication_type'] in ['LOCAL', 'LDAP'] %}
</li> <li class="nav-item">
{% endif %} <a class="nav-link" href="#tabs-authentication" data-toggle="tab">
</ul> Authentication
<div class="card-body"> </a>
<div class="tab-content"> </li>
<div class="tab-pane fade show active" id="tabs-personal"> {% endif %}
<form role="form" method="post" action="{{ user_profile }}"> </ul>
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> </div>
<div class="form-group"> <!-- /.nav-tabs-custom -->
<label for="firstname">First Name</label>
<input type="text" class="form-control" name="firstname" id="firstname" placeholder="{{ current_user.firstname }}" <div class="tab-content">
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}> <div class="tab-pane fade show active" id="tabs-personal">
<form role="form" method="post" action="{{ user_profile }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group">
<label for="firstname">First Name</label>
<input type="text" class="form-control" name="firstname"
id="firstname" placeholder="{{ current_user.firstname }}"
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
</div>
<div class="form-group">
<label for="lastname">Last Name</label>
<input type="text" class="form-control" name="lastname"
id="lastname" placeholder="{{ current_user.lastname }}"
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
</div>
<div class="form-group">
<label for="email">E-Mail</label> <input type="email"
class="form-control"
name="email" id="email"
placeholder="{{ current_user.email }}"
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
</div>
{% if session['authentication_type'] == 'LOCAL' %}
<div class="form-group">
<button type="submit" class="btn btn-primary" title="Save Profile">
<i class="fa-solid fa-floppy-disk"></i>&nbsp;Save Profile
</button>
</div>
{% endif %}
</form>
</div>
<!-- /.tab-pane -->
{% if session['authentication_type'] == 'LOCAL' %}
<div class="tab-pane fade" id="tabs-password">
{% if not current_user.password %}
Your account password is managed via LDAP which isn't supported to
change here.
{% else %}
<form action="{{ user_profile }}" method="post">
<input type="hidden" name="_csrf_token"
value="{{ csrf_token() }}">
<div class="form-group">
<label for="password">New Password</label>
<input type="password" class="form-control" name="password"
id="newpassword">
</div>
<div class="form-group">
<label for="rpassword">Re-type New Password</label>
<input type="password" class="form-control" name="rpassword"
id="rpassword">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary" title="Save Password">
<i class="fa-solid fa-floppy-disk"></i>&nbsp;Save Password
</button>
</div>
</form>
{% endif %}
</div>
<!-- /.tab-pane -->
{% endif %}
<div class="tab-pane fade" id="tabs-authentication">
<form action="{{ user_profile }}" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group">
<input type="checkbox" id="otp_toggle" class="otp_toggle"
{% if current_user.otp_secret %}checked{% endif %}>
<label for="otp_toggle">Enable Two Factor Authentication</label>
{% if current_user.otp_secret %}
<div id="token_information">
<p>
<img id="qrcode" src="{{ url_for('user.qrcode') }}">
</p>
<div style="position: relative; left: 15px">
Your secret key is:
<br/>
<form>
<input type=text id="otp_secret"
value={{ current_user.otp_secret }} readonly>
<button type=button
style="position:relative; right:28px"
onclick="copy_otp_secret_to_clipboard()">
<i class="fa-solid fa-clipboard"></i>
</button>
<br/>
<span style="color: red;" id="copy_tooltip"
style="visibility:collapse">Copied.</span>
</form>
</div>
You can use Google Authenticator
(<a target="_blank"
href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"><i
class="fa-brands fa-google-play"></i>Android</a>
- <a target="_blank"
href="https://apps.apple.com/us/app/google-authenticator/id388497605"><i
class="fa-brands fa-app-store-ios"></i>iOS</a>)
or FreeOTP
(<a target="_blank"
href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp&hl=en"><i
class="fa-brands fa-google-play"></i>Android</a>
- <a target="_blank"
href="https://itunes.apple.com/en/app/freeotp-authenticator/id872559395?mt=8"><i
class="fa-brands fa-app-store-ios"></i>iOS</a>)
on your smartphone to scan the QR code.
<br/>
<p style="color: red; font-style: italic; font-weight: bold;">
Make sure only you can see this QR Code and
secret key and
nobody can capture them.
</p>
</div>
{% endif %}
</div>
<!-- /.form-group -->
</form>
</div>
<!-- /.tab-pane -->
</div>
<!-- /.tab-content -->
</div>
<!-- /.card-body -->
</div> </div>
<div class="form-group"> <!-- /.card -->
<label for="lastname">Last Name</label>
<input type="text" class="form-control" name="lastname" id="lastname" placeholder="{{ current_user.lastname }}"
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
</div>
<div class="form-group">
<label for="email">E-Mail</label> <input type="email" class="form-control" name="email" id="email" placeholder="{{ current_user.email }}"
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
</div>
{% if session['authentication_type'] == 'LOCAL' %}
<div class="form-group">
<button type="submit" class="btn btn-primary">
<i class="fa-solid fa-floppy-disk"></i>&nbsp;Save
</button>
</div>
{% endif %}
</form>
</div> </div>
{% if session['authentication_type'] == 'LOCAL' %} <!-- /.col -->
<div class="tab-pane fade" id="tabs-password">
{% if not current_user.password %}
Your account password is managed via LDAP which isn't supported to change here.
{% else %}
<form action="{{ user_profile }}" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group">
<label for="password">New Password</label>
<input type="password" class="form-control" name="password" id="newpassword">
</div>
<div class="form-group">
<label for="rpassword">Re-type New Password</label>
<input type="password" class="form-control" name="rpassword" id="rpassword">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">
<i class="fa-solid fa-floppy-disk"></i>&nbsp;Save
</button>
</div>
</form>
{% endif %}
</div>
{% endif %}
<div class="tab-pane fade" id="tabs-authentication">
<form action="{{ user_profile }}" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group">
<input type="checkbox" id="otp_toggle" class="otp_toggle" {% if current_user.otp_secret %}checked{% endif %}>
<label for="otp_toggle">Enable Two Factor Authentication</label>
{% if current_user.otp_secret %}
<div id="token_information">
<p>
<img id="qrcode" src="{{ url_for('user.qrcode') }}">
</p>
<div style="position: relative; left: 15px">
Your secret key is:
<br />
<form>
<input type=text id="otp_secret" value={{current_user.otp_secret}} readonly>
<button type=button style="position:relative; right:28px" onclick="copy_otp_secret_to_clipboard()">
<i class="fa-solid fa-clipboard"></i>
</button>
<br />
<font color="red" id="copy_tooltip" style="visibility:collapse">Copied.</font>
</form>
</div>
You can use Google Authenticator
(<a target="_blank" href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"><i class="fa-brands fa-google-play"></i>Android</a>
- <a target="_blank" href="https://apps.apple.com/us/app/google-authenticator/id388497605"><i class="fa-brands fa-app-store-ios"></i>iOS</a>)
or FreeOTP
(<a target="_blank" href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp&hl=en"><i class="fa-brands fa-google-play"></i>Android</a>
- <a target="_blank" href="https://itunes.apple.com/en/app/freeotp-authenticator/id872559395?mt=8"><i class="fa-brands fa-app-store-ios"></i>iOS</a>)
on your smartphone to scan the QR code.
<br />
<font color="red">
<strong>
<i>Make sure only you can see this QR Code and secret key and
nobody can capture them.
</i>
</strong>
</font>
</div>
{% endif %}
</div>
</form>
</div>
</div>
</div> </div>
</div> <!-- /.row -->
</div> </div>
</div> <!-- /.container-fluid -->
</div> </section>
</div>
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// initialize pretty checkboxes // initialize pretty checkboxes
$('.otp_toggle').iCheck({ $('.otp_toggle').iCheck({
checkboxClass: 'icheckbox_square-blue', checkboxClass: 'icheckbox_square-blue',
increaseArea: '20%' increaseArea: '20%'
}); });
// handle checkbox toggling // handle checkbox toggling
$('.otp_toggle').on('ifToggled', function(event) { $('.otp_toggle').on('ifToggled', function (event) {
var enable_otp = $(this).prop('checked'); var enable_otp = $(this).prop('checked');
var postdata = { var postdata = {
'action': 'enable_otp', 'action': 'enable_otp',
'data': { 'data': {
'enable_otp': enable_otp 'enable_otp': enable_otp
}, },
'_csrf_token': '{{ csrf_token() }}' '_csrf_token': '{{ csrf_token() }}'
}; };
applyChanges(postdata, $SCRIPT_ROOT + '/user/profile', false, true, function() { applyChanges(postdata, $SCRIPT_ROOT + '/user/profile', false, true, function () {
window.location.reload(); window.location.reload();
$('#tabs li:eq(2) a').tab('show'); $('#tabs li:eq(2) a').tab('show');
}); });
}); });
</script> </script>
{% endblock %} {% endblock %}