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)
@ -264,3 +275,10 @@ def to_idna(value, action):
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)

View File

@ -4,7 +4,8 @@ import traceback
import re import re
from base64 import b64encode from base64 import b64encode
from ast import literal_eval from ast import literal_eval
from flask import Blueprint, render_template, render_template_string, make_response, url_for, current_app, request, redirect, jsonify, abort, flash, session from flask import Blueprint, render_template, render_template_string, make_response, url_for, current_app, request, \
redirect, jsonify, abort, flash, session
from flask_login import login_required, current_user from flask_login import login_required, current_user
from ..decorators import operator_role_required, admin_role_required, history_access_required from ..decorators import operator_role_required, admin_role_required, history_access_required
@ -44,6 +45,8 @@ change_type: "addition" or "deletion" or "status" for status change or "unchange
Note: A change in "content", is considered a deletion and recreation of the same record, Note: A change in "content", is considered a deletion and recreation of the same record,
holding the new content value. holding the new content value.
""" """
def get_record_changes(del_rrset, add_rrset): def get_record_changes(del_rrset, add_rrset):
changeSet = [] changeSet = []
delSet = del_rrset['records'] if 'records' in del_rrset else [] delSet = del_rrset['records'] if 'records' in del_rrset else []
@ -54,15 +57,15 @@ def get_record_changes(del_rrset, add_rrset):
if d['content'] == a['content']: if d['content'] == a['content']:
exists = True exists = True
if d['disabled'] != a['disabled']: if d['disabled'] != a['disabled']:
changeSet.append( ({"disabled":d['disabled'],"content":d['content']}, changeSet.append(({"disabled": d['disabled'], "content": d['content']},
{"disabled":a['disabled'],"content":a['content']}, {"disabled": a['disabled'], "content": a['content']},
"status") ) "status"))
break break
if not exists: # deletion if not exists: # deletion
changeSet.append( ({"disabled":d['disabled'],"content":d['content']}, changeSet.append(({"disabled": d['disabled'], "content": d['content']},
None, None,
"deletion") ) "deletion"))
for a in addSet: # get the additions for a in addSet: # get the additions
exists = False exists = False
@ -72,7 +75,7 @@ def get_record_changes(del_rrset, add_rrset):
# already checked for status change # already checked for status change
break break
if not exists: if not exists:
changeSet.append( (None, {"disabled":a['disabled'], "content":a['content']}, "addition") ) changeSet.append((None, {"disabled": a['disabled'], "content": a['content']}, "addition"))
continue continue
for a in addSet: # get the unchanged for a in addSet: # get the unchanged
@ -82,14 +85,15 @@ def get_record_changes(del_rrset, add_rrset):
exists = True exists = True
break break
if not exists: if not exists:
changeSet.append( ( {"disabled":a['disabled'], "content":a['content']}, {"disabled":a['disabled'], "content":a['content']}, "unchanged") ) changeSet.append(({"disabled": a['disabled'], "content": a['content']},
{"disabled": a['disabled'], "content": a['content']}, "unchanged"))
return changeSet return changeSet
# out_changes is a list of HistoryRecordEntry objects in which we will append the new changes # out_changes is a list of HistoryRecordEntry objects in which we will append the new changes
# a HistoryRecordEntry represents a pair of add_rrset and del_rrset # a HistoryRecordEntry represents a pair of add_rrset and del_rrset
def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_num, record_name=None, record_type=None): def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_num, record_name=None, record_type=None):
if history_entry.detail is None: if history_entry.detail is None:
return return
@ -101,7 +105,6 @@ def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_n
add_rrsets = detail_dict['add_rrsets'] add_rrsets = detail_dict['add_rrsets']
del_rrsets = detail_dict['del_rrsets'] del_rrsets = detail_dict['del_rrsets']
for add_rrset in add_rrsets: for add_rrset in add_rrsets:
exists = False exists = False
for del_rrset in del_rrsets: for del_rrset in del_rrsets:
@ -114,7 +117,8 @@ def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_n
if not exists: # this is a new record if not exists: # this is a new record
if change_num not in out_changes: if change_num not in out_changes:
out_changes[change_num] = [] out_changes[change_num] = []
out_changes[change_num].append(HistoryRecordEntry(history_entry, [], add_rrset, "+")) # (add_rrset, del_rrset, change_type) out_changes[change_num].append(
HistoryRecordEntry(history_entry, [], add_rrset, "+")) # (add_rrset, del_rrset, change_type)
for del_rrset in del_rrsets: for del_rrset in del_rrsets:
exists = False exists = False
for add_rrset in add_rrsets: for add_rrset in add_rrsets:
@ -126,7 +130,6 @@ def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_n
out_changes[change_num] = [] out_changes[change_num] = []
out_changes[change_num].append(HistoryRecordEntry(history_entry, del_rrset, [], "-")) out_changes[change_num].append(HistoryRecordEntry(history_entry, del_rrset, [], "-"))
# only used for changelog per record # only used for changelog per record
if record_name != None and record_type != None: # then get only the records with the specific (record_name, record_type) tuple if record_name != None and record_type != None: # then get only the records with the specific (record_name, record_type) tuple
if change_num in out_changes: if change_num in out_changes:
@ -134,15 +137,16 @@ def extract_changelogs_from_a_history_entry(out_changes, history_entry, change_n
else: else:
return return
for hre in changes_i: # for each history record entry in changes_i for hre in changes_i: # for each history record entry in changes_i
if 'type' in hre.add_rrset and hre.add_rrset['name'] == record_name and hre.add_rrset['type'] == record_type: if 'type' in hre.add_rrset and hre.add_rrset['name'] == record_name and hre.add_rrset[
'type'] == record_type:
continue continue
elif 'type' in hre.del_rrset and hre.del_rrset['name'] == record_name and hre.del_rrset['type'] == record_type: elif 'type' in hre.del_rrset and hre.del_rrset['name'] == record_name and hre.del_rrset[
'type'] == record_type:
continue continue
else: else:
out_changes[change_num].remove(hre) out_changes[change_num].remove(hre)
# records with same (name,type) are considered as a single HistoryRecordEntry # records with same (name,type) are considered as a single HistoryRecordEntry
# history_entry is of type History - used to extract created_by and created_on # history_entry is of type History - used to extract created_by and created_on
# add_rrset is a dictionary of replace # add_rrset is a dictionary of replace
@ -158,7 +162,6 @@ class HistoryRecordEntry:
self.changed_fields = [] # contains a subset of : [ttl, name, type] self.changed_fields = [] # contains a subset of : [ttl, name, type]
self.changeSet = [] # all changes for the records of this add_rrset-del_rrset pair self.changeSet = [] # all changes for the records of this add_rrset-del_rrset pair
if change_type == "+": # addition if change_type == "+": # addition
self.changed_fields.append("name") self.changed_fields.append("name")
self.changed_fields.append("type") self.changed_fields.append("type")
@ -175,22 +178,21 @@ class HistoryRecordEntry:
self.changed_fields.append("ttl") self.changed_fields.append("ttl")
self.changeSet = get_record_changes(del_rrset, add_rrset) self.changeSet = get_record_changes(del_rrset, add_rrset)
def toDict(self): def toDict(self):
return { return {
"add_rrset" : self.add_rrset, "add_rrset": self.add_rrset,
"del_rrset" : self.del_rrset, "del_rrset": self.del_rrset,
"changed_fields" : self.changed_fields, "changed_fields": self.changed_fields,
"created_on" : self.history_entry.created_on, "created_on": self.history_entry.created_on,
"created_by" : self.history_entry.created_by, "created_by": self.history_entry.created_by,
"change_type" : self.change_type, "change_type": self.change_type,
"changeSet" : self.changeSet "changeSet": self.changeSet
} }
def __eq__(self, obj2): # used for removal of objects from a list def __eq__(self, obj2): # used for removal of objects from a list
return True if obj2.toDict() == self.toDict() else False return True if obj2.toDict() == self.toDict() else False
@admin_bp.before_request @admin_bp.before_request
def before_request(): def before_request():
# Manage session timeout # Manage session timeout
@ -202,11 +204,10 @@ def before_request():
session.modified = True session.modified = True
@admin_bp.route('/server/statistics', methods=['GET'])
@admin_bp.route('/pdns', methods=['GET'])
@login_required @login_required
@operator_role_required @operator_role_required
def pdns_stats(): def server_statistics():
if not Setting().get('pdns_api_url') or not Setting().get( if not Setting().get('pdns_api_url') or not Setting().get(
'pdns_api_key') or not Setting().get('pdns_version'): 'pdns_api_key') or not Setting().get('pdns_version'):
return redirect(url_for('admin.setting_pdns')) return redirect(url_for('admin.setting_pdns'))
@ -215,7 +216,6 @@ def pdns_stats():
users = User.query.all() users = User.query.all()
server = Server(server_id='localhost') server = Server(server_id='localhost')
configs = server.get_config()
statistics = server.get_statistic() statistics = server.get_statistic()
history_number = History.query.count() history_number = History.query.count()
@ -226,12 +226,33 @@ def pdns_stats():
else: else:
uptime = 0 uptime = 0
return render_template('admin_pdns_stats.html', return render_template('admin_server_statistics.html',
domains=domains,
users=users,
statistics=statistics,
uptime=uptime,
history_number=history_number)
@admin_bp.route('/server/configuration', methods=['GET'])
@login_required
@operator_role_required
def server_configuration():
if not Setting().get('pdns_api_url') or not Setting().get(
'pdns_api_key') or not Setting().get('pdns_version'):
return redirect(url_for('admin.setting_pdns'))
domains = Domain.query.all()
users = User.query.all()
server = Server(server_id='localhost')
configs = server.get_config()
history_number = History.query.count()
return render_template('admin_server_configuration.html',
domains=domains, domains=domains,
users=users, users=users,
configs=configs, configs=configs,
statistics=statistics,
uptime=uptime,
history_number=history_number) history_number=history_number)
@ -296,6 +317,7 @@ def edit_user(user_username=None):
create=create, create=create,
error=result['msg']) error=result['msg'])
@admin_bp.route('/key/edit/<key_id>', methods=['GET', 'POST']) @admin_bp.route('/key/edit/<key_id>', methods=['GET', 'POST'])
@admin_bp.route('/key/edit', methods=['GET', 'POST']) @admin_bp.route('/key/edit', methods=['GET', 'POST'])
@login_required @login_required
@ -357,13 +379,13 @@ def edit_key(key_id=None):
try: try:
if role != "User": if role != "User":
domain_list, account_list = [], [] domain_list, account_list = [], []
apikey.update(role,description,domain_list, account_list) apikey.update(role, description, domain_list, account_list)
history_message = "Updated API key {0}".format(apikey.id) history_message = "Updated API key {0}".format(apikey.id)
except Exception as e: except Exception as e:
current_app.logger.error('Error: {0}'.format(e)) current_app.logger.error('Error: {0}'.format(e))
history = History(msg=history_message, history = History(msg=history_message,
detail = json.dumps({ detail=json.dumps({
'key': apikey.id, 'key': apikey.id,
'role': apikey.role.name, 'role': apikey.role.name,
'description': apikey.description, 'description': apikey.description,
@ -381,6 +403,7 @@ def edit_key(key_id=None):
create=create, create=create,
plain_key=plain_key) plain_key=plain_key)
@admin_bp.route('/manage-keys', methods=['GET', 'POST']) @admin_bp.route('/manage-keys', methods=['GET', 'POST'])
@login_required @login_required
@operator_role_required @operator_role_required
@ -404,7 +427,7 @@ def manage_keys():
history_apikey_id = apikey.id history_apikey_id = apikey.id
history_apikey_role = apikey.role.name history_apikey_role = apikey.role.name
history_apikey_description = apikey.description history_apikey_description = apikey.description
history_apikey_domains = [ domain.name for domain in apikey.domains] history_apikey_domains = [domain.name for domain in apikey.domains]
apikey.delete() apikey.delete()
except Exception as e: except Exception as e:
@ -412,7 +435,7 @@ def manage_keys():
current_app.logger.info('Delete API key {0}'.format(apikey.id)) current_app.logger.info('Delete API key {0}'.format(apikey.id))
history = History(msg='Delete API key {0}'.format(apikey.id), history = History(msg='Delete API key {0}'.format(apikey.id),
detail = json.dumps({ detail=json.dumps({
'key': history_apikey_id, 'key': history_apikey_id,
'role': history_apikey_role, 'role': history_apikey_role,
'description': history_apikey_description, 'description': history_apikey_description,
@ -427,6 +450,7 @@ def manage_keys():
'msg': 'Key has been removed.' 'msg': 'Key has been removed.'
}), 200) }), 200)
@admin_bp.route('/manage-user', methods=['GET', 'POST']) @admin_bp.route('/manage-user', methods=['GET', 'POST'])
@login_required @login_required
@operator_role_required @operator_role_required
@ -800,7 +824,9 @@ class DetailedHistory():
</table> </table>
""", """,
domaintype=detail_dict['domain_type'], domaintype=detail_dict['domain_type'],
account=Account.get_name_by_id(self=None, account_id=detail_dict['account_id']) if detail_dict['account_id'] != "0" else "None") account=Account.get_name_by_id(self=None, account_id=detail_dict[
'account_id']) if detail_dict[
'account_id'] != "0" else "None")
elif 'authenticator' in detail_dict: # this is a user authentication elif 'authenticator' in detail_dict: # this is a user authentication
self.detailed_msg = render_template_string(""" self.detailed_msg = render_template_string("""
@ -825,9 +851,11 @@ class DetailedHistory():
</tbody> </tbody>
</table> </table>
""", """,
background_rgba="68,157,68" if detail_dict['success'] == 1 else "201,48,44", background_rgba="68,157,68" if detail_dict[
'success'] == 1 else "201,48,44",
username=detail_dict['username'], username=detail_dict['username'],
auth_result="success" if detail_dict['success'] == 1 else "failure", auth_result="success" if detail_dict[
'success'] == 1 else "failure",
authenticator=detail_dict['authenticator'], authenticator=detail_dict['authenticator'],
ip_address=detail_dict['ip_address']) ip_address=detail_dict['ip_address'])
@ -844,7 +872,8 @@ class DetailedHistory():
</table> </table>
""", """,
template_name=DetailedHistory.get_key_val(detail_dict, "name"), template_name=DetailedHistory.get_key_val(detail_dict, "name"),
description=DetailedHistory.get_key_val(detail_dict, "description")) description=DetailedHistory.get_key_val(detail_dict,
"description"))
elif 'Change domain' in history.msg and 'access control' in history.msg: # added or removed a user from a domain elif 'Change domain' in history.msg and 'access control' in history.msg: # added or removed a user from a domain
users_with_access = DetailedHistory.get_key_val(detail_dict, "user_has_access") users_with_access = DetailedHistory.get_key_val(detail_dict, "user_has_access")
@ -868,9 +897,12 @@ class DetailedHistory():
""", """,
keyname=DetailedHistory.get_key_val(detail_dict, "key"), keyname=DetailedHistory.get_key_val(detail_dict, "key"),
rolename=DetailedHistory.get_key_val(detail_dict, "role"), rolename=DetailedHistory.get_key_val(detail_dict, "role"),
description=DetailedHistory.get_key_val(detail_dict, "description"), description=DetailedHistory.get_key_val(detail_dict,
linked_domains=DetailedHistory.get_key_val(detail_dict, "domains" if "domains" in detail_dict else "domain_acl"), "description"),
linked_accounts=DetailedHistory.get_key_val(detail_dict, "accounts")) linked_domains=DetailedHistory.get_key_val(detail_dict,
"domains" if "domains" in detail_dict else "domain_acl"),
linked_accounts=DetailedHistory.get_key_val(detail_dict,
"accounts"))
elif 'Delete API key' in history.msg: elif 'Delete API key' in history.msg:
self.detailed_msg = render_template_string(""" self.detailed_msg = render_template_string("""
@ -883,8 +915,10 @@ class DetailedHistory():
""", """,
keyname=DetailedHistory.get_key_val(detail_dict, "key"), keyname=DetailedHistory.get_key_val(detail_dict, "key"),
rolename=DetailedHistory.get_key_val(detail_dict, "role"), rolename=DetailedHistory.get_key_val(detail_dict, "role"),
description=DetailedHistory.get_key_val(detail_dict, "description"), description=DetailedHistory.get_key_val(detail_dict,
linked_domains=DetailedHistory.get_key_val(detail_dict, "domains")) "description"),
linked_domains=DetailedHistory.get_key_val(detail_dict,
"domains"))
elif 'Update type for domain' in history.msg: elif 'Update type for domain' in history.msg:
self.detailed_msg = render_template_string(""" self.detailed_msg = render_template_string("""
@ -905,8 +939,10 @@ class DetailedHistory():
<tr><td>Domain Master IPs:</td><td>{{ domain_master_ips }}</td></tr> <tr><td>Domain Master IPs:</td><td>{{ domain_master_ips }}</td></tr>
</table> </table>
""", """,
domain_type=DetailedHistory.get_key_val(detail_dict, "domain_type"), domain_type=DetailedHistory.get_key_val(detail_dict,
domain_master_ips=DetailedHistory.get_key_val(detail_dict, "domain_master_ips")) "domain_type"),
domain_master_ips=DetailedHistory.get_key_val(detail_dict,
"domain_master_ips"))
elif DetailedHistory.get_key_val(detail_dict, 'msg') and DetailedHistory.get_key_val(detail_dict, 'status'): elif DetailedHistory.get_key_val(detail_dict, 'msg') and DetailedHistory.get_key_val(detail_dict, 'status'):
self.detailed_msg = render_template_string(''' self.detailed_msg = render_template_string('''
@ -915,7 +951,8 @@ class DetailedHistory():
<tr><td>Message:</td><td>{{ history_msg }}</td></tr> <tr><td>Message:</td><td>{{ history_msg }}</td></tr>
</table> </table>
''', ''',
history_status=DetailedHistory.get_key_val(detail_dict, 'status'), history_status=DetailedHistory.get_key_val(detail_dict,
'status'),
history_msg=DetailedHistory.get_key_val(detail_dict, 'msg')) history_msg=DetailedHistory.get_key_val(detail_dict, 'msg'))
elif 'Update domain' in history.msg and 'associate account' in history.msg: # When an account gets associated or dissociate with domains elif 'Update domain' in history.msg and 'associate account' in history.msg: # When an account gets associated or dissociate with domains
@ -925,8 +962,10 @@ class DetailedHistory():
<tr><td>Dissociate:</td><td>{{ history_dissoc_account }}</td></tr> <tr><td>Dissociate:</td><td>{{ history_dissoc_account }}</td></tr>
</table> </table>
''', ''',
history_assoc_account=DetailedHistory.get_key_val(detail_dict, 'assoc_account'), history_assoc_account=DetailedHistory.get_key_val(detail_dict,
history_dissoc_account=DetailedHistory.get_key_val(detail_dict, 'dissoc_account')) 'assoc_account'),
history_dissoc_account=DetailedHistory.get_key_val(detail_dict,
'dissoc_account'))
# check for lower key as well for old databases # check for lower key as well for old databases
@staticmethod @staticmethod
@ -952,6 +991,7 @@ def convert_histories(histories):
detailedHistories.append(DetailedHistory(histories[i], None)) detailedHistories.append(DetailedHistory(histories[i], None))
return detailedHistories return detailedHistories
@admin_bp.route('/history', methods=['GET', 'POST']) @admin_bp.route('/history', methods=['GET', 'POST'])
@login_required @login_required
@history_access_required @history_access_required
@ -989,16 +1029,13 @@ def history():
'msg': 'Can not remove histories.' 'msg': 'Can not remove histories.'
}), 500) }), 500)
if request.method == 'GET': if request.method == 'GET':
doms = accounts = users = "" doms = accounts = users = ""
if current_user.role.name in [ 'Administrator', 'Operator']: if current_user.role.name in ['Administrator', 'Operator']:
all_domain_names = Domain.query.all() all_domain_names = Domain.query.all()
all_account_names = Account.query.all() all_account_names = Account.query.all()
all_user_names = User.query.all() all_user_names = User.query.all()
for d in all_domain_names: for d in all_domain_names:
doms += d.name + " " doms += d.name + " "
for acc in all_account_names: for acc in all_account_names:
@ -1026,7 +1063,6 @@ def history():
AccountUser.user_id == current_user.id AccountUser.user_id == current_user.id
)).all() )).all()
all_user_names = [] all_user_names = []
for a in all_account_names: for a in all_account_names:
temp = db.session.query(User) \ temp = db.session.query(User) \
@ -1051,17 +1087,21 @@ def history():
accounts += a.name + " " accounts += a.name + " "
for u in all_user_names: for u in all_user_names:
users += u.username + " " users += u.username + " "
return render_template('admin_history.html', all_domain_names=doms, all_account_names=accounts, all_usernames=users) return render_template('admin_history.html', all_domain_names=doms, all_account_names=accounts,
all_usernames=users)
# local_offset is the offset of the utc to the local time # local_offset is the offset of the utc to the local time
# offset must be int # offset must be int
# return the date converted and simplified # return the date converted and simplified
def from_utc_to_local(local_offset, timeframe): def from_utc_to_local(local_offset, timeframe):
offset = str(local_offset *(-1)) offset = str(local_offset * (-1))
date_split = str(timeframe).split(".")[0] date_split = str(timeframe).split(".")[0]
date_converted = datetime.datetime.strptime(date_split, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(minutes=int(offset)) date_converted = datetime.datetime.strptime(date_split, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(
minutes=int(offset))
return date_converted return date_converted
@admin_bp.route('/history_table', methods=['GET', 'POST']) @admin_bp.route('/history_table', methods=['GET', 'POST'])
@login_required @login_required
@history_access_required @history_access_required
@ -1097,7 +1137,7 @@ def history_table(): # ajax call data
lim = int(Setting().get('max_history_records')) # max num of records lim = int(Setting().get('max_history_records')) # max num of records
if request.method == 'GET': if request.method == 'GET':
if current_user.role.name in [ 'Administrator', 'Operator' ]: if current_user.role.name in ['Administrator', 'Operator']:
base_query = History.query base_query = History.query
else: else:
# if the user isn't an administrator or operator, # if the user isn't an administrator or operator,
@ -1115,28 +1155,35 @@ def history_table(): # ajax call data
)) ))
domain_name = request.args.get('domain_name_filter') if request.args.get('domain_name_filter') != None \ domain_name = request.args.get('domain_name_filter') if request.args.get('domain_name_filter') != None \
and len(request.args.get('domain_name_filter')) != 0 else None and len(
request.args.get('domain_name_filter')) != 0 else None
account_name = request.args.get('account_name_filter') if request.args.get('account_name_filter') != None \ account_name = request.args.get('account_name_filter') if request.args.get('account_name_filter') != None \
and len(request.args.get('account_name_filter')) != 0 else None and len(
request.args.get('account_name_filter')) != 0 else None
user_name = request.args.get('auth_name_filter') if request.args.get('auth_name_filter') != None \ user_name = request.args.get('auth_name_filter') if request.args.get('auth_name_filter') != None \
and len(request.args.get('auth_name_filter')) != 0 else None and len(request.args.get('auth_name_filter')) != 0 else None
min_date = request.args.get('min') if request.args.get('min') != None and len( request.args.get('min')) != 0 else None min_date = request.args.get('min') if request.args.get('min') != None and len(
request.args.get('min')) != 0 else None
if min_date != None: # get 1 day earlier, to check for timezone errors if min_date != None: # get 1 day earlier, to check for timezone errors
min_date = str(datetime.datetime.strptime(min_date, '%Y-%m-%d') - datetime.timedelta(days=1)) min_date = str(datetime.datetime.strptime(min_date, '%Y-%m-%d') - datetime.timedelta(days=1))
max_date = request.args.get('max') if request.args.get('max') != None and len( request.args.get('max')) != 0 else None max_date = request.args.get('max') if request.args.get('max') != None and len(
request.args.get('max')) != 0 else None
if max_date != None: # get 1 day later, to check for timezone errors if max_date != None: # get 1 day later, to check for timezone errors
max_date = str(datetime.datetime.strptime(max_date, '%Y-%m-%d') + datetime.timedelta(days=1)) max_date = str(datetime.datetime.strptime(max_date, '%Y-%m-%d') + datetime.timedelta(days=1))
tzoffset = request.args.get('tzoffset') if request.args.get('tzoffset') != None and len(request.args.get('tzoffset')) != 0 else None tzoffset = request.args.get('tzoffset') if request.args.get('tzoffset') != None and len(
request.args.get('tzoffset')) != 0 else None
changed_by = request.args.get('user_name_filter') if request.args.get('user_name_filter') != None \ changed_by = request.args.get('user_name_filter') if request.args.get('user_name_filter') != None \
and len(request.args.get('user_name_filter')) != 0 else None and len(
request.args.get('user_name_filter')) != 0 else None
""" """
Auth methods: LOCAL, Github OAuth, Azure OAuth, SAML, OIDC OAuth, Google OAuth Auth methods: LOCAL, Github OAuth, Azure OAuth, SAML, OIDC OAuth, Google OAuth
""" """
auth_methods = [] auth_methods = []
if (request.args.get('auth_local_only_checkbox') is None \ if (request.args.get('auth_local_only_checkbox') is None \
and request.args.get('auth_oauth_only_checkbox') is None \ and request.args.get('auth_oauth_only_checkbox') is None \
and request.args.get('auth_saml_only_checkbox') is None and request.args.get('auth_all_checkbox') is None): and request.args.get('auth_saml_only_checkbox') is None and request.args.get(
'auth_all_checkbox') is None):
auth_methods = [] auth_methods = []
if request.args.get('auth_all_checkbox') == "on": if request.args.get('auth_all_checkbox') == "on":
auth_methods.append("") auth_methods.append("")
@ -1152,11 +1199,8 @@ def history_table(): # ajax call data
else: else:
changelog_only = False changelog_only = False
# users cannot search for authentication # users cannot search for authentication
if user_name != None and current_user.role.name not in [ 'Administrator', 'Operator']: if user_name != None and current_user.role.name not in ['Administrator', 'Operator']:
histories = [] histories = []
elif domain_name != None: elif domain_name != None:
@ -1165,8 +1209,11 @@ def history_table(): # ajax call data
.filter( .filter(
db.and_( db.and_(
db.or_( db.or_(
History.msg.like("%domain "+ domain_name) if domain_name != "*" else History.msg.like("%domain%"), History.msg.like("%domain " + domain_name) if domain_name != "*" else History.msg.like(
History.msg.like("%domain "+ domain_name + " access control") if domain_name != "*" else History.msg.like("%domain%access control") "%domain%"),
History.msg.like(
"%domain " + domain_name + " access control") if domain_name != "*" else History.msg.like(
"%domain%access control")
), ),
History.created_on <= max_date if max_date != None else True, History.created_on <= max_date if max_date != None else True,
History.created_on >= min_date if min_date != None else True, History.created_on >= min_date if min_date != None else True,
@ -1214,14 +1261,19 @@ def history_table(): # ajax call data
) )
).order_by(History.created_on.desc()) \ ).order_by(History.created_on.desc()) \
.limit(lim).all() .limit(lim).all()
elif user_name != None and current_user.role.name in [ 'Administrator', 'Operator']: # only admins can see the user login-logouts elif user_name != None and current_user.role.name in ['Administrator',
'Operator']: # only admins can see the user login-logouts
histories = History.query \ histories = History.query \
.filter( .filter(
db.and_( db.and_(
db.or_( db.or_(
History.msg.like("User "+ user_name + " authentication%") if user_name != "*" and user_name != None else History.msg.like("%authentication%"), History.msg.like(
History.msg.like("User "+ user_name + " was not authorized%") if user_name != "*" and user_name != None else History.msg.like("User%was not authorized%") "User " + user_name + " authentication%") if user_name != "*" and user_name != None else History.msg.like(
"%authentication%"),
History.msg.like(
"User " + user_name + " was not authorized%") if user_name != "*" and user_name != None else History.msg.like(
"User%was not authorized%")
), ),
History.created_on <= max_date if max_date != None else True, History.created_on <= max_date if max_date != None else True,
History.created_on >= min_date if min_date != None else True, History.created_on >= min_date if min_date != None else True,
@ -1236,7 +1288,8 @@ def history_table(): # ajax call data
temp.append(h) temp.append(h)
break break
histories = temp histories = temp
elif (changed_by != None or max_date != None) and current_user.role.name in [ 'Administrator', 'Operator'] : # select changed by and date filters only elif (changed_by != None or max_date != None) and current_user.role.name in ['Administrator',
'Operator']: # select changed by and date filters only
histories = History.query \ histories = History.query \
.filter( .filter(
db.and_( db.and_(
@ -1246,7 +1299,8 @@ def history_table(): # ajax call data
) )
) \ ) \
.order_by(History.created_on.desc()).limit(lim).all() .order_by(History.created_on.desc()).limit(lim).all()
elif (changed_by != None or max_date != None): # special filtering for user because one user does not have access to log-ins logs elif (
changed_by != None or max_date != None): # special filtering for user because one user does not have access to log-ins logs
histories = base_query \ histories = base_query \
.filter( .filter(
db.and_( db.and_(
@ -1264,7 +1318,7 @@ def history_table(): # ajax call data
) )
).order_by(History.created_on.desc()).limit(lim).all() ).order_by(History.created_on.desc()).limit(lim).all()
else: # default view else: # default view
if current_user.role.name in [ 'Administrator', 'Operator']: if current_user.role.name in ['Administrator', 'Operator']:
histories = History.query.order_by(History.created_on.desc()).limit(lim).all() histories = History.query.order_by(History.created_on.desc()).limit(lim).all()
else: else:
histories = db.session.query(History) \ histories = db.session.query(History) \
@ -1289,14 +1343,15 @@ def history_table(): # ajax call data
max_date_split = max_date.split()[0] max_date_split = max_date.split()[0]
for i, history_rec in enumerate(detailedHistories): for i, history_rec in enumerate(detailedHistories):
local_date = str(from_utc_to_local(int(tzoffset), history_rec.history.created_on).date()) local_date = str(from_utc_to_local(int(tzoffset), history_rec.history.created_on).date())
if (min_date != None and local_date == min_date_split) or (max_date != None and local_date == max_date_split): if (min_date != None and local_date == min_date_split) or (
max_date != None and local_date == max_date_split):
detailedHistories[i] = None detailedHistories[i] = None
# Remove elements previously flagged as None # Remove elements previously flagged as None
detailedHistories = [h for h in detailedHistories if h is not None] detailedHistories = [h for h in detailedHistories if h is not None]
return render_template('admin_history_table.html', histories=detailedHistories, len_histories=len(detailedHistories), lim=lim) return render_template('admin_history_table.html', histories=detailedHistories,
len_histories=len(detailedHistories), lim=lim)
@admin_bp.route('/setting/basic', methods=['GET']) @admin_bp.route('/setting/basic', methods=['GET'])
@ -1528,7 +1583,7 @@ def setting_authentication():
Setting().set('autoprovisioning_attribute', Setting().set('autoprovisioning_attribute',
request.form.get('autoprovisioning_attribute')) request.form.get('autoprovisioning_attribute'))
if request.form.get('autoprovisioning')=='ON': if request.form.get('autoprovisioning') == 'ON':
if validateURN(request.form.get('urn_value')): if validateURN(request.form.get('urn_value')):
Setting().set('urn_value', Setting().set('urn_value',
request.form.get('urn_value')) request.form.get('urn_value'))
@ -1542,7 +1597,6 @@ def setting_authentication():
Setting().set('purge', True Setting().set('purge', True
if request.form.get('purge') == 'ON' else False) if request.form.get('purge') == 'ON' else False)
result = {'status': True, 'msg': 'Saved successfully'} result = {'status': True, 'msg': 'Saved successfully'}
elif conf_type == 'google': elif conf_type == 'google':
google_oauth_enabled = True if request.form.get( google_oauth_enabled = True if request.form.get(
@ -1738,7 +1792,7 @@ def create_template():
result = t.create() result = t.create()
if result['status'] == 'ok': if result['status'] == 'ok':
history = History(msg='Add domain template {0}'.format(name), history = History(msg='Add domain template {0}'.format(name),
detail = json.dumps({ detail=json.dumps({
'name': name, 'name': name,
'description': description 'description': description
}), }),
@ -1785,7 +1839,7 @@ def create_template_from_zone():
result = t.create() result = t.create()
if result['status'] == 'ok': if result['status'] == 'ok':
history = History(msg='Add domain template {0}'.format(name), history = History(msg='Add domain template {0}'.format(name),
detail = json.dumps({ detail=json.dumps({
'name': name, 'name': name,
'description': description 'description': description
}), }),
@ -1918,7 +1972,7 @@ def apply_records(template):
history = History( history = History(
msg='Apply domain template record changes to domain template {0}' msg='Apply domain template record changes to domain template {0}'
.format(template), .format(template),
detail = json.dumps(jdata), detail=json.dumps(jdata),
created_by=current_user.username) created_by=current_user.username)
history.add() history.add()
return make_response(jsonify(result), 200) return make_response(jsonify(result), 200)
@ -1948,7 +2002,7 @@ def delete_template(template):
if result['status'] == 'ok': if result['status'] == 'ok':
history = History( history = History(
msg='Deleted domain template {0}'.format(template), msg='Deleted domain template {0}'.format(template),
detail = json.dumps({'name': template}), detail=json.dumps({'name': template}),
created_by=current_user.username) created_by=current_user.username)
history.add() history.add()
return redirect(url_for('admin.templates')) return redirect(url_for('admin.templates'))
@ -2001,30 +2055,38 @@ def global_search():
else: else:
pass pass
return render_template('admin_global_search.html', domains=domains, records=records, comments=comments) params: dict = {
'query': query if query is not None else '',
'domains': domains,
'records': records,
'comments': comments,
}
return render_template('admin_global_search.html', **params)
def validateURN(value): def validateURN(value):
NID_PATTERN = re.compile(r'^[0-9a-z][0-9a-z-]{1,31}$', flags=re.IGNORECASE) NID_PATTERN = re.compile(r'^[0-9a-z][0-9a-z-]{1,31}$', flags=re.IGNORECASE)
NSS_PCHAR = '[a-z0-9-._~]|%[a-f0-9]{2}|[!$&\'()*+,;=]|:|@' NSS_PCHAR = '[a-z0-9-._~]|%[a-f0-9]{2}|[!$&\'()*+,;=]|:|@'
NSS_PATTERN = re.compile(fr'^({NSS_PCHAR})({NSS_PCHAR}|/|\?)*$', re.IGNORECASE) NSS_PATTERN = re.compile(fr'^({NSS_PCHAR})({NSS_PCHAR}|/|\?)*$', re.IGNORECASE)
prefix=value.split(':') prefix = value.split(':')
if (len(prefix)<3): if (len(prefix) < 3):
current_app.logger.warning( "Too small urn prefix" ) current_app.logger.warning("Too small urn prefix")
return False return False
urn=prefix[0] urn = prefix[0]
nid=prefix[1] nid = prefix[1]
nss=value.replace(urn+":"+nid+":", "") nss = value.replace(urn + ":" + nid + ":", "")
if not urn.lower()=="urn": if not urn.lower() == "urn":
current_app.logger.warning( urn + ' contains invalid characters ' ) current_app.logger.warning(urn + ' contains invalid characters ')
return False return False
if not re.match(NID_PATTERN, nid.lower()): if not re.match(NID_PATTERN, nid.lower()):
current_app.logger.warning( nid + ' contains invalid characters ' ) current_app.logger.warning(nid + ' contains invalid characters ')
return False return False
if not re.match(NSS_PATTERN, nss): if not re.match(NSS_PATTERN, nss):
current_app.logger.warning( nss + ' contains invalid characters ' ) current_app.logger.warning(nss + ' contains invalid characters ')
return False return False
return True return True

View File

@ -45,3 +45,15 @@ 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,28 +1,26 @@
{% 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 %}
<small>{% if create %}Account{% else %}{{ account.name }}{% endif %}</small>
</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('dashboard.dashboard') }}">Home</a></li> <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"><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> <li class="breadcrumb-item active">{{ action_label }} Account</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -31,30 +29,35 @@
{% 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">
<h3 class="card-title">{% if create %}Add{% else %}Edit{% endif %} Account</h3>
</div>
<form role="form" method="post" action="{% if create %}{{ url_for('admin.edit_account') }}{% else %}{{ url_for('admin.edit_account', account_name=account.name) }}{% endif %}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="create" value="{{ create }}"> <input type="hidden" name="create" value="{{ create }}">
<div class="card card-outline card-primary shadow">
<div class="card-header">
<h3 class="card-title">Account Editor</h3>
</div>
<!-- /.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">
&times;
</button>
<h4><i class="fa-solid fa-ban"></i> Error!</h4> <h4><i class="fa-solid fa-ban"></i> Error!</h4>
{{ error }} {{ error }}
</div> </div>
<span class="help-block">{{ error }}</span> <span class="help-block">{{ error }}</span>
{% endif %} {% endif %}
<div class="form-group has-feedback {% if invalid_accountname or duplicate_accountname %}has-error{% endif %}"> <div
class="form-group has-feedback {% if invalid_accountname or duplicate_accountname %}has-error{% endif %}">
<label class="control-label" for="accountname">Name</label> <label class="control-label" for="accountname">Name</label>
<input type="text" class="form-control" placeholder="Account Name (required)" <input type="text" class="form-control" placeholder="Account Name (required)"
name="accountname" {% if account %}value="{{ account.name }}" {% endif %} name="accountname" id="accountname"
{% if account %}value="{{ account.name }}" {% endif %}
{% if not create %}disabled{% endif %} required> {% if not create %}disabled{% endif %} required>
<span class="form-control-feedback"></span> <span class="form-control-feedback"></span>
{% if invalid_accountname %} {% if invalid_accountname %}
@ -68,25 +71,30 @@
<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"
{% if account %}value="{{ account.description }}" {% endif %}>
<span class="orm-control-feedback"></span> <span class="orm-control-feedback"></span>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label class="control-label" for="accountcontact">Contact Person</label> <label class="control-label" for="accountcontact">Contact Person</label>
<input type="text" class="form-control" placeholder="Contact Person (optional)" <input type="text" class="form-control" placeholder="Contact Person (optional)"
name="accountcontact" {% if account %}value="{{ account.contact }}" {% endif %}> name="accountcontact" id="accountcontact"
{% if account %}value="{{ account.contact }}" {% endif %}>
<span class="form-control-feedback"></span> <span class="form-control-feedback"></span>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label class="control-label" for="accountmail">Mail Address</label> <label class="control-label" for="accountmail">Mail Address</label>
<input type="email" class="form-control" placeholder="Mail Address (optional)" <input type="email" class="form-control" placeholder="Mail Address (optional)"
name="accountmail" {% if account %}value="{{ account.mail }}" {% endif %}> name="accountmail" id="accountmail"
{% if account %}value="{{ account.mail }}" {% endif %}>
<span class="form-control-feedback"></span> <span class="form-control-feedback"></span>
</div> </div>
</div> </div>
<!-- /.card-body -->
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Access Control</h3> <h3 class="card-title">Access Control</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<p>Users on the right have access to manage records in all domains <p>Users on the right have access to manage records in all domains
associated with the account. associated with the account.
@ -103,22 +111,30 @@
</select> </select>
</div> </div>
</div> </div>
<!-- /.card-body -->
<div class="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. <p>Domains on the right are associated with the account. Red marked domain names are
Moving already associated domains to this account will overwrite the previous associated account. already associated with other accounts.
Moving already associated domains to this account will overwrite the previous
associated account.
</p> </p>
<p>Hover over the red domain names to show the associated account. Click on domains to move between columns.</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"> <div class="form-group col-2">
<select multiple="multiple" class="form-control" id="account_domains" name="account_domains"> <select multiple="multiple" class="form-control" id="account_domains"
name="account_domains">
{% for domain in domains %} {% for domain in domains %}
{% if account != None and domain.account_id != None and account.id != domain.account_id %} {% if account != None and domain.account_id != None and account.id != domain.account_id %}
{% with 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 }}"> <option style="color: red" data-toggle="tooltip"
title="Associated with: {{ accounts[account_id].name }}"
value="{{ domain.name }}">
{{ domain.name }} {{ domain.name }}
</option> </option>
{% endwith %} {% endwith %}
{% else %} {% else %}
<option {% if account.id == domain.account_id %}selected{% endif %} value="{{ domain.name }}"> <option {% if account.id == domain.account_id %}selected{% endif %}
value="{{ domain.name }}">
{{ domain.name }} {{ domain.name }}
</option> </option>
{% endif %} {% endif %}
@ -126,48 +142,67 @@
</select> </select>
</div> </div>
</div> </div>
<!-- /.card-body -->
<div class="card-footer"> <div class="card-footer">
<button type="button" class="btn btn-secondary float-left" onclick="window.location.href='{{ url_for('admin.manage_account') }}'"> <button type="button" class="btn btn-secondary float-left" title="Cancel"
onclick="window.location.href='{{ url_for('admin.manage_account') }}'">
Cancel Cancel
</button> </button>
<button type="submit" class="btn btn-primary float-right"> <button type="submit" class="btn btn-primary float-right"
{% if create %}Create{% else %}Update{% endif %} Account title="{{ action_label }} Account">
{{ action_label }} Account
</button> </button>
</div> </div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form> </form>
</div> </div>
</div> <!-- /.col -->
<div class="col-8">
<div class="card"> <div class="col-12 col-sm-6 col-lg-8">
<div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Help with creating a new account</h3> <h3 class="card-title">Account Editor Help</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<p> <p>
An account allows grouping of domains belonging to a particular entity, such as a customer or An account allows grouping of domains belonging to a particular entity, such as a
customer or
department. department.
<br /> </p>
A domain can be assigned to an account upon domain creation or through the domain administration <p>
A domain can be assigned to an account upon domain creation or through the domain
administration
page. page.
</p> </p>
<p>Fill in all the fields to the in the form to the left.</p> <p>Fill in all the fields to the in the form to the left.</p>
<p> <p>
<strong>Name</strong> is an account identifier. It will be lowercased and can contain alphanumeric <strong>Name</strong> is an account identifier. It will be lowercase and can contain
characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens and underscores (no space or other special character is allowed) alphanumeric
{% else %} (no extra character is allowed){% endif %}.<br /> characters{% if SETTING.get('account_name_extra_chars') %}, dots, hyphens and
<strong>Description</strong> is a user friendly name for this account.<br /> underscores (no space or other special character is allowed)
<strong>Contact person</strong> is the name of a contact person at the account.<br /> {% 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. <strong>Mail Address</strong> is an e-mail address for the contact person.
</p> </p>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</section> <!-- /.container-fluid -->
</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}'>`,
@ -205,7 +240,8 @@
} }
}); });
} }
addMultiSelect("#account_multi_user", "Username") addMultiSelect("#account_multi_user", "Username")
addMultiSelect("#account_domains", "Domain") addMultiSelect("#account_domains", "Domain")
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,30 +1,28 @@
{% 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
<small>{% if create %}Add API Key{% else %}Edit API Key - {{ key.id }}{% endif %}</small>
</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('dashboard.dashboard') }}">Dashboard</a></li> <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"><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> <li class="breadcrumb-item active">{{ action_label }} API Key</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -33,18 +31,18 @@
{% 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">
<h3 class="card-title">{% if create %}Add{% else %}Edit{% endif %} Key</h3>
</div>
<form role="form" method="post"
action="{% if create %}{{ url_for('admin.edit_key') }}{% else %}{{ url_for('admin.edit_key', key_id=key.id) }}{% endif %}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="create" value="{{ create }}"> <input type="hidden" name="create" value="{{ create }}">
<div class="card card-outline card-primary shadow">
<div class="card-header">
<h3 class="card-title">API Key Editor</h3>
</div>
<!-- /.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>
@ -58,88 +56,110 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<!-- /.form-group -->
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label class="control-label" for="description">Description</label> <label class="control-label" for="description">Description</label>
<input type="text" class="form-control" placeholder="Description" name="description" <input type="text" class="form-control" placeholder="Description" name="description"
{% if key is not none %} value="{{ key.description }}" {% endif %}> {% if key is not none %} value="{{ key.description }}" {% endif %}>
<span class="glyphicon glyphicon-pencil form-control-feedback"></span> <span class="glyphicon glyphicon-pencil form-control-feedback"></span>
</div> </div>
<!-- /.form-group -->
</div> </div>
<!-- /.card-body -->
<div class="card-header key-opts"{% if hide_opts %} style="display: none;"{% endif %}> <div class="card-header key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
<h3 class="card-title">Accounts Access Control</h3> <h3 class="card-title">Accounts Access Control</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}> <div class="card-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
<p>This key will be linked to the accounts on the right,</p> <p>This key will be linked to the accounts on the right,</p>
<p>thus granting access to domains owned by the selected accounts.</p> <p>thus granting access to domains owned by the selected accounts.</p>
<p>Click on accounts to move between the columns.</p> <p>Click on accounts to move between the columns.</p>
<div class="form-group col-2"> <div class="form-group col-2">
<select multiple="multiple" class="form-control" id="key_multi_account" name="key_multi_account"> <select multiple="multiple" class="form-control" id="key_multi_account"
name="key_multi_account">
{% for account in accounts %} {% for account in accounts %}
<option {% if key and account in key.accounts %}selected{% endif %} value="{{ account.name }}">{{ account.name }}</option> <option {% if key and account in key.accounts %}selected{% endif %}
value="{{ account.name }}">{{ account.name }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<!-- /.form-group -->
</div> </div>
<!-- /.card-body -->
<div class="card-header key-opts"{% if hide_opts %} style="display: none;"{% endif %}> <div class="card-header key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
<h3 class="card-title">Domain Access Control</h3> <h3 class="card-title">Domain Access Control</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}> <div class="card-body key-opts"{% if hide_opts %} style="display: none;"{% endif %}>
<p>This key will have acess to the domains on the right.</p> <p>This key will have access to the domains on the right.</p>
<p>Click on domains to move between the columns.</p> <p>Click on domains to move between the columns.</p>
<div class="form-group col-2"> <div class="form-group col-2">
<select multiple="multiple" class="form-control" id="key_multi_domain" <select multiple="multiple" class="form-control" id="key_multi_domain"
name="key_multi_domain"> name="key_multi_domain">
{% for domain in domains %} {% for domain in domains %}
<option {% if key and domain in key.domains %}selected{% endif %} value="{{ domain.name }}">{{ domain.name }}</option> <option {% if key and domain in key.domains %}selected{% endif %}
value="{{ domain.name }}">{{ domain.name }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
</div> </div>
<!-- /.card-body -->
<div class="card-footer"> <div class="card-footer">
<button type="button" class="btn btn-secondary float-left" onclick="window.location.href='{{ url_for('admin.manage_keys') }}'"> <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 <i class="fa-solid fa-window-close"></i>&nbsp;Cancel
</button> </button>
<button type="submit" class="btn btn-primary float-right" id="key_submit"> <button type="submit" class="btn btn-primary float-right"
<i class="fa-solid fa-save"></i>&nbsp;{% if create %}Create{% else %}Update{% endif %} Key title="{{ action_label }} API Key" id="key_submit">
<i class="fa-solid fa-save"></i>&nbsp;{{ action_label }} API Key
</button> </button>
</div> </div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form> </form>
</div> </div>
</div> <!-- /.col -->
<div class="col-8">
<div class="card"> <div class="col-12 col-sm-6 col-lg-8">
<div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Help with {% if create %}creating a new{% else%}updating a{% endif %} key <h3 class="card-title">API Key Editor Help</h3>
</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<p>Fill in all the fields in the form to the left.</p> <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>Role</strong> The role of the key.</p>
<p><strong>Description</strong> The key description.</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> <p><strong>Access Control</strong> The domains or accounts which the key has access to.</p>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</section> <!-- /.container-fluid -->
</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){ if (selectedRole == "User" && selectedDomains == 0 && selectedAccounts == 0) {
var warning = "User role must have at least one account or one domain bound. None selected."; var warning = "User role must have at least one account or one domain bound. None selected.";
e.preventDefault(e); e.preventDefault(e);
warn_modal.modal('show'); warn_modal.modal('show');
@ -148,24 +168,37 @@
warn_modal.find('.modal-body p').text(warning); warn_modal.find('.modal-body p').text(warning);
warn_modal.find('#button_key_confirm_warn').click(clearModal); warn_modal.find('#button_key_confirm_warn').click(clearModal);
}); });
function clearModal(){
function clearModal() {
$("#modal_warning").modal('hide'); $("#modal_warning").modal('hide');
} }
$('#key_role').on('change', function (e) { $('#key_role').on('change', function (e) {
var optionSelected = $("option:selected", this); var optionSelected = $("option:selected", this);
if (this.value != "User") { if (this.value != "User") {
// Clear the visible list // Clear the visible list
$('#ms-key_multi_domain .ms-selection li').each(function(){ $(this).css('display', 'none');}) $('#ms-key_multi_domain .ms-selection li').each(function () {
$('#ms-key_multi_domain .ms-selectable li').each(function(){ $(this).css('display', '');}) $(this).css('display', 'none');
$('#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', '');}) $('#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 // Deselect invisible selectbox
$('#key_multi_domain option:selected').each(function(){ $(this).prop('selected', false);}) $('#key_multi_domain option:selected').each(function () {
$('#key_multi_account option:selected').each(function(){ $(this).prop('selected', false);}) $(this).prop('selected', false);
})
$('#key_multi_account option:selected').each(function () {
$(this).prop('selected', false);
})
// Hide the lists // Hide the lists
$(".key-opts").hide(); $(".key-opts").hide();
} } else {
else {
$(".key-opts").show(); $(".key-opts").show();
} }
}); });
@ -254,10 +287,11 @@
modal.modal('hide'); modal.modal('hide');
} }
{% endif %} {% endif %}
</script> </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">
@ -276,13 +310,14 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<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"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">WARNING</h5> <h5 class="modal-title">WARNING</h5>
<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"
id="button_close_warn_modal">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
@ -299,5 +334,5 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,28 +1,26 @@
{% 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
<small>{% if create %}New user{% else %}Edit user {{ user.username }}{% endif %}</small>
</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('dashboard.dashboard') }}">Dashboard</a></li> <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"><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> <li class="breadcrumb-item active">{{ action_label }} User</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -31,22 +29,24 @@
{% 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">
<h3 class="card-title">{% if create %}Add{% else %}Edit{% endif %} user</h3>
</div>
<form role="form" method="post"
action="{% if create %}{{ url_for('admin.edit_user') }}{% else %}{{ url_for('admin.edit_user', user_username=user.username) }}{% endif %}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="create" value="{{ create }}"> <input type="hidden" name="create" value="{{ create }}">
<div class="card card-outline card-primary shadow">
<div class="card-header with-border">
<h3 class="card-title">User Editor</h3>
</div>
<!-- /.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">
&times;
</button>
<h4><i class="fa-solid fa-ban"></i> Error!</h4> <h4><i class="fa-solid fa-ban"></i> Error!</h4>
{{ error }} {{ error }}
</div> </div>
@ -55,12 +55,14 @@
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label class="control-label" for="firstname">First Name</label> <label class="control-label" for="firstname">First Name</label>
<input type="text" class="form-control" placeholder="First Name" name="firstname" <input type="text" class="form-control" placeholder="First Name" name="firstname"
id="firstname"
{% if user %}value="{{ user.firstname }}" {% endif %}> {% if user %}value="{{ user.firstname }}" {% endif %}>
<span class="form-control-feedback"></span> <span class="form-control-feedback"></span>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label class="control-label" for="lastname">Last Name</label> <label class="control-label" for="lastname">Last Name</label>
<input type="text" class="form-control" placeholder="Last name" name="lastname" <input type="text" class="form-control" placeholder="Last name" name="lastname"
id="lastname"
{% if user %}value="{{ user.lastname }}" {% endif %}> {% if user %}value="{{ user.lastname }}" {% endif %}>
<span class="form-control-feedback"></span> <span class="form-control-feedback"></span>
</div> </div>
@ -70,32 +72,34 @@
{% if user %}value="{{ user.email }}" {% endif %}> {% if user %}value="{{ user.email }}" {% endif %}>
<span class="form-control-feedback"></span> <span class="form-control-feedback"></span>
</div> </div>
<p class="login-box-msg">Enter the account details below</p>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label class="control-label" for="username">Username</label> <label class="control-label" for="username">Username</label>
<input type="text" class="form-control" placeholder="Username" name="username" <input type="text" class="form-control" placeholder="Username" name="username"
id="username"
{% if user %}value="{{ user.username }}" {% endif %} {% if user %}value="{{ user.username }}" {% endif %}
{% if not create %}disabled{% endif %}> {% if not create %}disabled{% endif %}>
<span class="form-control-feedback"></span> <span class="form-control-feedback"></span>
</div> </div>
<div class="form-group has-feedback {% if blank_password %}has-error{% endif %}"> <div class="form-group has-feedback {% if blank_password %}has-error{% endif %}">
<label class="control-label" for="username">Password</label> <label class="control-label" for="password">Password</label>
<input type="password" class="form-control" <input type="password" class="form-control"
placeholder="Password {% if create %}(Required){% else %}(Leave blank to keep unchanged){% endif %}" placeholder="Password {% if create %}(Required){% else %}(Leave blank to keep unchanged){% endif %}"
name="password"> name="password" id="password">
<span class="form-control-feedback"></span> <span class="form-control-feedback"></span>
{% if blank_password %} {% if blank_password %}
<span class="help-block">The password cannot be blank.</span> <span class="help-block">The password cannot be blank.</span>
{% endif %} {% endif %}
</div> </div>
</div> </div>
<!-- /.card-body -->
<div class="card-footer"> <div class="card-footer">
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary" title="{{ action_label }} User">
{% if create %}Create{% else %}Update{% endif %} User {{ action_label }} User
</button> </button>
</div> </div>
</form> <!-- /.card-footer -->
</div> </div>
</form>
{% if not create %} {% if not create %}
{% if user.otp_secret %} {% if user.otp_secret %}
<div class="card"> <div class="card">
@ -103,14 +107,16 @@
<h3 class="card-title">Two Factor Authentication</h3> <h3 class="card-title">Two Factor Authentication</h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<p>If two factor authentication is configured for this user and is causing problems due to a lost device or <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. technical issue, it can be disabled here.
</p> </p>
<p>The user will need to reconfigure two factor authentication, to re-enable it.</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> <p><strong>Beware: This could compromise security!</strong></p>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<button type="button" class="btn btn-warning button_otp_disable" id="{{ user.username }}"> <button type="button" class="btn btn-warning button_otp_disable"
id="{{ user.username }}">
Disable Two Factor Authentication Disable Two Factor Authentication
</button> </button>
</div> </div>
@ -118,34 +124,42 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
<div class="col-8"> <!-- /.col -->
<div class="card">
<div class="col-12 col-sm-6 col-lg-8">
<div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title"> <h3 class="card-title">User Editor Help</h3>
Help with {% if create %}creating a new{% else%}editing a{% endif %} user
</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<p>Fill in all the fields to the in the form to the left.</p> <p>Fill in all the fields to the in the form to the left.</p>
{% if create %} {% if create %}
<p><strong>Newly created users do not have access to any domains.</strong> You will need to grant <p><strong>Newly created users do not have access to any domains.</strong> You will need
access to the user once it is created via the domain management buttons on the dashboard. to grant
access to the user once it is created via the domain management buttons on the
dashboard.
</p> </p>
{% else %} {% else %}
<p><strong>Username</strong> cannot be changed.</p> <p><strong>Username</strong> cannot be changed.</p>
<p><strong>Password</strong> can be left empty to keep the current password.</p> <p><strong>Password</strong> can be left empty to keep the current password.</p>
{% endif %} {% endif %}
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</section> <!-- /.container-fluid -->
</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');
@ -161,10 +175,11 @@
}) })
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">
@ -186,5 +201,5 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,32 +1,28 @@
{% 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
<small>Search for domains, records and comments directly from PDNS API</small>
</h1>
</div> </div>
<div class="col-sm-6"> <!-- /.col -->
<div class="col-6">
<ol class="breadcrumb float-sm-right"> <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('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Global Search</li> <li class="breadcrumb-item active">Global Search</li>
</ol> </ol>
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
<!-- /.container-fluid -->
</div> </div>
<!-- /.content-header -->
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -34,41 +30,63 @@
<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-header">
<h3 class="card-title">Global Search</h3>
</div>
<div class="card-body">
<form action="" method="get"> <form action="" method="get">
<div class="card card-outline card-primary shadow">
<div class="card-header">
<h3 class="card-title">Search Form</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="callout callout-info">
<p>This tool can be used to search for domains, records, and comments via the PDNS
API.</p>
</div>
<!-- /.callout -->
<div class="form-group">
<div class="input-group"> <div class="input-group">
<input type="text" name="q" class="form-control" placeholder="Your keyword..."> <div class="input-group-prepend">
<div class="input-group-btn"> <label id="txtQuery" class="input-group-text">
<button type="submit" class="btn btn-success"><i class="fa fa-search"></i></button> 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> </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> </form>
<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> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
<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> </div>
<div class="card-body"> <!-- /.card-header -->
<table id="tbl_domain" class="table table-bordered table-striped"> <div class="card-body table-responsive">
<table id="tbl_domain" class="table table-bordered table-striped table-hover table-sm">
<thead> <thead>
<tr> <tr>
<th>Domain</th> <th>Zone Name</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -82,18 +100,23 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
<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> </div>
<div class="card-body"> <!-- /.card-header -->
<table id="tbl_record" class="table table-bordered table-striped"> <div class="card-body table-responsive">
<table id="tbl_record" class="table table-bordered table-striped table-hover table-sm">
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
@ -118,23 +141,28 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
<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> </div>
<div class="card-body"> <!-- /.card-header -->
<table id="tbl_comment" class="table table-bordered table-striped"> <div class="card-body table-responsive">
<table id="tbl_comment" class="table table-bordered table-striped table-hover table-sm">
<thead> <thead>
<tr> <tr>
<th>Comment</th> <th>Comment</th>
<th>Record</th> <th>Record</th>
<th>Domain</th> <th>Zone</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -150,20 +178,57 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
<!-- /.container-fluid -->
</section> </section>
{% endblock %} {% 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 %}
{% 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,
@ -172,11 +237,11 @@
] ]
}); });
// 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,
@ -185,11 +250,11 @@
] ]
}); });
// 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,

View File

@ -1,22 +1,13 @@
{% extends "base.html" %} {% extends "base.html" %}
{% set active_page = "admin_history" %} {% set active_page = "admin_history" %}
{% block title %}<title>Activity - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
History - {{ 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">Activity</h1>
History
<small>Recent Events</small>
</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">
@ -30,19 +21,22 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% import 'applied_change_macro.html' as applied_change_macro %} {% import 'applied_change_macro.html' as applied_change_macro %}
<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 card-outline card-secondary"> <div class="card card-outline card-secondary">
<div class="card-header with-border"> <div class="card-header with-border">
<h3 class="card-title">History Management</h3> <h3 class="card-title">Activity Search</h3>
{% if current_user.role.name != 'User' %} {% if current_user.role.name == 'Administrator' %}
<button type="button" class="btn btn-danger float-right" data-toggle="modal" data-target="#modal_clear_history" {% if current_user.role.name != 'Administrator' %}disabled{% endif %}> <div class="card-tools">
<button type="button" class="btn btn-danger" data-toggle="modal"
data-target="#modal_clear_history" title="Clear Activity">
<i class="fa-solid fa-trash"></i> <i class="fa-solid fa-trash"></i>
&nbsp;Clear History Clear Activity
</button> </button>
</div>
{% endif %} {% endif %}
</div> </div>
<div class="card-body clearfix"> <div class="card-body clearfix">
@ -59,7 +53,8 @@
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link with-border" href="#tabs-account" data-toggle="pill" role="tab"> <a class="nav-link with-border" href="#tabs-account" data-toggle="pill"
role="tab">
Search By Account Search By Account
</a> </a>
</li> </li>
@ -80,32 +75,40 @@
</td> </td>
<td> <td>
<div class="autocomplete" style="width:250px;"> <div class="autocomplete" style="width:250px;">
<input type="text" class="form-control" id="domain_name_filter" name="domain_name_filter" placeholder="Enter * to search for any domain" value=""> <input type="text" class="form-control" id="domain_name_filter"
name="domain_name_filter"
placeholder="Enter * to search for any domain" value="">
</div> </div>
</td> </td>
<td> <td>
<div style="position: relative; top:10px;"> <div style="position: relative; top:10px;">
<td>Record Changelog only &nbsp</td> <td>Record Changelog only &nbsp</td>
<td> <td>
<input type="checkbox" id="domain_changelog_only_checkbox" name="domain_changelog_only_checkbox" <input type="checkbox" id="domain_changelog_only_checkbox"
class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;"> name="domain_changelog_only_checkbox"
</td> class="checkbox"
</div> style="border:2px dotted #00f;display:block;background:#ff0000;">
</td> </td>
</div> </div>
<div class="tab-pane" id="tabs-account"> </td>
<td><label>Account Name</label></td> </div>
<td> <div class="tab-pane" id="tabs-account">
<div class="autocomplete" style="width:250px;"> <td><label>Account Name</label></td>
<input type="text" class="form-control" id="account_name_filter" name="account_name_filter" placeholder="Enter * to search for any account" value="">
</div>
</td>
</div>
<div class="tab-pane" id="tabs-auth">
<td><label>Username</label></td>
<td> <td>
<div class="autocomplete" style="width:250px;"> <div class="autocomplete" style="width:250px;">
<input type="text" class="form-control" id="auth_name_filter" name="auth_name_filter" placeholder="Enter * to search for any username" value=""> <input type="text" class="form-control" id="account_name_filter"
name="account_name_filter"
placeholder="Enter * to search for any account" value="">
</div>
</td>
</div>
<div class="tab-pane" id="tabs-auth">
<td><label>Username</label></td>
<td>
<div class="autocomplete" style="width:250px;">
<input type="text" class="form-control" id="auth_name_filter"
name="auth_name_filter"
placeholder="Enter * to search for any username" value="">
</div> </div>
</td> </td>
@ -115,25 +118,33 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
<td>&nbsp All</td> <td>&nbsp All</td>
<td> <td>
<input type="checkbox" checked id="auth_all_checkbox" name="auth_all_checkbox" <input type="checkbox" checked id="auth_all_checkbox" name="auth_all_checkbox"
class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;"> class="checkbox"
style="border:2px dotted #00f;display:block;background:#ff0000;">
</td> </td>
<td>&nbsp LOCAL</td> <td>&nbsp LOCAL</td>
<td> <td>
<input type="checkbox" checked id="auth_local_only_checkbox" name="auth_local_only_checkbox" <input type="checkbox" checked id="auth_local_only_checkbox"
class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;"> name="auth_local_only_checkbox"
class="checkbox"
style="border:2px dotted #00f;display:block;background:#ff0000;">
</td> </td>
<td>&nbsp OAuth</td> <td>&nbsp OAuth</td>
<td> <td>
<input type="checkbox" checked id="auth_oauth_only_checkbox" name="auth_oauth_only_checkbox" <input type="checkbox" checked id="auth_oauth_only_checkbox"
class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;"> name="auth_oauth_only_checkbox"
class="checkbox"
style="border:2px dotted #00f;display:block;background:#ff0000;">
</td> </td>
<td>&nbsp SAML</td> <td>&nbsp SAML</td>
<td> <td>
<input type="checkbox" checked id="auth_saml_only_checkbox" name="auth_saml_only_checkbox" <input type="checkbox" checked id="auth_saml_only_checkbox"
class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;"> name="auth_saml_only_checkbox"
class="checkbox"
style="border:2px dotted #00f;display:block;background:#ff0000;">
</td> </td>
</div> </div>
</td> </td>
</form>
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body">
@ -146,7 +157,8 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
<td><label>Changed by: &nbsp</label></td> <td><label>Changed by: &nbsp</label></td>
<td> <td>
<div class="autocomplete" style="width:250px;"> <div class="autocomplete" style="width:250px;">
<input type="text" style=" border:1px solid #d2d6de; width:250px; height: 34px;" id="user_name_filter" name="user_name_filter" value=""> <input type="text" style=" border:1px solid #d2d6de; width:250px; height: 34px;"
id="user_name_filter" name="user_name_filter" value="">
</div> </div>
</td> </td>
</tr> </tr>
@ -155,7 +167,8 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
<label>Minimum date: &nbsp</label> <label>Minimum date: &nbsp</label>
</td> </td>
<td style="position: relative; top:10px;"> <td style="position: relative; top:10px;">
<input type="text" id="min" name="min" class="datepicker" autocomplete="off" style=" border:1px solid #d2d6de; width:250px; height: 34px;"> <input type="text" id="min" name="min" class="datepicker" autocomplete="off"
style=" border:1px solid #d2d6de; width:250px; height: 34px;">
</td> </td>
</tr> </tr>
<tr> <tr>
@ -163,17 +176,27 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
<label>Maximum date: &nbsp</label> <label>Maximum date: &nbsp</label>
</td> </td>
<td style="position: relative; top:20px;"> <td style="position: relative; top:20px;">
<input type="text" id="max" name="max" class="datepicker" autocomplete="off" style=" border:1px solid #d2d6de; width:250px; height: 34px;"> <input type="text" id="max" name="max" class="datepicker" autocomplete="off"
style=" border:1px solid #d2d6de; width:250px; height: 34px;">
</td> </td>
</tr> </tr>
<tr><td>&nbsp</td></tr> <tr>
<tr><td>&nbsp</td></tr> <td>&nbsp</td>
</tr>
<tr>
<td>&nbsp</td>
</tr>
<tr> <tr>
<td> <td>
<button type="submit" id="search-submit" name="search-submit" class="btn btn-primary button-filter"><i class="fa fa-search"></i>&nbsp;Search</button> <button type="submit" id="search-submit" name="search-submit"
class="btn btn-primary button-filter"><i class="fa fa-search"></i>&nbsp;Search
</button>
</td> </td>
<td> <td>
<button id="clear-filters" name="clear-filters" class="btn btn-warning button-clearf"><i class="fa fa-trash"></i>&nbsp;Clear Filters</button> <button id="clear-filters" name="clear-filters"
class="btn btn-warning button-clearf"><i class="fa fa-trash"></i>&nbsp;Clear
Filters
</button>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -182,23 +205,24 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
</div> </div>
<div id="table_from_ajax"></div> <div id="table_from_ajax"></div>
</div> </div>
</section> </section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
/* Don't let user search with a blank main field */ /* Don't let user search with a blank main field */
var canSearch=true; var canSearch = true;
$(document).ready(function () { $(document).ready(function () {
$.ajax({ $.ajax({
url: "/admin/history_table", url: "/admin/history_table",
type: "get", type: "get",
success: function(response) { success: function (response) {
console.log('Submission was successful.'); console.log('Submission was successful.');
$("#table_from_ajax").html(response); $("#table_from_ajax").html(response);
}, },
error: function(xhr) { error: function (xhr) {
console.log("Sending data: ", data, " failed") console.log("Sending data: ", data, " failed")
} }
}); });
@ -209,26 +233,26 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
// Show/hide filters // Show/hide filters
$('#domain_name_filter, #account_name_filter, #auth_name_filter').on('keyup change', function (e) { $('#domain_name_filter, #account_name_filter, #auth_name_filter').on('keyup change', function (e) {
if ( $('#domain_name_filter').val() == "" && $('#account_name_filter').val() == "" && $('#auth_name_filter').val() == "") if ($('#domain_name_filter').val() == "" && $('#account_name_filter').val() == "" && $('#auth_name_filter').val() == "")
canSearch=false; canSearch = false;
else else
canSearch=true; canSearch = true;
}); });
// Handle giving later mindate than current max // Handle giving later mindate than current max
$('#min').on('change', function () { $('#min').on('change', function () {
if (minDate.val() > maxDate.val()) if (minDate.val() > maxDate.val())
$('#max').datepicker('setDate', minDate.val() ); $('#max').datepicker('setDate', minDate.val());
}); });
// Handle giving earlier maxdate than current min // Handle giving earlier maxdate than current min
$('#max').on('keyup change', function () { $('#max').on('keyup change', function () {
if (maxDate.val() < minDate.val()) if (maxDate.val() < minDate.val())
$('#min').datepicker('setDate', maxDate.val() ); $('#min').datepicker('setDate', maxDate.val());
}); });
$(function() { $(function () {
$( ".datepicker" ).datepicker({ $(".datepicker").datepicker({
changeMonth: true, changeMonth: true,
changeYear: true, changeYear: true,
format: "yyyy-mm-dd", format: "yyyy-mm-dd",
@ -246,16 +270,15 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
}); });
//Handle "ALL" Checkbox //Handle "ALL" Checkbox
$('#auth_all_checkbox').on('ifChecked',function() { $('#auth_all_checkbox').on('ifChecked', function () {
$('#auth_local_only_checkbox').iCheck('check'); $('#auth_local_only_checkbox').iCheck('check');
$('#auth_oauth_only_checkbox').iCheck('check'); $('#auth_oauth_only_checkbox').iCheck('check');
$('#auth_saml_only_checkbox').iCheck('check'); $('#auth_saml_only_checkbox').iCheck('check');
}); });
$('#auth_all_checkbox').on('ifUnchecked',function() { $('#auth_all_checkbox').on('ifUnchecked', function () {
//check if all were checked //check if all were checked
if($('#auth_local_only_checkbox').is(':checked') && $('#auth_oauth_only_checkbox').is(':checked') && $('#auth_saml_only_checkbox').is(':checked')) if ($('#auth_local_only_checkbox').is(':checked') && $('#auth_oauth_only_checkbox').is(':checked') && $('#auth_saml_only_checkbox').is(':checked')) {
{
$('#auth_local_only_checkbox').iCheck('uncheck'); $('#auth_local_only_checkbox').iCheck('uncheck');
$('#auth_oauth_only_checkbox').iCheck('uncheck'); $('#auth_oauth_only_checkbox').iCheck('uncheck');
$('#auth_saml_only_checkbox').iCheck('uncheck'); $('#auth_saml_only_checkbox').iCheck('uncheck');
@ -263,28 +286,28 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
}); });
//Handle other auth checkboxes //Handle other auth checkboxes
$('#auth_local_only_checkbox').on('ifChecked',function() { $('#auth_local_only_checkbox').on('ifChecked', function () {
//check if all others were checked //check if all others were checked
if($('#auth_oauth_only_checkbox').is(':checked') && $('#auth_saml_only_checkbox').is(':checked')) if ($('#auth_oauth_only_checkbox').is(':checked') && $('#auth_saml_only_checkbox').is(':checked'))
$('#auth_all_checkbox').iCheck('check'); $('#auth_all_checkbox').iCheck('check');
}); });
$('#auth_local_only_checkbox').on('ifUnchecked',function() { $('#auth_local_only_checkbox').on('ifUnchecked', function () {
$('#auth_all_checkbox').iCheck('uncheck'); $('#auth_all_checkbox').iCheck('uncheck');
}); });
$('#auth_oauth_only_checkbox').on('ifChecked',function() { $('#auth_oauth_only_checkbox').on('ifChecked', function () {
if($('#auth_local_only_checkbox').is(':checked') && $('#auth_saml_only_checkbox').is(':checked')) if ($('#auth_local_only_checkbox').is(':checked') && $('#auth_saml_only_checkbox').is(':checked'))
$('#auth_all_checkbox').iCheck('check'); $('#auth_all_checkbox').iCheck('check');
}); });
$('#auth_oauth_only_checkbox').on('ifUnchecked',function() { $('#auth_oauth_only_checkbox').on('ifUnchecked', function () {
$('#auth_all_checkbox').iCheck('uncheck'); $('#auth_all_checkbox').iCheck('uncheck');
}); });
$('#auth_saml_only_checkbox').on('ifChecked',function() { $('#auth_saml_only_checkbox').on('ifChecked', function () {
if($('#auth_local_only_checkbox').is(':checked') && $('#auth_oauth_only_checkbox').is(':checked')) if ($('#auth_local_only_checkbox').is(':checked') && $('#auth_oauth_only_checkbox').is(':checked'))
$('#auth_all_checkbox').iCheck('check'); $('#auth_all_checkbox').iCheck('check');
}); });
$('#auth_saml_only_checkbox').on('ifUnchecked',function() { $('#auth_saml_only_checkbox').on('ifUnchecked', function () {
$('#auth_all_checkbox').iCheck('uncheck'); $('#auth_all_checkbox').iCheck('uncheck');
}); });
@ -313,11 +336,13 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
the text field element and an array of possible autocompleted values:*/ the text field element and an array of possible autocompleted values:*/
var currentFocus; var currentFocus;
/*execute a function when someone writes in the text field:*/ /*execute a function when someone writes in the text field:*/
inp.addEventListener("input", function(e) { inp.addEventListener("input", function (e) {
var a, b, i, val = this.value; var a, b, i, val = this.value;
/*close any already open lists of autocompleted values*/ /*close any already open lists of autocompleted values*/
closeAllLists(); closeAllLists();
if (!val) { return false;} if (!val) {
return false;
}
currentFocus = -1; currentFocus = -1;
/*create a DIV element that will contain the items (values):*/ /*create a DIV element that will contain the items (values):*/
a = document.createElement("DIV"); a = document.createElement("DIV");
@ -337,7 +362,7 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
/*insert a input field that will hold the current array item's value:*/ /*insert a input field that will hold the current array item's value:*/
b.innerHTML += "<input type='hidden' value='" + arr[i] + "'>"; b.innerHTML += "<input type='hidden' value='" + arr[i] + "'>";
/*execute a function when someone clicks on the item value (DIV element):*/ /*execute a function when someone clicks on the item value (DIV element):*/
b.addEventListener("click", function(e) { b.addEventListener("click", function (e) {
/*insert the value for the autocomplete text field:*/ /*insert the value for the autocomplete text field:*/
inp.value = this.getElementsByTagName("input")[0].value; inp.value = this.getElementsByTagName("input")[0].value;
/*close the list of autocompleted values, /*close the list of autocompleted values,
@ -349,7 +374,7 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
} }
}); });
/*execute a function presses a key on the keyboard:*/ /*execute a function presses a key on the keyboard:*/
inp.addEventListener("keydown", function(e) { inp.addEventListener("keydown", function (e) {
var x = document.getElementById(this.id + "autocomplete-list"); var x = document.getElementById(this.id + "autocomplete-list");
if (x) x = x.getElementsByTagName("div"); if (x) x = x.getElementsByTagName("div");
if (e.keyCode == 40) { if (e.keyCode == 40) {
@ -373,6 +398,7 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
} }
} }
}); });
function addActive(x) { function addActive(x) {
/*a function to classify an item as "active":*/ /*a function to classify an item as "active":*/
if (!x) return false; if (!x) return false;
@ -383,12 +409,14 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
/*add class "autocomplete-active":*/ /*add class "autocomplete-active":*/
x[currentFocus].classList.add("autocomplete-active"); x[currentFocus].classList.add("autocomplete-active");
} }
function removeActive(x) { function removeActive(x) {
/*a function to remove the "active" class from all autocomplete items:*/ /*a function to remove the "active" class from all autocomplete items:*/
for (var i = 0; i < x.length; i++) { for (var i = 0; i < x.length; i++) {
x[i].classList.remove("autocomplete-active"); x[i].classList.remove("autocomplete-active");
} }
} }
function closeAllLists(elmnt) { function closeAllLists(elmnt) {
/*close all autocomplete lists in the document, /*close all autocomplete lists in the document,
except the one passed as an argument:*/ except the one passed as an argument:*/
@ -399,6 +427,7 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
} }
} }
} }
/*execute a function when someone clicks in the document:*/ /*execute a function when someone clicks in the document:*/
document.addEventListener("click", function (e) { document.addEventListener("click", function (e) {
closeAllLists(e.target); closeAllLists(e.target);
@ -413,77 +442,75 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
// prevent multiple filter field at the same time // prevent multiple filter field at the same time
$('#domain_tab').click(function() { $('#domain_tab').click(function () {
$('#account_name_filter').val(''); $('#account_name_filter').val('');
$('#auth_name_filter').val(''); $('#auth_name_filter').val('');
$('#user_name_filter').removeAttr('disabled'); $('#user_name_filter').removeAttr('disabled');
canSearch=false; canSearch = false;
main_field="Domain Name" main_field = "Domain Name"
}); });
$('#account_tab').click(function() { $('#account_tab').click(function () {
$('#domain_name_filter').val(''); $('#domain_name_filter').val('');
$('#auth_name_filter').val(''); $('#auth_name_filter').val('');
$('#user_name_filter').removeAttr('disabled'); $('#user_name_filter').removeAttr('disabled');
canSearch=false; canSearch = false;
main_field="Account Name" main_field = "Account Name"
}); });
$('#user_auth_tab').click( function() { $('#user_auth_tab').click(function () {
$('#domain_name_filter').val(''); $('#domain_name_filter').val('');
$('#account_name_filter').val(''); $('#account_name_filter').val('');
$('#user_name_filter').val(''); $('#user_name_filter').val('');
$('#user_name_filter').attr('disabled','disabled'); $('#user_name_filter').attr('disabled', 'disabled');
canSearch=false; canSearch = false;
main_field="Username" main_field = "Username"
}); });
$('#activity_tab').click( function() { $('#activity_tab').click(function () {
$('#domain_name_filter').val(''); $('#domain_name_filter').val('');
$('#account_name_filter').val(''); $('#account_name_filter').val('');
$('#auth_name_filter').val(''); $('#auth_name_filter').val('');
$('#user_name_filter').removeAttr('disabled'); $('#user_name_filter').removeAttr('disabled');
$('#search-submit').removeAttr('disabled','disabled'); $('#search-submit').removeAttr('disabled', 'disabled');
canSearch=true; canSearch = true;
main_field="" main_field = ""
}); });
// if search submit is pressed, and max date not initialized // if search submit is pressed, and max date not initialized
// then initialize it // then initialize it
$('#search-submit').on('click', function() { $('#search-submit').on('click', function () {
if ($('#max').val() === "" || $('#max').val() === undefined) if ($('#max').val() === "" || $('#max').val() === undefined)
$('#max').datepicker('setDate', 'now'); $('#max').datepicker('setDate', 'now');
}); });
$("#history-search-form").submit(function(e){ // ajax call to load results on submition $("#history-search-form").submit(function (e) { // ajax call to load results on submition
e.preventDefault(); // prevent page reloading e.preventDefault(); // prevent page reloading
if(!canSearch) if (!canSearch) {
{
showErrorModal("Please fill out the " + main_field + " field."); showErrorModal("Please fill out the " + main_field + " field.");
} } else {
else
{
var form = $(this); var form = $(this);
var tzoffset = (new Date()).getTimezoneOffset(); var tzoffset = (new Date()).getTimezoneOffset();
$.ajax({ $.ajax({
url: "/admin/history_table", url: "/admin/history_table",
type: "get", type: "get",
data: form.serialize() + "&tzoffset=" + tzoffset, data: form.serialize() + "&tzoffset=" + tzoffset,
success: function(response) { success: function (response) {
console.log('Submission was successful.'); console.log('Submission was successful.');
$("#table_from_ajax").html(response); $("#table_from_ajax").html(response);
}, },
error: function(xhr) { error: function (xhr) {
console.log("Sending data: ", data, " failed") console.log("Sending data: ", data, " failed")
} }
}); });
} }
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<!-- Clear History Confirmation Box --> <!-- Clear History Confirmation Box -->
<div class="modal fade modal-warning" id="modal_clear_history"> <div class="modal fade modal-warning" id="modal_clear_history">
@ -500,7 +527,8 @@ class="checkbox" style="border:2px dotted #00f;display:block;background:#ff0000;
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">Close</button> <button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-danger float-right" onclick="applyChanges({'_csrf_token': '{{ csrf_token() }}'}, $SCRIPT_ROOT + '/admin/history', false, true);"> <button type="button" class="btn btn-danger float-right"
onclick="applyChanges({'_csrf_token': '{{ csrf_token() }}'}, $SCRIPT_ROOT + '/admin/history', false, true);">
Clear History Clear History
</button> </button>
</div> </div>

View File

@ -1,22 +1,13 @@
{% 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
<small>Manage</small>
</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">
@ -30,21 +21,26 @@
{% 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>
<div class="card-tools">
<a href="{{ url_for('admin.edit_account') }}"> <a href="{{ url_for('admin.edit_account') }}">
<button type="button" class="btn btn-primary float-right button_add_account"> <button type="button" class="btn btn-primary button_add_account"
title="Add Account">
<i class="fa-solid fa-plus"></i>&nbsp;Add Account <i class="fa-solid fa-plus"></i>&nbsp;Add Account
</button> </button>
</a> </a>
</div> </div>
<div class="card-body"> </div>
<table id="tbl_accounts" class="table table-bordered table-striped"> <!-- /.card-header -->
<div class="card-body table-responsive">
<table id="tbl_accounts"
class="table table-bordered table-striped table-hover table-sm records">
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
@ -65,20 +61,25 @@
<td>{{ account.mail }}</td> <td>{{ account.mail }}</td>
<td>{{ account.user_num }}</td> <td>{{ account.user_num }}</td>
<td>{{ account.domains|length }}</td> <td>{{ account.domains|length }}</td>
<td width="15%"> <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"
<i class="fa-solid fa-bars"></i>&nbsp;Actions id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<i class="fa-solid fa-bars"></i>
</button> </button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu"> <div class="dropdown-menu" aria-labelledby="dropdownMenu">
<button type="button" class="dropdown-item btn-warning" onclick="window.location.href='{{ url_for('admin.edit_account', account_name=account.name) }}'"> <button type="button" class="dropdown-item btn-warning"
onclick="window.location.href='{{ url_for('admin.edit_account', account_name=account.name) }}'">
<i class="fa-solid fa-edit"></i>&nbsp;Edit Account <i class="fa-solid fa-edit"></i>&nbsp;Edit Account
</button> </button>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{ account.name }}"> <button type="button"
<font color="red"> class="dropdown-item btn-secondary button_delete"
id="{{ account.name }}">
<span style="color: red;">
<i class="fa-solid fa-trash"></i>&nbsp;Delete Account <i class="fa-solid fa-trash"></i>&nbsp;Delete Account
</font> </span>
</button> </button>
</div> </div>
</div> </div>
@ -88,15 +89,21 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</section> <!-- /.container-fluid -->
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// set up accounts data table // Intialize DataTable
$("#tbl_accounts").DataTable({ $("#tbl_accounts").DataTable({
"paging": true, "paging": true,
"lengthChange": true, "lengthChange": true,
@ -132,10 +139,11 @@
}) })
modal.modal('show'); modal.modal('show');
}); });
</script> </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">
@ -155,5 +163,5 @@
<!-- /.modal-content --> <!-- /.modal-content -->
</div> </div>
<!-- /.modal-dialog --> <!-- /.modal-dialog -->
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,22 +1,13 @@
{% 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
<small>Management</small>
</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">
@ -30,19 +21,23 @@
{% 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>
<div class="card-tools">
<a href="{{ url_for('admin.edit_key') }}"> <a href="{{ url_for('admin.edit_key') }}">
<button type="button" class="btn btn-primary float-right button_add_key"> <button type="button" class="btn btn-primary float-right button_add_key" title="Create Key">
<i class="fa-solid fa-plus"></i>&nbsp;Add Key <i class="fa-solid fa-plus"></i>&nbsp;Create Key
</button> </button>
</a> </a>
</div> </div>
<div class="card-body"> <!-- /.card-tools -->
<table id="tbl_keys" class="table table-bordered table-striped"> </div>
<!-- /.card-header -->
<div class="card-body table-responsive">
<table id="tbl_keys" class="table table-bordered table-striped table-hover table-sm records">
<thead> <thead>
<tr> <tr>
<th>Id</th> <th>Id</th>
@ -59,19 +54,24 @@
<td>{{ key.id }}</td> <td>{{ key.id }}</td>
<td>{{ key.role.name }}</td> <td>{{ key.role.name }}</td>
<td>{{ key.description }}</td> <td>{{ key.description }}</td>
<td>{% for domain in key.domains %}{{ domain.name }}{% if not loop.last %}, {% endif %}{% endfor %}</td> <td>{% for domain in key.domains %}{{ domain.name }}{% if not loop.last %},
<td>{% for account in key.accounts %}{{ account.name }}{% if not loop.last %}, {% endif %}{% endfor %}</td> {% endif %}{% endfor %}</td>
<td width="15%"> <td>{% for account in key.accounts %}{{ account.name }}{% if not loop.last %},
{% endif %}{% endfor %}</td>
<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"
<i class="fa-solid fa-bars"></i>&nbsp;Actions data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa-solid fa-bars"></i>
</button> </button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu"> <div class="dropdown-menu" aria-labelledby="dropdownMenu">
<button type="button" class="dropdown-item btn-warning" onclick="window.location.href='{{ url_for('admin.edit_key', key_id=key.id) }}'"> <button type="button" class="dropdown-item btn-warning"
onclick="window.location.href='{{ url_for('admin.edit_key', key_id=key.id) }}'">
<i class="fa-solid fa-edit"></i>&nbsp;Edit API Key <i class="fa-solid fa-edit"></i>&nbsp;Edit API Key
</button> </button>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{ key.id }}"> <button type="button" class="dropdown-item btn-secondary button_delete"
id="{{ key.id }}">
<font color="red"> <font color="red">
<i class="fa-solid fa-trash"></i>&nbsp;Delete API Key <i class="fa-solid fa-trash"></i>&nbsp;Delete API Key
</font> </font>
@ -84,9 +84,12 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
</section> <!-- /.container-fluid -->
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}

View File

@ -1,27 +1,18 @@
{% 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
<small>Manage user privileges</small>
</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('dashboard.dashboard') }}">Home</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Home</a></li>
<li class="breadcrumb-item active">User</li> <li class="breadcrumb-item active">Users</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -34,17 +25,22 @@
<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>
<div class="card-tools">
<a href="{{ url_for('admin.edit_user') }}"> <a href="{{ url_for('admin.edit_user') }}">
<button type="button" class="btn btn-primary float-right button_add_user"> <button type="button" class="btn btn-primary button_add_user" title="Create User">
<i class="fa-solid fa-plus"></i>&nbsp;Add User <i class="fa-solid fa-plus"></i>&nbsp;Create User
</button> </button>
</a> </a>
</div> </div>
<div class="card-body"> <!-- /.card-tools -->
<table id="tbl_users" class="table table-bordered table-striped"> </div>
<!-- /.card-header -->
<div class="card-body table-responsive">
<table id="tbl_users"
class="table table-bordered table-striped table-hover table-sm records">
<thead> <thead>
<tr> <tr>
<th>Username</th> <th>Username</th>
@ -74,30 +70,35 @@
{% endfor %} {% endfor %}
</select> </select>
</td> </td>
<td width="6%"> <td>
<button type="button" class="btn btn-warning button_revoke" <button type="button" class="btn btn-warning button_revoke"
title="Revoke Privileges"
id="{{ user.username }}" id="{{ user.username }}"
{% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}> {% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
<i class="fa-solid fa-link-slash"></i>&nbsp;Revoke <i class="fa-solid fa-link-slash"></i>
</button> </button>
</td> </td>
<td width="15%"> <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" aria-haspopup="true"
aria-expanded="false"
{% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}> {% if current_user.role.name=='Operator' and user.role.name=='Administrator' %}disabled{% endif %}>
<i class="fa-solid fa-bars"></i>&nbsp;Actions <i class="fa-solid fa-bars"></i>
</button> </button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu"> <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) }}'"> <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 <i class="fa-solid fa-edit"></i>&nbsp;Edit User
</button> </button>
{% if not user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %} {% if not user.username==current_user.username or (current_user.role.name=='Operator' and user.role.name=='Administrator') %}
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{ user.username }}"> <button type="button"
<font color="red"> class="dropdown-item btn-secondary button_delete"
id="{{ user.username }}">
<span style="color: red;">
<i class="fa-solid fa-trash"></i>&nbsp;Delete User <i class="fa-solid fa-trash"></i>&nbsp;Delete User
</font> </span>
</button> </button>
{% endif %} {% endif %}
</div> </div>
@ -108,10 +109,15 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
<!-- /.container-fluid -->
</section> </section>
{% 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,27 +1,18 @@
{% 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
<small>Basic</small>
</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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Settings - Basic</li> <li class="breadcrumb-item active">Basic Settings</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -30,14 +21,15 @@
{% 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">
<table id="tbl_settings" class="table table-bordered table-striped table-hover table-sm">
<thead> <thead>
<tr> <tr>
<th>Setting Name</th> <th>Setting Name</th>
@ -49,28 +41,31 @@
{% for setting in settings %} {% for setting in settings %}
<tr class="odd"> <tr class="odd">
<td> <td>
{{ setting }} <label for="value{{ loop.index }}">{{ setting }}</label>
</td> </td>
{% if SETTING.get(setting) in [False] %} {% if SETTING.get(setting) in [False] %}
<td><i class="fas fa-toggle-off"></i>&nbsp;Off</td> <td><i class="fas fa-toggle-off"></i>&nbsp;Off</td>
<td width="20%"> <td>
<button type="button" class="btn btn-success setting-toggle-button" id="{{ setting }}"> <button type="button" class="btn btn-success setting-toggle-button"
id="{{ setting }}">
<i class="fa-solid fa-toggle-on"></i>&nbsp;Turn On <i class="fa-solid fa-toggle-on"></i>&nbsp;Turn On
</button> </button>
</td> </td>
{% elif SETTING.get(setting) in [True] %} {% elif SETTING.get(setting) in [True] %}
<td><i class="fas fa-toggle-on"></i>&nbsp;On</td> <td><i class="fas fa-toggle-on"></i>&nbsp;On</td>
<td width="20%"> <td>
<button type="button" class="btn btn-danger setting-toggle-button" id="{{ setting }}"> <button type="button" class="btn btn-danger setting-toggle-button"
id="{{ setting }}">
<i class="fa-solid fa-toggle-off"></i>&nbsp;Turn Off <i class="fa-solid fa-toggle-off"></i>&nbsp;Turn Off
</button> </button>
</td> </td>
{% else %} {% else %}
<td> <td>
<input name="value" id="value" value="{{ SETTING.get(setting) }}"> <input name="value" id="value{{ loop.index }}" value="{{ SETTING.get(setting) }}">
</td> </td>
<td width="20%"> <td>
<button type="button" class="btn btn-primary setting-save-button" id="{{ setting }}"> <button type="button" class="btn btn-primary setting-save-button"
id="{{ setting }}">
<i class="fa-solid fa-save"></i>&nbsp;Save <i class="fa-solid fa-save"></i>&nbsp;Save
</button> </button>
</td> </td>
@ -80,9 +75,12 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
</section> <!-- /.container-fluid -->
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}

View File

@ -1,27 +1,18 @@
{% 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
<small>PowerDNS Authoritative Server</small>
</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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Settings - PowerDNS Authoritative Server</li> <li class="breadcrumb-item active">Server Settings</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -33,57 +24,73 @@
<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">
<div class="card-header">
<h3 class="card-title">PDNS Settings</h3>
</div>
<form role="form" method="post" data-toggle="validator"> <form role="form" 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="card card-outline card-primary shadow">
<div class="card-header">
<h3 class="card-title">Settings Editor</h3>
</div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
{% if not SETTING.get('pdns_api_url') or not SETTING.get('pdns_api_key') or not SETTING.get('pdns_version') %} {% 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"> <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>
<h4><i class="icon fa fa-ban"></i> Error!</h4> <h4><i class="icon fa fa-ban"></i> Error!</h4>
Please complete your PowerDNS API configuration before continuing Please complete your PowerDNS API configuration before continuing
</div> </div>
{% endif %} {% endif %}
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label class="control-label" for="pdns_api_url">PowerDNS API URL</label> <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" <input type="url" class="form-control" placeholder="PowerDNS API URL"
data-error="Please input a valid PowerDNS API URL" required value="{{ pdns_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> <span class="help-block with-errors"></span>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label class="control-label" for="pdns_api_key">PowerDNS API Key</label> <label class="control-label" for="pdns_api_key">PowerDNS API Key</label>
<input type="password" class="form-control" placeholder="PowerDNS API Key" <input type="password" class="form-control" placeholder="PowerDNS API Key"
name="pdns_api_key" data-error="Please input a valid PowerDNS API key" required name="pdns_api_key" id="pdns_api_key"
data-error="Please input a valid PowerDNS API key"
required
value="{{ pdns_api_key }}"> value="{{ pdns_api_key }}">
<span class="help-block with-errors"></span> <span class="help-block with-errors"></span>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label class="control-label" for="pdns_version">PowerDNS Version</label> <label class="control-label" for="pdns_version">PowerDNS Version</label>
<input type="text" class="form-control" placeholder="PowerDNS Version" name="pdns_version" <input type="text" class="form-control" placeholder="PowerDNS Version"
data-error="Please input PowerDNS version" required value="{{ pdns_version }}"> name="pdns_version" id="pdns_version"
data-error="Please input PowerDNS version" required
value="{{ pdns_version }}">
<span class="help-block with-errors"></span> <span class="help-block with-errors"></span>
</div> </div>
</div> </div>
<!-- /.card-body -->
<div class="card-footer"> <div class="card-footer">
<button type="submit" class="btn btn-primary float-right"> <button type="submit" class="btn btn-primary float-right" title="Save Settings">
<i class="fa-solid fa-save"></i>&nbsp;Save <i class="fa-solid fa-save"></i>&nbsp;Save Settings
</button> </button>
</div> </div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form> </form>
</div> </div>
</div> <!-- /.col -->
<div class="col-8">
<div class="card shadow card-outline card-secondary"> <div class="col-12 col-sm-6 col-lg-8">
<div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Help</h3> <h3 class="card-title">Settings Editor</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<p>You must configure the API connection information before PowerDNS-Admin can query your <p>You must configure the API connection information before PowerDNS-Admin can query
your
PowerDNS data. Following fields are required:</p> PowerDNS data. Following fields are required:</p>
<dt>PowerDNS API URL</dt> <dt>PowerDNS API URL</dt>
<dd>Your PowerDNS API URL (eg. http://127.0.0.1:8081/).</dd> <dd>Your PowerDNS API URL (eg. http://127.0.0.1:8081/).</dd>
@ -93,13 +100,18 @@
<dd>Your PowerDNS version number (eg. 4.7.0).</dd> <dd>Your PowerDNS version number (eg. 4.7.0).</dd>
</dl> </dl>
<p>Find more details at <p>Find more details at
<a href="https://doc.powerdns.com/md/httpapi/README/">https://doc.powerdns.com/md/httpapi/README/</a> <a href="https://doc.powerdns.com/md/httpapi/README/" target="_blank">https://doc.powerdns.com/md/httpapi/README/</a>
</p> </p>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
<!-- /.container-fluid -->
</section> </section>
{% endblock %} {% endblock %}

View File

@ -1,27 +1,18 @@
{% 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
<small>Records</small>
</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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Settings - Records </li> <li class="breadcrumb-item active">Zone Record Settings</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -30,22 +21,23 @@
{% 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">
<div class="card-header">
<h3 class="card-title">DNS record Settings</h3>
</div>
<form role="form" method="post"> <form role="form" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="create" value="{{ create }}"> <input type="hidden" name="create" value="{{ create }}">
<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"> <div class="card-body">
<table class="table table-bordered"> <table class="table table-bordered">
<tr> <tr>
<th style="width: 10px">#</th> <th>#</th>
<th style="width: 40px">Record</th> <th>Record</th>
<th>Forward Zone</th> <th>Forward Zone</th>
<th>Reverse Zone</th> <th>Reverse Zone</th>
</tr> </tr>
@ -54,41 +46,57 @@
<td>{{ loop.index }}</td> <td>{{ loop.index }}</td>
<td>{{ record }}</td> <td>{{ record }}</td>
<td> <td>
<input type="checkbox" id="fr_{{ record|lower }}" name="fr_{{ record|lower }}" <input type="checkbox" id="fr_{{ record|lower }}"
name="fr_{{ record|lower }}"
class="checkbox" {% if f_records[record] %}checked{% endif %}> class="checkbox" {% if f_records[record] %}checked{% endif %}>
</td> </td>
<td> <td>
<input type="checkbox" id="rr_{{ record|lower }}" name="rr_{{ record|lower }}" <input type="checkbox" id="rr_{{ record|lower }}"
name="rr_{{ record|lower }}"
class="checkbox" {% if r_records[record] %}checked{% endif %}> class="checkbox" {% if r_records[record] %}checked{% endif %}>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
</div> </div>
<!-- /.card-body -->
<div class="card-footer"> <div class="card-footer">
<button type="submit" class="btn btn-primary float-right"> <button type="submit" class="btn btn-primary float-right" title="Save Settings">
<i class="fa-solid fa-save"></i>&nbsp;Save <i class="fa-solid fa-save"></i>&nbsp;Save Settings
</button> </button>
</div> </div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form> </form>
</div> </div>
</div> <!-- /.col -->
<div class="col-7">
<div class="card"> <div class="col-12 col-sm-6 col-lg-8">
<div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Help</h3> <h3 class="card-title">Settings Editor Help</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<p>Select record types you allow user to edit in the forward zone and reverse zone. Take a look at <p>Select record types you allow user to edit in the forward zone and reverse zone. Take a
<a href="https://doc.powerdns.com/authoritative/appendices/types.html">PowerDNS docs</a> for look at
<a href="https://doc.powerdns.com/authoritative/appendices/types.html" target="_blank">PowerDNS
docs</a>
for
full list of supported record types. full list of supported record types.
</p> </p>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</section> <!-- /.container-fluid -->
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}

View File

@ -5,13 +5,8 @@
<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>
{{ SITE_NAME }}
</title>
{% endblock %}
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/style.css') }}"> <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/source_sans_pro.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='assets/css/roboto_mono.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='assets/css/roboto_mono.css') }}">
<!-- Tell the browser to be responsive to screen width --> <!-- Tell the browser to be responsive to screen width -->
@ -19,17 +14,17 @@
<!-- Tell Safari to not recognise telephone numbers --> <!-- Tell Safari to not recognise telephone numbers -->
<meta name="format-detection" content="telephone=no"> <meta name="format-detection" content="telephone=no">
{% assets "css_main" -%} {% assets "css_main" -%}
<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 %}
{% block head_styles %}{% endblock %}
{% endblock %} {% 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 -->
@ -55,7 +50,8 @@
<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"
class="brand-image img-circle elevation-3" style="opacity: .8">
<span class="brand-text font-weight-light"> <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>
@ -72,23 +68,16 @@
<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>
<span>
<a href="{{ url_for('user.profile') }}"><i class="nav-icon fa-solid fa-user"></i> Edit Profile</a>
&nbsp;|&nbsp;
<a href="{{ url_for('index.logout') }}"><i class="nav-icon fa-solid fa-sign-out-alt"></i> Logout</a>
</span>
</div> </div>
</div> </div>
<!-- sidebar menu: : style can be found in sidebar.less --> <!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu"> <ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu">
<li class="{{ 'nav-item active' if active_page == 'user_profile' else 'nav-item' }}">
<a href="{{ url_for('user.profile') }}" class="nav-link">
<i class="nav-icon fa-solid fa-user"></i>
<p>Profile</p>
</a>
</li>
<li class="nav-item">
<a href="{{ url_for('index.logout') }}" class="nav-link">
<i class="nav-icon fa-solid fa-sign-out-alt"></i>
<p>Logout</p>
</a>
</li>
<li class="nav-header">Zone Management</li> <li class="nav-header">Zone Management</li>
<li class="{{ 'nav-item active' if active_page == 'nav-item dashboard' else 'nav-item' }}"> <li class="{{ 'nav-item active' if active_page == 'nav-item dashboard' else 'nav-item' }}">
<a href="{{ url_for('dashboard.dashboard') }}" class="nav-link"> <a href="{{ url_for('dashboard.dashboard') }}" class="nav-link">
@ -100,7 +89,7 @@
<li class="{{ 'nav-item active' if active_page == 'nav-item new_domain' else 'nav-item' }}"> <li class="{{ 'nav-item active' if active_page == 'nav-item new_domain' else 'nav-item' }}">
<a href="{{ url_for('domain.add') }}" class="nav-link"> <a href="{{ url_for('domain.add') }}" class="nav-link">
<i class="nav-icon fa-solid fa-plus"></i> <i class="nav-icon fa-solid fa-plus"></i>
<p>New Domain</p> <p>Create Zone</p>
</a> </a>
</li> </li>
{% endif %} {% endif %}
@ -108,16 +97,22 @@
<li class="{{ 'nav-item active' if active_page == 'remove_domain' else 'nav-item' }}"> <li class="{{ 'nav-item active' if active_page == 'remove_domain' else 'nav-item' }}">
<a href="{{ url_for('domain.remove') }}" class="nav-link"> <a href="{{ url_for('domain.remove') }}" class="nav-link">
<i class="nav-icon fa-solid fa-trash-alt"></i> <i class="nav-icon fa-solid fa-trash-alt"></i>
<p>Remove Domain</p> <p>Remove Zone</p>
</a> </a>
</li> </li>
{% endif %} {% endif %}
{% if current_user.role.name in ['Administrator', 'Operator'] %} {% if current_user.role.name in ['Administrator', 'Operator'] %}
<li class="nav-header">Administration</li> <li class="nav-header">Administration</li>
<li class="{{ 'nav-item active' if active_page == 'admin_console' else 'nav-item' }}"> <li class="{{ 'nav-item active' if active_page == 'server_statistics' else 'nav-item' }}">
<a href="{{ url_for('admin.pdns_stats') }}" class="nav-link"> <a href="{{ url_for('admin.server_statistics') }}" class="nav-link">
<i class="nav-icon fa-solid fa-info-circle"></i> <i class="nav-icon fa-solid fa-chart-simple"></i>
<p>PowerDNS Info</p> <p>Server Statistics</p>
</a>
</li>
<li class="{{ 'nav-item active' if active_page == 'server_configuration' else 'nav-item' }}">
<a href="{{ url_for('admin.server_configuration') }}" class="nav-link">
<i class="nav-icon fa-solid fa-cog"></i>
<p>Server Configuration</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' }}">
@ -128,19 +123,19 @@
</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-calendar-alt"></i> <i class="nav-icon fa-solid fa-timeline"></i>
<p>History</p> <p>Activity</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>Domain Templates</p> <p>Zone 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-industry"></i> <i class="nav-icon fa-solid fa-users-rectangle"></i>
<p>Accounts</p> <p>Accounts</p>
</a> </a>
</li> </li>
@ -164,29 +159,30 @@
<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" {% if active_page == 'admin_settings' %}style="display: block;"{% endif %}> <ul class="nav nav-treeview"
{% 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>
@ -236,18 +232,17 @@
</div> </div>
<!-- /.content-wrapper --> <!-- /.content-wrapper -->
<footer class="main-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. <strong><a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></strong> - A PowerDNS web
interface with advanced features.
</footer> </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>
@ -255,7 +250,7 @@
// 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');
} }
}); });
@ -265,7 +260,7 @@
$.get({ $.get({
url: $.jTimeout().options.extendUrl, url: $.jTimeout().options.extendUrl,
cache: false, cache: false,
success: function(){ success: function () {
$.jTimeout().resetExpiration(); $.jTimeout().resetExpiration();
} }
}); });
@ -278,7 +273,7 @@
}); });
// 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);
@ -291,7 +286,7 @@
}); });
// jTimeout definition // jTimeout definition
$(function(){ $(function () {
$.jTimeout({ $.jTimeout({
flashTitle: true, flashTitle: true,
flashTitleSpeed: 500, flashTitleSpeed: 500,
@ -306,14 +301,14 @@
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')) {
@ -327,15 +322,16 @@
} }
}, },
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');
} }
}); });
@ -354,12 +350,12 @@
} }
</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">
@ -378,9 +374,9 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- /.modal --> <!-- /.modal -->
<div class="modal fade modal-success" id="modal_success"> <div class="modal fade modal-success" id="modal_success">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
@ -399,8 +395,8 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade modal-warning" data-backdrop="static" id="modal_session_warning"> <div class="modal fade modal-warning" data-backdrop="static" id="modal_session_warning">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
@ -412,7 +408,8 @@
<p>To coninue your ssession, select <strong>Stay Signed In</strong></p> <p>To coninue your ssession, select <strong>Stay Signed In</strong></p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-success float-right button_stay_signed_in" 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">
@ -421,7 +418,7 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
{% endblock %} {% endblock %}

View File

@ -1,12 +1,6 @@
{% 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">
@ -15,7 +9,6 @@
<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>
<div class="col-sm-6"> <div class="col-sm-6">
@ -33,17 +26,17 @@
{% 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="row">
<div class="col-3"> <div class="col-lg-12">
<div class="card"> <div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Statistics</h3> <h3 class="card-title">Statistics</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-6"> <div class="col-6 col-sm-3">
<!-- small box -->
<div class="small-box bg-info"> <div class="small-box bg-info">
<div class="inner"> <div class="inner">
<h3>{{ domain_count }}</h3> <h3>{{ domain_count }}</h3>
@ -54,8 +47,22 @@
</div> </div>
</div> </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'] %} {% if current_user.role.name in ['Administrator', 'Operator'] %}
<div class="col-6"> <div class="col-6 col-sm-3">
<a href="{{ url_for('admin.manage_user') }}"> <a href="{{ url_for('admin.manage_user') }}">
<div class="small-box bg-green"> <div class="small-box bg-green">
<div class="inner"> <div class="inner">
@ -68,28 +75,13 @@
</div> </div>
</a> </a>
</div> </div>
{% endif %} <div class="col-6 col-sm-3">
</div> <a href="{{ url_for('admin.server_statistics') }}">
<div class="row">
<div class="col-6">
<a href="{{ url_for('admin.history') }}">
<div class="small-box bg-green"> <div class="small-box bg-green">
<div class="inner"> <div class="inner">
<h3>{{ history_number }}</h3> <h3><span
<p>{% if history_number > 1 %}Histories{% else %}History{% endif %}</p> style="font-size: 18px">{{ uptime|display_second_to_time }}</span>
</div> </h3>
<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> <p>Uptime</p>
</div> </div>
<div class="icon"> <div class="icon">
@ -100,31 +92,41 @@
</div> </div>
{% endif %} {% endif %}
</div> </div>
<!-- /.row -->
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<div class="col-9"> <!-- /.col -->
<div class="card"> </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"> <div class="card-header">
<h3 class="card-title">Recent History</h3> <h3 class="card-title">Recent History</h3>
</div> </div>
<div class="card-body"> <!-- /.card-header -->
<table id="tbl_history" class="table table-bordered table-striped"> <div class="card-body table-responsive records p-0">
<table class="table table-striped table-hover table-sm records">
<thead> <thead>
<tr> <tr>
<th>Changed By</th> <th>Log Message</th>
<th>Content</th> <th>Timestamp</th>
<th>Time</th> <th>User</th>
<th>Detail</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for history in histories %} {% for history in histories %}
<tr class="odd"> <tr class="odd">
<td>{{ history.history.created_by }}</td>
<td>{{ history.history.msg }}</td> <td>{{ history.history.msg }}</td>
<td>{{ history.history.created_on }}</td> <td>{{ history.history.created_on | format_datetime_local }}</td>
<td width="6%"> <td>{{ history.history.created_by }}</td>
<td>
<div id="history-info-div-{{ loop.index0 }}" style="display: none;"> <div id="history-info-div-{{ loop.index0 }}" style="display: none;">
{{ history.detailed_msg | safe }} {{ history.detailed_msg | safe }}
{% if history.change_set %} {% if history.change_set %}
@ -136,10 +138,11 @@
{% endif %} {% endif %}
</div> </div>
<button type="button" class="btn btn-sm btn-primary history-info-button" <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 %} {% if history.detailed_msg == "" and history.change_set is none %}
style="visibility: hidden;" style="visibility: hidden;"
{% endif %} value="{{ loop.index0 }}"> {% endif %} value="{{ loop.index0 }}">
<i class="fa-solid fa-info-circle"></i>&nbsp;Info <i class="fa-solid fa-info-circle"></i>
</button> </button>
</td> </td>
</tr> </tr>
@ -147,44 +150,58 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
{% endif %} {% endif %}
<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 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"> <div class="nav-tabs-custom">
<ul class="nav nav-tabs card-header-tabs" id="custom-content-below-tab" role="tablist"> <ul class="nav nav-tabs card-header-tabs" id="custom-content-below-tab"
role="tablist">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" href="#tab_{{custom_boxes.order[0]}}" data-toggle="pill" role="tab"> <a class="nav-link active" href="#tab_{{ custom_boxes.order[0] }}"
Hosted Domains <b>{{custom_boxes.boxes[custom_boxes.order[0]][0]}}</b> data-toggle="pill" role="tab">
Zones <b>{{ custom_boxes.boxes[custom_boxes.order[0]][0] }}</b>
</a> </a>
</li> </li>
{% for boxId in custom_boxes.order[1:] %} {% for boxId in custom_boxes.order[1:] %}
<li class="nav-item"> <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> <a class="nav-link" href="#tab_{{ boxId }}" data-toggle="pill"
role="tab">
Zones <b>{{ custom_boxes.boxes[boxId][0] }}</b>
</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
</div> </div>
<div class="card-body"> </div>
<!-- /.card-header -->
<div class="card-body p-0">
<div class="tab-content"> <div class="tab-content">
{% for boxId in custom_boxes.order %} {% for boxId in custom_boxes.order %}
<div class="tab-pane fade show" id='tab_{{boxId}}'> <div class="tab-pane show" id='tab_{{ boxId }}'>
<div class="card-header"> <div class="card-body table-responsive records p-0 pt-2">
<h3 class="card-title">Hosted Domains <b>{{custom_boxes.boxes[boxId][0]}}</b></h3> <table id='tbl_domain_list_{{ boxId }}'
{% if show_bg_domain_button %} class="table table-striped table-hover table-sm records">
<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> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
@ -200,72 +217,68 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.tab-pane -->
{% endfor %} {% endfor %}
</div> </div>
<!-- /.tab-content -->
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
<!-- /.container-fluid -->
</section> </section>
{% endblock %} {% endblock %}
{% block head_styles %}
<style>
/* Page Specific Overrides */
table.records tbody td:first-of-type { text-align: left; }
</style>
{% 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, "searching": true,
"ordering" : true, "ordering": true,
"columnDefs": [ "columnDefs": [
{ "orderable": false, "targets": [-1] } {"orderable": false, "targets": [-1]}
{% if current_user.role.name not in ['Administrator', 'Operator'] %},{ "visible": false, "targets": [-2] }{% endif %}
], ],
"processing" : true, "processing": true,
"serverSide" : true, "serverSide": true,
"ajax" : url, "ajax": url,
"info" : false, "info": false,
"autoWidth" : false, "autoWidth": false,
{% if SETTING.get('default_domain_table_size')|string in ['10','25','50','100'] %} {% if SETTING.get('default_domain_table_size')|string in ['10','25','50','100'] %}
"lengthMenu": [ [10, 25, 50, 100, -1], "lengthMenu": [[10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"]], [10, 25, 50, 100, "All"]],
{% else %} {% else %}
"lengthMenu": [ [10, 25, 50, 100, {{ SETTING.get('default_domain_table_size') }}, -1], "lengthMenu": [[10, 25, 50, 100, {{ SETTING.get('default_domain_table_size') }}, -1],
[10, 25, 50, 100, {{ SETTING.get('default_domain_table_size') }}, "All"]], [10, 25, 50, 100, {{ SETTING.get('default_domain_table_size') }}, "All"]],
{% endif %} {% endif %}
"pageLength": {{ SETTING.get('default_domain_table_size') }} "pageLength": {{ SETTING.get('default_domain_table_size') }}
}); });
} }
$('#tab_{{custom_boxes.order[0]}}').addClass( "active" );
$('#tab_{{custom_boxes.order[0]}}').addClass("active");
{% for boxId in custom_boxes.order %} {% for boxId in custom_boxes.order %}
setUpDomainList("#tbl_domain_list_{{boxId}}", "{{url_for('dashboard.domains_custom',boxId=boxId)}}"); setUpDomainList("#tbl_domain_list_{{boxId}}", "{{url_for('dashboard.domains_custom',boxId=boxId)}}");
{% endfor %} {% endfor %}
//SYBPATCH END// //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
}
]
});
$(document.body).on('click', '.history-info-button', function () { $(document.body).on('click', '.history-info-button', function () {
var modal = $("#modal_history_info"); var modal = $("#modal_history_info");
@ -275,7 +288,7 @@
modal.modal('show'); modal.modal('show');
}); });
$(document.body).on('click', '.refresh-bg-button', function() { $(document.body).on('click', '.refresh-bg-button', function () {
var modal = $("#modal_bg_reload"); var modal = $("#modal_bg_reload");
modal.modal('show'); modal.modal('show');
reload_domains($SCRIPT_ROOT + '/dashboard/domains-updater'); reload_domains($SCRIPT_ROOT + '/dashboard/domains-updater');
@ -288,10 +301,10 @@
<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();
@ -299,7 +312,7 @@
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');
}) })
@ -307,26 +320,26 @@
}); });
{% 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">
@ -346,9 +359,9 @@
</div> </div>
</div> </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">
@ -370,9 +383,9 @@
</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">
@ -391,9 +404,9 @@
</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">
@ -414,5 +427,5 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -4,14 +4,20 @@
{% 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>
<button class="btn badge btn-success button_dnssec" title="Edit DNSSEC" id="{{ domain.name }}"><i
class="fa-solid fa-lock"></i></button>
</td>
{% else %} {% else %}
<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> <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 %} {% 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">
<i class="fa-solid fa-bars"></i>
</button> </button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu"> <div class="dropdown-menu" aria-labelledby="dropdownMenu">
<button class="dropdown-item btn-success" type="button"
onclick="window.location.href='{{ url_for('domain.domain', domain_name=domain.name) }}'">
<i class="fa-solid fa-pencil"></i>&nbsp;Edit Records
</button>
{% if current_user.role.name in ['Administrator', 'Operator'] %} {% 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) }}'">
<i class="fa-solid fa-cog"></i>&nbsp;Zone Settings
</button>
<button class="dropdown-item btn-success button_template" id="{{ domain.name }}" type="button"> <button class="dropdown-item btn-success button_template" id="{{ domain.name }}" type="button">
<i class="fa-solid fa-clone"></i>&nbsp;Clone to Template <i class="fa-solid fa-clone"></i>&nbsp;Create Template
</button> </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_view_history %}
<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-primary" type="button"
<i class="fa-solid fa-cog"></i>&nbsp;Admin Settings onclick="window.location.href='{{ url_for('domain.changelog', domain_name=domain.name) }}'">
</button> <i class="fa-solid fa-history" aria-hidden="true"></i>&nbsp;Zone Changelog
<button class="dropdown-item btn-primary" type="button" onclick="window.location.href='{{ url_for('domain.changelog', domain_name=domain.name) }}'">
<i class="fa-solid fa-history" aria-hidden="true"></i>&nbsp;Domain Changelog
</button> </button>
{% endif %}
{% if current_user.role.name in ['Administrator', 'Operator'] or allow_user_remove_domain %}
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<button type="button"class="dropdown-item btn-secondary button_delete" onclick="window.location.href='{{ url_for('domain.remove') }}'"> <button type="button" class="dropdown-item btn-secondary button_delete"
<font color="red"> onclick="window.location.href='{{ url_for('domain.remove') }}'">
<i class="fa-solid fa-trash"></i>&nbsp;Remove Domain <span style="color: red;"><i class="fa-solid fa-trash"></i>&nbsp;Remove Zone</span>
</font>
</button> </button>
</div>
{% else %}
<button class="dropdown-item btn-success" type="button" onclick="window.location.href='{{ url_for('domain.domain', domain_name=domain.name) }}'">
<i class="fa-solid fa-cog"></i>&nbsp;Manage Domain
</button>
{% if allow_user_view_history %}
<button class="dropdown-item btn-primary" type="button" onclick="window.location.href='{{ url_for('domain.changelog', domain_name=domain.name) }}'">
<i class="fa-solid fa-history" aria-hidden="true"></i>&nbsp;Domain Changelog
</button>
{% endif %}
{% if allow_user_remove_domain %}
<div class="dropdown-divider"></div>
<button type="button"class="dropdown-item btn-secondary button_delete" onclick="window.location.href='{{ url_for('domain.remove') }}'">
<font color="red">
<i class="fa-solid fa-trash"></i>&nbsp;Remove Domain
</font>
</button>
{% endif %}
{% endif %} {% endif %}
</div> </div>
</div> </div>
</td> </td>
{% endmacro %} {% endmacro %}

View File

@ -1,24 +1,17 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}<title>Zone Records - {{ domain.name | pretty_domain_name }} - {{ SITE_NAME }}</title>{% endblock %}
{% block title %}
<title>
{{ domain.name | pretty_domain_name }} - {{ 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 Records - {{ domain.name | pretty_domain_name }}</h1>
Domain: <b>{{ domain.name | pretty_domain_name }}</b>
</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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Domain: {{ domain.name | pretty_domain_name }}</li> <li class="breadcrumb-item active">Zone Records - {{ domain.name | pretty_domain_name }}</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -27,42 +20,54 @@
{% 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">
<div class="card-header"> <div class="card-header">
{% if domain.type != 'Slave' %} <h3 class="card-title">Zone Editor</h3>
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-primary float-left button_add_record" id="{{ domain.name }}"> <div class="card-tools">
<i class="fa-solid fa-plus"></i>
&nbsp;Add Record
</button>
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-primary float-right button_apply_changes" id="{{ domain.name }}" value="{{ domain.serial }}">
<i class="fa-solid fa-save"></i>
&nbsp;Apply Changes
</button>
{% else %}
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-primary float-left button_update_from_primary" id="{{ domain.name }}">
<i class="fa-solid fa-sync"></i>
&nbsp;Update from Primary
</button>
{% endif %}
{% if current_user.role.name in ['Administrator', 'Operator'] %} {% if current_user.role.name in ['Administrator', 'Operator'] %}
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-primary float-left btn-danger" onclick="window.location.href='{{ url_for('domain.setting', domain_name=domain.name) }}'"> <button type="button" title="Zone Settings"
class="btn btn-primary btn-danger mt-2 mb-2"
onclick="window.location.href='{{ url_for('domain.setting', domain_name=domain.name) }}'">
<i class="fa-solid fa-toolbox"></i> <i class="fa-solid fa-toolbox"></i>
&nbsp;Admin &nbsp;Zone Settings
</button> </button>
{% endif %} {% endif %}
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %} {% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
<button type="button" style="position: relative; margin-left: 20px" class="btn btn-primary button_changelog" id="{{ domain.name }}"> <button type="button" title="Zone Changelog"
class="btn btn-primary ml-2 mt-2 mb-2 button_changelog" id="{{ domain.name }}">
<i class="fa-solid fa-history" aria-hidden="true"></i> <i class="fa-solid fa-history" aria-hidden="true"></i>
&nbsp;Changelog &nbsp;Changelog
</button> </button>
{% endif %} {% endif %}
{% if domain.type != 'Slave' %}
<button type="button" title="Add Record"
class="btn btn-primary ml-2 mt-2 mb-2 button_add_record" id="{{ domain.name }}">
<i class="fa-solid fa-plus"></i>
&nbsp;Add Record
</button>
<button type="button" title="Save Changes"
class="btn btn-primary ml-2 mt-2 mb-2 button_apply_changes"
id="{{ domain.name }}"
value="{{ domain.serial }}">
<i class="fa-solid fa-save"></i>
&nbsp;Save Changes
</button>
{% else %}
<button type="button" title="Update from Primary"
class="btn btn-primary ml-2 mt-2 mb-2 button_update_from_primary"
id="{{ domain.name }}">
<i class="fa-solid fa-sync"></i>
&nbsp;Update from Primary
</button>
{% endif %}
</div> </div>
<div class="card-body"> </div>
<table id="tbl_records" class="table table-bordered table-striped"> <div class="card-body table-responsive">
<table id="tbl_records" class="table table-bordered table-striped table-hover table-sm">
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
@ -75,7 +80,7 @@
<th>Edit</th> <th>Edit</th>
<th>Delete</th> <th>Delete</th>
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %} {% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
<th >Changelog</th> <th>Changelog</th>
{% endif %} {% endif %}
{% else %} {% else %}
<th>Invisible Sorting Column</th> <th>Invisible Sorting Column</th>
@ -90,29 +95,31 @@
<td>{{ record.status }}</td> <td>{{ record.status }}</td>
<td>{{ record.ttl }}</td> <td>{{ record.ttl }}</td>
<td>{{ record.data | pretty_domain_name }}</td> <td>{{ record.data | pretty_domain_name }}</td>
{% if domain.type != 'Secondary' %} {% if domain.type != 'Slave' %}
<td>{{ record.comment }}</td> <td>{{ record.comment }}</td>
<td width="6%"> <td>
{% if record.is_allowed_edit() %} {% if record.is_allowed_edit() %}
<button type="button" class="btn btn-warning button_edit"> <button type="button" class="btn btn-sm btn-warning button_edit">
<i class="fa-solid fa-edit"></i> <i class="fa-solid fa-edit"></i>
</button> </button>
{% else %} {% else %}
<button type="button" class="btn btn-warning"> <button type="button" class="btn btn-sm btn-warning">
<i class="fa-solid fa-exclamation-circle"></i> <i class="fa-solid fa-exclamation-circle"></i>
</button> </button>
{% endif %} {% endif %}
</td> </td>
<td width="6%"> <td>
{% if record.is_allowed_delete() %} {% if record.is_allowed_delete() %}
<button type="button" class="btn btn-danger button_delete"> <button type="button" class="btn btn-sm btn-danger button_delete">
<i class="fa-solid fa-trash"></i> <i class="fa-solid fa-trash"></i>
</button> </button>
{% endif %} {% endif %}
</td> </td>
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %} {% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
<td width="6%"> <td>
<button type="button" onclick="show_record_changelog('{{record.name}}','{{record.type}}',event)" class="btn btn-primary"> <button type="button"
onclick="show_record_changelog('{{ record.name }}','{{ record.type }}',event)"
class="btn btn-primary">
<i class="fa-solid fa-history" aria-hidden="true"></i> <i class="fa-solid fa-history" aria-hidden="true"></i>
</button> </button>
</td> </td>
@ -129,11 +136,24 @@
</div> </div>
</div> </div>
</div> </div>
</section> </section>
{% endblock %}
{% block head_styles %}
<style>
/* Page Specific Overrides */
table#tbl_records thead th:nth-child(1),
table#tbl_records thead th:nth-child(2),
table#tbl_records thead th:nth-child(3),
table#tbl_records thead th:nth-child(4) { width: 100px; }
table#tbl_records tbody td { text-align: center; }
table#tbl_records tbody td:nth-child(0n+5),
table#tbl_records tbody td:nth-child(0n+6) { text-align: left; }
</style>
{% endblock %} {% 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 }};
@ -142,24 +162,24 @@
// set up user data table // set up user data table
$("#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, "retrieve": true,
"columnDefs": [ "columnDefs": [
{ {
type: 'natural', type: 'natural',
@ -175,17 +195,13 @@
visible: false, visible: false,
{% if domain.type != 'Slave' %} {% if domain.type != 'Slave' %}
{% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %} {% if current_user.role.name in ['Administrator', 'Operator'] or SETTING.get('allow_user_view_history') %}
targets: [ 9 ] targets: [9]
{% else %} {% else %}
targets: [ 8 ] targets: [8]
{% endif %} {% endif %}
{% else %} {% else %}
targets: [ 5 ] targets: [5]
{% endif %} {% endif %}
},
{
className: "length-break",
targets: [ 4, 5 ]
} }
], ],
{% if domain.type != 'Slave' %} {% if domain.type != 'Slave' %}
@ -204,14 +220,15 @@
e.stopPropagation(); e.stopPropagation();
window.location.href = "/domain/{{domain.name}}/changelog/" + record_name + "./" + record_type; window.location.href = "/domain/{{domain.name}}/changelog/" + record_name + "./" + record_type;
} }
// handle changelog button // handle changelog button
$(document.body).on("click", ".button_changelog", function(e) { $(document.body).on("click", ".button_changelog", function (e) {
e.stopPropagation(); e.stopPropagation();
window.location.href = "/domain/{{domain.name}}/changelog"; window.location.href = "/domain/{{domain.name}}/changelog";
}); });
// 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();
var modal = $("#modal_delete"); var modal = $("#modal_delete");
var table = $("#tbl_records").DataTable(); var table = $("#tbl_records").DataTable();
@ -221,18 +238,18 @@
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.modal('show'); modal.modal('show');
$("#button_delete_confirm").unbind().one('click', function(e) { $("#button_delete_confirm").unbind().one('click', function (e) {
table.row(nRow).remove().draw(); table.row(nRow).remove().draw();
modal.modal('hide'); modal.modal('hide');
}); });
$("#button_delete_cancel").unbind().one('click', function(e) { $("#button_delete_cancel").unbind().one('click', function (e) {
modal.modal('hide'); modal.modal('hide');
}); });
}); });
// handle edit button and record click // handle edit button and record click
{% if domain.type != 'Slave' %} {% if domain.type != 'Slave' %}
$(document.body).on("click", ".button_edit{% if quick_edit %}, .row_record{% endif %}", function(e) { $(document.body).on("click", ".button_edit{% if quick_edit %}, .row_record{% endif %}", function (e) {
e.stopPropagation(); e.stopPropagation();
if ($(this).is('tr')) { if ($(this).is('tr')) {
var nRow = $(this)[0]; var nRow = $(this)[0];
@ -263,7 +280,7 @@
{% endif %} {% endif %}
// handle apply changes button // handle apply changes button
$(document.body).on("click",".button_apply_changes", function() { $(document.body).on("click", ".button_apply_changes", function () {
if (nNew || nEditing) { if (nNew || nEditing) {
showErrorModal("Previous record not saved. Please save it before applying the changes."); showErrorModal("Previous record not saved. Please save it before applying the changes.");
return; return;
@ -277,7 +294,7 @@
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
// following unbind("click") is to avoid multiple times execution // following unbind("click") is to avoid multiple times execution
modal.find('#button_apply_confirm').unbind("click").click(function() { modal.find('#button_apply_confirm').unbind("click").click(function () {
var data = {'serial': serial, 'record': getTableData(table), '_csrf_token': '{{ csrf_token() }}'}; var data = {'serial': serial, 'record': getTableData(table), '_csrf_token': '{{ csrf_token() }}'};
applyRecordChanges(data, domain); applyRecordChanges(data, domain);
modal.modal('hide'); modal.modal('hide');
@ -334,7 +351,10 @@
//handle update_from_primary button //handle update_from_primary button
$(document.body).on("click", ".button_update_from_primary", function (e) { $(document.body).on("click", ".button_update_from_primary", function (e) {
var domain = $(this).prop('id'); var domain = $(this).prop('id');
applyChanges({'domain': domain, '_csrf_token': '{{ csrf_token() }}'}, $SCRIPT_ROOT + '/domain/' + domain + '/update', true); applyChanges({
'domain': domain,
'_csrf_token': '{{ csrf_token() }}'
}, $SCRIPT_ROOT + '/domain/' + domain + '/update', true);
}); });
{% if SETTING.get('record_helper') %} {% if SETTING.get('record_helper') %}
@ -363,7 +383,7 @@
"; ";
} }
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();
@ -389,7 +409,7 @@
"; ";
} }
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;
@ -425,7 +445,7 @@
"; ";
} }
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();
@ -475,7 +495,7 @@
"; ";
} }
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();
@ -514,7 +534,7 @@
"; ";
} }
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 () {
tlsa_certificate_usage = modal.find('#tlsa_certificate_usage').val(); tlsa_certificate_usage = modal.find('#tlsa_certificate_usage').val();
tlsa_selector = modal.find('#tlsa_selector').val(); tlsa_selector = modal.find('#tlsa_selector').val();
tlsa_matching = modal.find('#tlsa_matching').val(); tlsa_matching = modal.find('#tlsa_matching').val();
@ -532,9 +552,9 @@
<textarea style=\"min-width: 100%;color: #333;\" placeholder=\"Your TXT record data\" rows=\"5\" id=\"txt_record\" name=\"txt_record\">" + txt_data + "</textarea> \ <textarea style=\"min-width: 100%;color: #333;\" placeholder=\"Your TXT record data\" rows=\"5\" id=\"txt_record\" name=\"txt_record\">" + txt_data + "</textarea> \
"; ";
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 () {
data = modal.find('#txt_record').val(); data = modal.find('#txt_record').val();
if (! /^".*"$/.test(data)) { if (!/^".*"$/.test(data)) {
data = '"' + data + '"'; data = '"' + data + '"';
} }
record_data.val(data); record_data.val(data);
@ -551,10 +571,10 @@
<textarea style=\"min-width: 100%;color: #333;\" placeholder=\"Your LUA snippet\" rows=\"5\" id=\"lua_record\" name=\"lua_record\">" + lua_data + "</textarea> \ <textarea style=\"min-width: 100%;color: #333;\" placeholder=\"Your LUA snippet\" rows=\"5\" id=\"lua_record\" name=\"lua_record\">" + lua_data + "</textarea> \
"; ";
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 () {
type = modal.find('#lua_type').val(); type = modal.find('#lua_type').val();
data = modal.find('#lua_record').val(); data = modal.find('#lua_record').val();
if (! /^".*"$/.test(data)) { if (!/^".*"$/.test(data)) {
data = '"' + data + '"'; data = '"' + data + '"';
} }
data = type + ' ' + data; data = type + ' ' + data;
@ -566,11 +586,11 @@
}); });
{% endif %} {% endif %}
window.onload = function() { window.onload = function () {
document.getElementById("loading-spinner").style.display = "none"; document.getElementById("loading-spinner").style.display = "none";
} }
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
@ -587,7 +607,8 @@
<p></p> <p></p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary pull-left" id="button_delete_cancel" data-dismiss="modal"> <button type="button" class="btn btn-secondary pull-left" id="button_delete_cancel"
data-dismiss="modal">
<i class="fa-solid fa-window-close"></i>&nbsp;Close <i class="fa-solid fa-window-close"></i>&nbsp;Close
</button> </button>
<button type="button" class="btn btn-danger" id="button_delete_confirm"> <button type="button" class="btn btn-danger" id="button_delete_confirm">

View File

@ -1,12 +1,6 @@
{% 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">
@ -14,14 +8,13 @@
<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>
<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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Domain - New Domain</li> <li class="breadcrumb-item active">New Zone</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -30,56 +23,66 @@
{% 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-header">
<h3 class="card-title">Create new domain</h3>
</div>
<form role="form" method="post" action="{{ url_for('domain.add') }}"> <form role="form" method="post" action="{{ url_for('domain.add') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <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 Editor</h3>
</div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" name="domain_name" id="domain_name" placeholder="Enter a valid domain name (required)"> <label for="domain_name">Zone Name</label>
<input type="text" class="form-control" name="domain_name" id="domain_name"
placeholder="Enter a valid zone name (required)">
</div> </div>
{% if domain_override_toggle == True %} {% if domain_override_toggle == True %}
<div class="form-group"> <div class="form-group">
<label>Domain Override Record</label> <input type="checkbox" id="domain_override" name="domain_override"
<input type="checkbox" id="domain_override" name="domain_override" class="checkbox"> class="checkbox">
&nbsp;
<label for="domain_override">Zone Override Record</label>
</div> </div>
{% endif %} {% endif %}
<select name="accountid" class="form-control" style="width:15em;"> <div class="form-group">
<label for="selAccount">Account</label>
<select name="accountid" id="selAccount" class="form-control">
<option value="0">- No Account -</option> <option value="0">- No Account -</option>
{% for account in accounts %} {% for account in accounts %}
<option value="{{ account.id }}">{{ account.name }}</option> <option value="{{ account.id }}">{{ account.name }}</option>
{% endfor %} {% endfor %}
</select> </select>
<br /> </div>
<div class="form-group"> <div class="form-group">
<label>Type</label> <label>Zone Type</label>
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" name="radio_type" id="radio_type_native" value="native" checked> <input type="radio" name="radio_type" id="radio_type_native" value="native"
checked>
Native Native
</label> </label>
</div> </div>
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" name="radio_type" id="radio_type_primary" value="primary"> <input type="radio" name="radio_type" id="radio_type_primary"
value="primary">
Primary Primary
</label> </label>
</div> </div>
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" name="radio_type" id="radio_type_secondary" value="secondary"> <input type="radio" name="radio_type" id="radio_type_secondary"
value="secondary">
Secondary Secondary
</label> </label>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Select a template</label> <label for="domain_template">Zone Template</label>
<select class="form-control" id="domain_template" name="domain_template"> <select class="form-control" id="domain_template" name="domain_template">
<option value="0">No template</option> <option value="0">No template</option>
{% for template in templates %} {% for template in templates %}
@ -88,105 +91,137 @@
</select> </select>
</div> </div>
<div class="form-group" style="display: none;" id="domain_primary_address_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)"> <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>
<div class="form-group"> <div class="form-group">
<label>SOA-EDIT-API</label> <label>SOA-EDIT-API</label>
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_default" value="DEFAULT" checked> <input type="radio" name="radio_type_soa_edit_api" id="radio_default"
value="DEFAULT" checked>
DEFAULT DEFAULT
</label> </label>
</div> </div>
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_increase" value="INCREASE"> <input type="radio" name="radio_type_soa_edit_api" id="radio_increase"
value="INCREASE">
INCREASE INCREASE
</label> </label>
</div> </div>
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_epoch" value="EPOCH"> <input type="radio" name="radio_type_soa_edit_api" id="radio_epoch"
value="EPOCH">
EPOCH EPOCH
</label> </label>
</div> </div>
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" name="radio_type_soa_edit_api" id="radio_off" value="OFF"> OFF <input type="radio" name="radio_type_soa_edit_api" id="radio_off"
value="OFF"> OFF
</label> </label>
</div> </div>
</div> </div>
</div> </div>
<!-- /.card-body -->
<div class="card-footer"> <div class="card-footer">
<button type="button" class="btn btn-secondary" onclick="window.location.href='{{ url_for('dashboard.dashboard') }}'"> <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 <i class="fa-solid fa-window-close"></i>&nbsp;Cancel
</button> </button>
<button type="submit" class="btn btn-primary float-right"> <button type="submit" title="Create Zone" class="btn btn-primary float-right">
<i class="fa-solid fa-save"></i>&nbsp;Create <i class="fa-solid fa-save"></i>&nbsp;Create Zone
</button> </button>
</div> </div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form> </form>
</div> </div>
</div> <!-- /.col -->
<div class="col-8"> <div class="col-12 col-sm-6 col-lg-8">
<div class="card"> <div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Help with creating a new domain</h3> <h3 class="card-title">Zone Editor Help</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<dt>Domain name</dt> <dt>Zone Name</dt>
<dd>Enter your domain name in the format of name.tld (eg. powerdns-admin.com). You can also <dd>Enter your zone name in the format of name.tld (eg. powerdns-admin.com). You can
enter sub-domains to create a sub-root zone (eg. sub.powerdns-admin.com) in case you want to also enter sublevel zones to create delegate zones (eg. sub.powerdns-admin.com)
delegate sub-domain management to specific users. which can be useful for delegating control to other users.
</dd> </dd>
<dt>Type</dt> <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. <dd>The type decides how the domain will be replicated across multiple DNS servers.
<ul> <ul>
<li> <li>
<b>Native - </b>{{ SITE_NAME }} will not perform any Primary or Secondary zone functions. <strong>Native</strong> - The server will not perform any Primary or Secondary
zone functions.
</li> </li>
<li> <li>
<b>Primary - </b>{{ SITE_NAME }} will serve as the Primary and will send zone transfers <strong>Primary</strong> - The server will serve as the Primary and will send
(AXFRs) to other servers configured as secondaries. zone transfers (AXFRs) to other servers configured as secondaries.
</li> </li>
<li> <li>
<b>Secondary - </b>{{ SITE_NAME }} will serve as the Secondary and will request and receive <strong>Secondary</strong> - The server will serve as the Secondary and will
zone transfers (AXFRs) from other servers configured as Primaries. request and receive zone transfers (AXFRs) from other servers configured as
Primaries.
</li> </li>
</ul> </ul>
</dd> </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> <dt>SOA-EDIT-API</dt>
<dd>The SOA-EDIT-API setting defines how the SOA serial number will be updated after a change is <dd>The SOA-EDIT-API setting defines how the SOA serial number will be updated after a
change is
made to the domain. made to the domain.
<ul> <ul>
<li> <li>
<b>DEFAULT - </b>Generate a soa serial of YYYYMMDD01. If the current serial is lower than <strong>DEFAULT</strong> - Generate a soa serial of YYYYMMDD01. If the current serial
the generated serial, use the generated serial. If the current serial is higher or 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. equal to the generated serial, increase the current serial by 1.
</li> </li>
<li> <li>
<b>INCREASE - </b>Increase the current serial by 1. <strong>INCREASE</strong> - Increase the current serial by 1.
</li> </li>
<li> <li>
<b>EPOCH - </b>Change the serial to the number of seconds since the EPOCH, aka unixtime. <strong>EPOCH</strong> - Change the serial to the number of seconds since the EPOCH,
AKA UNIX timestamps
</li> </li>
<li> <li>
<b>OFF - </b>Disable automatic updates of the SOA serial. <strong>OFF</strong> - Disable automatic updates of the SOA serial.
</li> </li>
</ul> </ul>
</dd> </dd>
</dl> </dl>
<p> <p>
Find more details at <a href="https://docs.powerdns.com/md/">https://docs.powerdns.com/md/</a> Find more details at <a href="https://docs.powerdns.com/md/" target="_blank">https://docs.powerdns.com/md/</a>
</p> </p>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</section> <!-- /.container-fluid -->
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
@ -221,7 +256,8 @@
<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"
id="button_close_warn_modal">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
@ -229,7 +265,8 @@
<p></p> <p></p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary center-block" data-dismiss="modal" id="button_confirm_warn_modal"> <button type="button" class="btn btn-primary center-block" data-dismiss="modal"
id="button_confirm_warn_modal">
CLOSE CLOSE
</button> </button>
</div> </div>

View File

@ -1,12 +1,6 @@
{% 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">
@ -14,14 +8,13 @@
<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>
<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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Domain - Remove Domain</li> <li class="breadcrumb-item active">Remove Zone</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -30,71 +23,76 @@
{% 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">
<div class="form-group">
<label for="domainid">Zone</label>
<select id=domainid class="form-control" style="width:15em;"> <select id=domainid class="form-control" style="width:15em;">
<option value="0">- Select Domain -</option> <option value="0">- Select Domain -</option>
{% for domain in domainss %} {% for domain in domainss %}
<option value="{{ domain.id }}">{{ domain.name }}</option> <option value="{{ domain.id }}">{{ domain.name }}</option>
{% endfor %} {% endfor %}
</select> </select>
<br /> </div>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<button type="button" class="btn btn-secondary" onclick="window.location.href='{{ url_for('dashboard.dashboard') }}'"> <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 <i class="fa-solid fa-window-close"></i>&nbsp;Cancel
</button> </button>
<button type="button" class="btn btn-danger button_delete float-right"> <button type="button" title="Remove Zone"
<i class="fa-solid fa-trash-alt"></i>&nbsp;Remove class="btn btn-danger button_delete float-right">
<i class="fa-solid fa-trash-alt"></i>&nbsp;Remove Zone
</button> </button>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<div class="col-8"> <div class="col-12 col-sm-6 col-lg-8">
<div class="card"> <div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Help with removing a domain</h3> <h3 class="card-title">Zone Selector Help</h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<dt>Domain Name</dt> <dt>Zone</dt>
<dd>Select the domain you wish to remove from the system.</dd> <dd>Select the zone you wish to remove from the system.</dd>
</dl> </dl>
<p>Find more details at <a href="https://docs.powerdns.com/md/">https://docs.powerdns.com/md/</a></p> <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>
</div> </div>
</div> </div>
</section> </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 () {
@ -103,19 +101,19 @@
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>
@ -124,14 +122,16 @@
<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"
data-dismiss="modal">
<i class="fa-solid fa-window-close"></i>&nbsp;Cancel <i class="fa-solid fa-window-close"></i>&nbsp;Cancel
</button> </button>
<button type="button" class="btn btn-danger float-right" id="button_delete_confirm"> <button type="button" title="Remove Zone" class="btn btn-danger float-right"
<i class="fa-solid fa-trash-alt"></i>&nbsp;Remove id="button_delete_confirm">
<i class="fa-solid fa-trash-alt"></i>&nbsp;Remove Zone
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,14 +1,8 @@
{% 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') }}
@ -24,43 +18,132 @@
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}
<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 Settings Edit Zone - {{ domain.name | pretty_domain_name }}
<small>{{ domain.name | pretty_domain_name }}</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('dashboard.dashboard') }}">Dashboard</a></li> <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> <li class="breadcrumb-item active">Edit Zone - {{ domain.name | pretty_domain_name }}</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">
<div class="row"> <div class="row">
<div class="col-12">
<div class="card"> <div class="col-12 col-sm-4">
<form method="post"
action="{{ url_for('domain.change_account', 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">Change Zone Account</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="form-group">
<label for="selAccountId">Account</label>
<select id="selAccountId" name="accountid" class="form-control" style="width:15em;">
<option value="0">- No Account -</option>
{% for account in accounts %}
<option value="{{ account.id }}"
{% if domain_account.id == account.id %}selected{% endif %}>{{ account.name }}
</option>
{% endfor %}
</select>
</div>
<!-- /.form-group -->
</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>
<!-- /.card -->
</form>
</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">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) }}"> <form method="post" action="{{ url_for('domain.setting', domain_name=domain.name) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Domain Access Control</h3> <h3 class="card-title">Zone Access Control</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-2"> <div class="col-12">
<p>Users on the right have access to manage the records in <p>Users on the right have access to manage the records in
the {{ domain.name | pretty_domain_name }} domain.</p> the {{ domain.name | pretty_domain_name }} domain.</p>
<p>Click on users to move from between columns.</p> <p>Click on users to move from between columns.</p>
@ -69,101 +152,47 @@
and already have access to <b>ALL</b> domains. and already have access to <b>ALL</b> domains.
</p> </p>
</div> </div>
<div class="form-group col-2"> </div>
<!-- /.row -->
<div class="row">
<div class="col-12">
<div class="form-group">
<select multiple="multiple" class="form-control" id="domain_multi_user" <select multiple="multiple" class="form-control" id="domain_multi_user"
name="domain_multi_user[]"> name="domain_multi_user[]">
{% for user in users %} {% for user in users %}
<option {% if user.id in <option {% if user.id in
domain_user_ids %}selected{% endif %} value="{{ user.username }}" domain_user_ids %}selected{% endif %} value="{{ user.username }}"
{% if user.role.name== 'Administrator' %}style="color: red" {% endif %}>{{ {% if user.role.name== 'Administrator' %}style="color: red" {% endif %}>{{ user.username }}</option> {% endfor %}
user.username}}</option> {% endfor %}
</select> </select>
</div> </div>
<!-- /.form-group -->
</div> </div>
<div class="card-body"> </div>
<div class="col-offset-2"> <!-- /.row -->
<div class="form-group"> </div>
<button type="submit" class="btn btn-primary"> <!-- /.card-body -->
<i class="fa-solid fa-check"></i> <div class="card-footer">
Save <button type="submit" title="Save Changes" class="btn btn-primary">
<i class="fa-solid fa-floppy-disk"></i>
Save Changes
</button> </button>
</div> </div>
<!-- /.card-footer -->
</div> </div>
</div> <!-- /.card -->
</div>
</form> </form>
</div> </div>
<!-- /.col -->
</div> </div>
</div> <!-- /.row -->
<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">Account</h3> <h3 class="card-title">Change Zone Type</h3>
</div>
<div class="card-body">
<div class="col-12">
<div class="form-group">
<form method="post"
action="{{ url_for('domain.change_account', domain_name=domain.name) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<select name="accountid" class="form-control" style="width:15em;">
<option value="0">- No Account -</option>
{% for account in accounts %}
<option value="{{ account.id }}"
{% if domain_account.id == account.id %}selected{% endif %}>{{ account.name }}
</option>
{% endfor %}
</select><br />
<button type="submit" class="btn btn-flat btn-primary" id="change_soa_edit_api">
<i class="fa-solid fa-check"></i>&nbsp;Change account for {{ domain.name | pretty_domain_name }}
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Auto PTR creation</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>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<p>The type decides how the domain will be replicated across multiple DNS servers.</p> <p>The type decides how the domain will be replicated across multiple DNS servers.</p>
<ul> <ul>
@ -172,11 +201,13 @@
PowerDNS server or you handle replication via your backend. PowerDNS server or you handle replication via your backend.
</li> </li>
<li> <li>
Primary - This PowerDNS server will serve as the primary and will send zone transfers Primary - This PowerDNS server will serve as the primary and will send zone
transfers
(AXFRs) to other servers configured as secondaries. (AXFRs) to other servers configured as secondaries.
</li> </li>
<li> <li>
Secondary - This PowerDNS server will serve as the secondaries and will request and receive Secondary - This PowerDNS server will serve as the secondaries and will request and
receive
zone transfers (AXFRs) from other servers configured as primaries. zone transfers (AXFRs) from other servers configured as primaries.
</li> </li>
</ul> </ul>
@ -188,33 +219,42 @@
<option value="native">Native</option> <option value="native">Native</option>
<option value="priamry">Primary</option> <option value="priamry">Primary</option>
<option value="secondary">Secondary</option> <option value="secondary">Secondary</option>
</select><br /> </select><br/>
<div class="form-group" style="display: none;" id="domain_primary_address_div"> <div class="form-group" style="display: none;" id="domain_primary_address_div">
<input type="text" class="form-control" name="domain_primary_address" <input type="text" class="form-control" name="domain_primary_address"
id="domain_primary_address" id="domain_primary_address"
placeholder="Enter valid Primary Server IP addresses (separated by commas)"> placeholder="Enter valid Primary Server IP addresses (separated by commas)">
</div> </div>
<button type="submit" class="btn btn-primary" id="change_type"> <button type="submit" title="Update Zone Type" class="btn btn-primary" id="change_type">
<i class="fa-solid fa-check"></i>&nbsp;Change type for {{ domain.name | pretty_domain_name }} <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>
<!-- /.row -->
<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">Change SOA-EDIT-API</h3> <h3 class="card-title">Change SOA-EDIT-API</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<p>The SOA-EDIT-API setting defines how the SOA serial number will be updated after a change is made <p>The SOA-EDIT-API setting defines how the SOA serial number will be updated after a change
is made
to the domain.</p> to the domain.</p>
<ul> <ul>
<li> <li>
DEFAULT - Generate a soa serial of YYYYMMDD01. If the current serial is lower than the DEFAULT - Generate a soa serial of YYYYMMDD01. If the current serial is lower than
generated serial, use the generated serial. If the current serial is higher or equal to the 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. generated serial, increase the current serial by 1.
</li> </li>
<li> <li>
@ -228,7 +268,8 @@
</li> </li>
</ul> </ul>
<b>New SOA-EDIT-API Setting:</b> <b>New SOA-EDIT-API Setting:</b>
<form method="post" action="{{ url_for('domain.change_soa_edit_api', domain_name=domain.name) }}"> <form method="post"
action="{{ url_for('domain.change_soa_edit_api', domain_name=domain.name) }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<select name="soa_edit_api" class="form-control" style="width:15em;"> <select name="soa_edit_api" class="form-control" style="width:15em;">
<option selected value="0">- Unchanged -</option> <option selected value="0">- Unchanged -</option>
@ -236,38 +277,53 @@
<option>INCREASE</option> <option>INCREASE</option>
<option>EPOCH</option> <option>EPOCH</option>
<option>OFF</option> <option>OFF</option>
</select><br /> </select><br/>
<button type="submit" class="btn btn-primary" id="change_soa_edit_api"> <button type="submit" title="Update SOA-EDIT-API" 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 }} <i class="fa-solid fa-floppy-disk"></i>&nbsp;Update SOA-EDIT-API
</button> </button>
</form> </form>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
<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">Domain Deletion</h3> <h3 class="card-title">Remove Zone</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<p>This function is used to remove a domain from PowerDNS-Admin <b>AND</b> PowerDNS. All records and <p>This function is used to remove a domain from PowerDNS-Admin <b>AND</b> PowerDNS. All
records and
user privileges associated with this domain will also be removed. This change cannot be user privileges associated with this domain will also be removed. This change cannot be
reverted.</p> reverted.</p>
<button type="button" class="btn btn-danger float-left delete_domain" <button type="button" title="Delete Zone" class="btn btn-danger float-left delete_domain"
id="{{ domain.name }}"> id="{{ domain.name }}">
<i class="fa-solid fa-trash"></i>&nbsp;DELETE DOMAIN {{ domain.name | pretty_domain_name }} <i class="fa-solid fa-trash"></i>&nbsp;Delete Zone
</button> </button>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</section> <!-- /.container-fluid -->
</div>
<!-- /.content -->
</section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
//initialize pretty checkboxes //initialize pretty checkboxes
$('.dyndns_on_demand_toggle').iCheck({ $('.dyndns_on_demand_toggle').iCheck({
checkboxClass: 'icheckbox_square-blue', checkboxClass: 'icheckbox_square-blue',
@ -369,10 +425,11 @@
} }
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block modals %} {% block modals %}
<div class="modal fade" id="modal_delete_domain"> <div class="modal fade" id="modal_delete_domain">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
@ -387,9 +444,10 @@
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary float-left" data-dismiss="modal">Close</button> <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"> <button type="button" class="btn btn-danger" id="button_delete_confirm">
Delete</button> Delete
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,10 +1,9 @@
<!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">
@ -13,9 +12,8 @@
{%- 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 %}
@ -30,21 +28,24 @@
<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> </div>
<!-- /.error-content --> <!-- /.error-content -->
</div> </div>
<!-- /.error-page --> <!-- /.error-page -->
</section> </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,13 +98,12 @@
} }
</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>
@ -154,12 +152,12 @@
<tr> <tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; border-radius: 5px; text-align: center; background-color: #3498db;" <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" valign="top" align="center"
bgcolor="#3498db"> <a bgcolor="#3498db"><a
href="{{ verification_link }}" href="{{ verification_link }}"
target="_blank" target="_blank"
rel="noopener noreferrer" 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 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> Email Address</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -169,7 +167,8 @@
</table> </table>
<p <p
style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;"> 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> Your confirmation link will be expired in 24 hours. If you did not create an
account, no further action is required.</p>
<p <p
style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;"> style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; margin-bottom: 15px;">
Cheers!</p> Cheers!</p>
@ -214,7 +213,7 @@
</td> </td>
<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>
</tr> </tr>
</table> </table>
</body> </body>
</html> </html>

View File

@ -1,25 +1,17 @@
{% 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
<small>Error</small>
</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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">400 Error</li> <li class="breadcrumb-item active">HTTP 400 Error</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -30,13 +22,11 @@
{% 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
</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! Bad request Oops! Bad Request
</h3> </h3>
<p> <p>
{% if msg %} {% if msg %}

View File

@ -1,25 +1,18 @@
{% 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
<small>Error</small>
</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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">403 Error</li> <li class="breadcrumb-item active">HTTP 403 Error</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -31,12 +24,12 @@
<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

View File

@ -1,25 +1,17 @@
{% 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
<small>Error</small>
</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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">404 Error</li> <li class="breadcrumb-item active">HTTP 404 Error</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -30,13 +22,11 @@
{% 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
</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! You're lost Oops! Page not found.
</h3> </h3>
<p> <p>
The page you requested could not be found. The page you requested could not be found.

View File

@ -1,25 +1,17 @@
{% 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
<small>Error</small>
</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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">500 Error</li> <li class="breadcrumb-item active">HTTP 500 Error</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -30,13 +22,11 @@
{% 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
</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! Something went wrong Oops! Something went wrong.
</h3> </h3>
<p> <p>
Try again later. Try again later.

View File

@ -1,20 +1,12 @@
{% 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
<small>Error</small>
</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">
@ -31,16 +23,14 @@
<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
</h1>
</div> </div>
<br/> <br/>
<br/> <br/>
<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! Something went wrong Oops! Something went wrong with your authentication.
</h3> </h3>
<br> <br>
<p> <p>

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>
@ -14,10 +14,10 @@
{% 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') %}
@ -38,16 +38,21 @@
<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="Username" name="username" data-error="Please input your username" required {% if username %}value="{{ username }}" {% endif %}> <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> <span class="help-block with-errors"></span>
</div> </div>
<div class="form-group"> <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 %}> <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> <span class="help-block with-errors"></span>
</div> </div>
{% if SETTING.get('otp_field_enabled') %} {% if SETTING.get('otp_field_enabled') %}
<div class="form-group"> <div class="form-group">
<input type="otptoken" class="form-control" placeholder="OTP Token" name="otptoken" autocomplete="off"> <input type="otptoken" class="form-control" placeholder="OTP Token" name="otptoken"
autocomplete="off">
</div> </div>
{% endif %} {% endif %}
{% if SETTING.get('ldap_enabled') and SETTING.get('local_db_enabled') %} {% if SETTING.get('ldap_enabled') and SETTING.get('local_db_enabled') %}
@ -101,25 +106,29 @@
<p>- OR -</p> <p>- OR -</p>
{% endif %} {% endif %}
{% if SETTING.get('oidc_oauth_enabled') %} {% if SETTING.get('oidc_oauth_enabled') %}
<a href="{{ url_for('index.oidc_login') }}" class="btn btn-block btn-social btn-openid btn-flat"> <a href="{{ url_for('index.oidc_login') }}"
class="btn btn-block btn-social btn-openid btn-flat">
<i class="fa-brands fa-openid"></i> <i class="fa-brands fa-openid"></i>
Sign in using OpenID Connect Sign in using OpenID Connect
</a> </a>
{% endif %} {% endif %}
{% if SETTING.get('github_oauth_enabled') %} {% if SETTING.get('github_oauth_enabled') %}
<a href="{{ url_for('index.github_login') }}" class="btn btn-block btn-social btn-github btn-flat"> <a href="{{ url_for('index.github_login') }}"
class="btn btn-block btn-social btn-github btn-flat">
<i class="fa-brands fa-github"></i> <i class="fa-brands fa-github"></i>
Sign in using Github Sign in using Github
</a> </a>
{% endif %} {% endif %}
{% if SETTING.get('google_oauth_enabled') %} {% if SETTING.get('google_oauth_enabled') %}
<a href="{{ url_for('index.google_login') }}" class="btn btn-block btn-social btn-google btn-flat"> <a href="{{ url_for('index.google_login') }}"
class="btn btn-block btn-social btn-google btn-flat">
<i class="fa-brands fa-google"></i> <i class="fa-brands fa-google"></i>
Sign in using Google Sign in using Google
</a> </a>
{% endif %} {% endif %}
{% if SETTING.get('azure_oauth_enabled') %} {% if SETTING.get('azure_oauth_enabled') %}
<a href="{{ url_for('index.azure_login') }}" class="btn btn-block btn-social btn-microsoft btn-flat"> <a href="{{ url_for('index.azure_login') }}"
class="btn btn-block btn-social btn-microsoft btn-flat">
<i class="fa-brands fa-windows"></i> <i class="fa-brands fa-windows"></i>
Sign in using Microsoft Azure Sign in using Microsoft Azure
</a> </a>
@ -144,20 +153,21 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
<!-- /.card-body -->
</div> </div>
<div class="login-box-footer"> <!-- /.card -->
<center> </div>
<p>Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></p> <!-- /.login-box -->
</center> <div class="text-center pt-3">
</div> <p>Powered by <a href="https://powerdnsadmin.org" target="_blank">PowerDNS-Admin</a></p>
</div> </div>
{% 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 () {
$('input').iCheck({ $('input').iCheck({
checkboxClass: 'icheckbox_square-blue', checkboxClass: 'icheckbox_square-blue',
@ -165,6 +175,6 @@
increaseArea: '20%' // optional increaseArea: '20%' // optional
}); });
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,6 +1,8 @@
<!doctype html> <!DOCTYPE html>
<title>Site Maintenance</title> <html lang="en" class>
<style> <head>
<title>Site Maintenance</title>
<style>
body { body {
text-align: center; text-align: center;
padding: 150px; padding: 150px;
@ -31,13 +33,17 @@
color: #333; color: #333;
text-decoration: none; text-decoration: none;
} }
</style> </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
System
Administrator if you need more information, otherwise we&rsquo;ll be back online shortly!</p> Administrator if you need more information, otherwise we&rsquo;ll be back online shortly!</p>
<p>&mdash; Team</p> <p>&mdash; Team</p>
</div> </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>
@ -12,11 +11,11 @@
{% 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') %}
@ -44,7 +43,10 @@
<i class="fas fa-user"></i> <i class="fas fa-user"></i>
</span> </span>
</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> <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 %} {% if 'firstname' in error_messages %}
<div class="invalid-feedback"> <div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i> <i class="fas fa-exclamation-triangle"></i>
@ -61,7 +63,10 @@
<i class="fas fa-user"></i> <i class="fas fa-user"></i>
</span> </span>
</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> <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 %} {% if 'lastname' in error_messages %}
<div class="invalid-feedback"> <div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i> <i class="fas fa-exclamation-triangle"></i>
@ -78,7 +83,8 @@
<i class="fas fa-envelope"></i> <i class="fas fa-envelope"></i>
</span> </span>
</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> <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 %} {% if 'email' in error_messages %}
<div class="invalid-feedback"> <div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i> <i class="fas fa-exclamation-triangle"></i>
@ -97,7 +103,10 @@
<i class="fas fa-user"></i> <i class="fas fa-user"></i>
</span> </span>
</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> <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 %} {% if 'username' in error_messages %}
<div class="invalid-feedback"> <div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i> <i class="fas fa-exclamation-triangle"></i>
@ -114,7 +123,9 @@
<i class="fas fa-lock"></i> <i class="fas fa-lock"></i>
</span> </span>
</div> </div>
<input type="password" class="form-control {{ 'is-invalid' if 'password' in error_messages else '' }}" placeholder="Password" id="password" name="password" required> <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 %} {% if 'password' in error_messages %}
<div class="invalid-feedback"> <div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i> <i class="fas fa-exclamation-triangle"></i>
@ -131,7 +142,9 @@
<i class="fas fa-lock"></i> <i class="fas fa-lock"></i>
</span> </span>
</div> </div>
<input type="password" class="form-control {{ 'is-invalid' if 'rpassword' in error_messages else '' }}" placeholder="Retype password" id="rpassword" name="rpassword" required> <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 %} {% if 'rpassword' in error_messages %}
<div class="invalid-feedback"> <div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i> <i class="fas fa-exclamation-triangle"></i>
@ -151,7 +164,9 @@
<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"
class="form-control {{ 'is-invalid' if 'captcha_result' in error_messages else '' }}"
placeholder="CAPTCHA" id="captcha" name="captcha" required>
{% if 'captcha_result' in error_messages %} {% if 'captcha_result' in error_messages %}
<div class="invalid-feedback"> <div class="invalid-feedback">
<i class="fas fa-exclamation-triangle"></i> <i class="fas fa-exclamation-triangle"></i>
@ -169,24 +184,26 @@
</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> <!-- /.card -->
{% assets "js_login" -%} </div>
<!-- /.register-box -->
<div class="text-center pt-3">
<p>Powered by <a href="https://powerdnsadmin.org" target="_blank">PowerDNS-Admin</a></p>
</div>
{% 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,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>
@ -14,10 +13,9 @@
{% 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>
@ -28,35 +26,36 @@
{{ 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>
@ -74,17 +73,17 @@
</div> </div>
</form> </form>
</div> </div>
<div class="login-box-footer"> <!-- /.register-box-body -->
<center> <div class="text-center">
<p>Powered by <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin">PowerDNS-Admin</a></p> <p>Powered by <a href="https://powerdnsadmin.org" target="_blank">PowerDNS-Admin</a></p>
</center>
</div> </div>
</div> </div>
</body> <!-- /.register-box -->
{% assets "js_login" -%} </body>
{% 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 %}
</html> </html>

View File

@ -1,10 +1,9 @@
<!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">
@ -13,9 +12,8 @@
{%- 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>
@ -33,7 +31,8 @@
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="form-group has-feedback"> <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> <input type="email" name="email" class="form-control" placeholder="Email address"
data-error="Please input your email" required>
<span class="glyphicon glyphicon-envelope form-control-feedback"></span> <span class="glyphicon glyphicon-envelope form-control-feedback"></span>
<span class="help-block with-errors"></span> <span class="help-block with-errors"></span>
</div> </div>
@ -59,28 +58,25 @@
</div> </div>
</form> </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,45 +1,35 @@
{% 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"> <h1 class="m-0 text-dark">Zone Templates</h1>
Templates
<small>List</small>
</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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Templates - List</li> <li class="breadcrumb-item active">Zone Templates</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;
</button>
<h4> <h4>
<i class="fa-solid fa-ban"></i> Error! <i class="fa-solid fa-ban"></i> Error!
</h4> </h4>
@ -56,25 +46,30 @@
</div> </div>
{% endif %} {% endif %}
{% endwith %} {% endwith %}
<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">Templates</h3> <h3 class="card-title">Templates</h3>
<div class="card-tools">
<a href="{{ url_for('admin.create_template') }}"> <a href="{{ url_for('admin.create_template') }}">
<button type="button" class="btn btn-primary float-right"> <button type="button" class="btn btn-primary">
<i class="fa-solid fa-plus"></i>&nbsp;Create Template <i class="fa-solid fa-plus"></i> Create Template
</button> </button>
</a> </a>
</div> </div>
<div class="card-body"> </div>
<table id="tbl_template_list" class="table table-bordered table-striped"> <!-- /.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> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Description</th> <th>Description</th>
<th>Number of Records</th> <th>Total Records</th>
<th width="20%">Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -85,26 +80,27 @@
<strong>{{ template.name }}</strong> <strong>{{ template.name }}</strong>
</a> </a>
</td> </td>
<td> <td>{{ template.description }}</td>
{{ template.description }} <td>{{ template.records|count }}</td>
</td>
<td>
{{ template.records|count }}
</td>
<td> <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"
<i class="fa-solid fa-bars"></i>&nbsp;Actions id="dropdownMenu" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<i class="fa-solid fa-bars"></i>
</button> </button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu"> <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) }}'"> <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 <i class="fa-solid fa-edit"></i>&nbsp;Edit Template
</button> </button>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<button type="button"class="dropdown-item btn-secondary button_delete" id="{{template.name}}"> <button type="button"
<font color="red"> class="dropdown-item btn-secondary button_delete"
id="{{ template.name }}">
<span style="color: red;">
<i class="fa-solid fa-trash"></i>&nbsp;Delete Template <i class="fa-solid fa-trash"></i>&nbsp;Delete Template
</font> </span>
</button> </button>
</div> </div>
</div> </div>
@ -114,16 +110,29 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</section> <!-- /.container-fluid -->
</section>
{% endblock %}
{% block head_styles %}
<style>
/* Page Specific Overrides */
table.records tbody td:first-of-type,
table.records tbody td:nth-child(0n+2) { text-align: left; }
</style>
{% 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,
@ -142,7 +151,7 @@
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,43 +1,37 @@
{% 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</li> <li class="breadcrumb-item active">Create Zone Template</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;
</button>
<h4> <h4>
<i class="fa-solid fa-ban"></i> Error! <i class="fa-solid fa-ban"></i> Error!
</h4> </h4>
@ -49,63 +43,86 @@
{% endfor -%} {% endfor -%}
</ul> </ul>
</div> </div>
<!-- /.alert-message -->
</div> </div>
<!-- /.alert -->
</div> </div>
<!-- /.col -->
</div>
<!-- /.row -->
{% endif %} {% endif %}
{% endwith %} {% endwith %}
<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-header">
<h3 class="card-title">Create new template</h3>
</div>
<form role="form" method="post" action="{{ url_for('admin.create_template') }}"> <form role="form" method="post" action="{{ url_for('admin.create_template') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}"> <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="card-body">
<div class="form-group"> <div class="form-group">
<label for="name">Template Name</label>
<input type="text" class="form-control" name="name" id="name" <input type="text" class="form-control" name="name" id="name"
placeholder="Enter a valid template name (required)"> placeholder="Enter a valid template name (required)">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="description">Template Description</label>
<input type="text" class="form-control" name="description" id="description" <input type="text" class="form-control" name="description" id="description"
placeholder="Enter a template description (optional)"> placeholder="Enter a template description (optional)">
</div> </div>
</div> </div>
<!-- /.card-body -->
<div class="card-footer"> <div class="card-footer">
<button type="button" class="btn btn-secondary float-left" onclick="window.location.href='{{ url_for('admin.templates') }}'"> <button type="button" class="btn btn-secondary float-left" title="Cancel"
onclick="window.location.href='{{ url_for('admin.templates') }}'">
Cancel Cancel
</button> </button>
<button type="submit" class="btn btn-primary float-right"> <button type="submit" class="btn btn-primary float-right" title="Save Template">
<i class="fa-solid fa-save"></i>&nbsp;Save <i class="fa-solid fa-save"></i>&nbsp;Save Template
</button> </button>
</div> </div>
<!-- /.card-footer -->
</div>
<!-- /.card -->
</form> </form>
</div> </div>
</div> <!-- /.col -->
<div class="col-8">
<div class="card"> <div class="col-12 col-sm-6 col-lg-8">
<div class="card card-outline card-secondary shadow">
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Help with creating a new template</h3> <h3 class="card-title">Zone Template Editor Help</h3>
</div> </div>
<!-- /.card-header -->
<div class="card-body"> <div class="card-body">
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<dt>Template name</dt> <dt>Template Name</dt>
<dd>Enter your template name, this is the name of the template that <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 will be shown to users. The name should not have any spaces but
can have symbols.</dd> can have symbols.
<dt>Template description</dt> </dd>
<dt>Template Description</dt>
<dd>Enter your template description, this is to help better <dd>Enter your template description, this is to help better
identify the template.</dd> identify the template.
</dd>
</dl> </dl>
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</section> <!-- /.container-fluid -->
</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") {
@ -114,5 +131,5 @@
$("#domain_primary_address_div").hide(); $("#domain_primary_address_div").hide();
} }
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,27 +1,18 @@
{% 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>
<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">Template Edit: {{ template }}</li> <li class="breadcrumb-item active">Edit Zone Template</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -30,24 +21,27 @@
{% 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">Manage Template Records for {{ template }}</h3> <h3 class="card-title">Zone Template Editor</h3>
</div> <div class="card-tools">
<div class="card-body"> <button type="button" class="btn btn-primary float-left button_add_record"
<button type="button" class="btn btn-primary float-left button_add_record" id="{{ template }}"> title="Add Record" id="{{ template }}">
<i class="fa-solid fa-plus"></i>&nbsp;Add Record <i class="fa-solid fa-plus"></i>&nbsp;Add Record
</button> </button>
<button type="button" class="btn btn-primary float-right button_apply_changes" id="{{ template }}"> <button type="button" class="btn btn-primary float-right button_apply_changes ml-2"
<i class="fa-solid fa-save"></i>&nbsp;Save Changes title="Save Template" id="{{ template }}">
<i class="fa-solid fa-save"></i>&nbsp;Save Template
</button> </button>
</div> </div>
<div class="card-body"> </div>
<table id="tbl_records" class="table table-bordered table-striped"> <div class="card-body table-responsive">
<table id="tbl_records"
class="table table-bordered table-striped table-hover table-sm records">
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
@ -82,20 +76,19 @@
<td> <td>
{{ record.comment }} {{ record.comment }}
</td> </td>
<td width="6%"> <td>
<button type="button" class="btn btn-warning button_edit"> <button type="button" class="btn btn-sm btn-warning button_edit"
<i class="fa-solid fa-edit"></i>&nbsp;Edit title="Edit Record">
</button> <i class="fa-solid fa-edit"></i>
</td>
<td width="6%">
<button type="button" class="btn btn-danger button_delete">
<i class="fa-solid fa-trash"></i>&nbsp;Delete
</button> </button>
</td> </td>
<td> <td>
{{ record.id }} <button type="button" class="btn btn-sm btn-danger button_delete"
</td> title="Delete Record">
<i class="fa-solid fa-trash"></i>
</button>
</td> </td>
<td>{{ id }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -105,36 +98,37 @@
</div> </div>
</div> </div>
</div> </div>
</section> </section>
{% endblock %} {% 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, "retrieve": true,
"columnDefs": [ "columnDefs": [
{ {
type: 'natural', type: 'natural',
@ -144,18 +138,18 @@
// hidden column so that we can add new records on top // hidden column so that we can add new records on top
// regardless of whatever sorting is done. See orderFixed // regardless of whatever sorting is done. See orderFixed
visible: false, visible: false,
targets: [ 8 ] targets: [8]
}, },
{ {
className: "length-break", className: "length-break",
targets: [ 4, 5 ] targets: [4, 5]
} }
], ],
"orderFixed": [[8, 'asc']] "orderFixed": [[8, 'asc']]
}); });
// 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();
var modal = $("#modal_delete"); var modal = $("#modal_delete");
var table = $("#tbl_records").DataTable(); var table = $("#tbl_records").DataTable();
@ -165,17 +159,18 @@
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
modal.modal('show'); modal.modal('show');
$("#button_delete_confirm").unbind().one('click', function(e) { $("#button_delete_confirm").unbind().one('click', function (e) {
table.row(nRow).remove().draw(); table.row(nRow).remove().draw();
modal.modal('hide'); modal.modal('hide');
}); });
$("#button_delete_cancel").unbind().one('click', function(e) { $("#button_delete_cancel").unbind().one('click', function (e) {
modal.modal('hide'); modal.modal('hide');
}); });
}); });
// handle edit button and record click // handle edit button and record click
$(document.body).on("click", ".button_edit{% if quick_edit %}, .row_record{% endif %}", function(e) { $(document.body).on("click", ".button_edit{% if quick_edit %}, .row_record{% endif %}", function (e) {
e.stopPropagation(); e.stopPropagation();
if ($(this).is('tr')) { if ($(this).is('tr')) {
var nRow = $(this)[0]; var nRow = $(this)[0];
@ -205,7 +200,7 @@
}); });
// handle apply changes button // handle apply changes button
$(document.body).on("click",".button_apply_changes", function() { $(document.body).on("click", ".button_apply_changes", function () {
if (nNew || nEditing) { if (nNew || nEditing) {
showErrorModal("Previous record not saved. Please save it before applying the changes."); showErrorModal("Previous record not saved. Please save it before applying the changes.");
return; return;
@ -218,9 +213,12 @@
modal.find('.modal-body p').text(info); modal.find('.modal-body p').text(info);
// following unbind("click") is to avoid multiple times execution // following unbind("click") is to avoid multiple times execution
modal.find('#button_apply_confirm').unbind("click").click(function() { modal.find('#button_apply_confirm').unbind("click").click(function () {
var data = getTableData(table); var data = getTableData(table);
applyChanges( {'_csrf_token': '{{ csrf_token() }}', 'records': data}, $SCRIPT_ROOT + '/admin/template/' + template + '/apply', true); applyChanges({
'_csrf_token': '{{ csrf_token() }}',
'records': data
}, $SCRIPT_ROOT + '/admin/template/' + template + '/apply', true);
modal.modal('hide'); modal.modal('hide');
}) })
modal.modal('show'); modal.modal('show');
@ -294,7 +292,7 @@
"; ";
} }
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();
@ -320,7 +318,7 @@
"; ";
} }
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;
@ -356,7 +354,7 @@
"; ";
} }
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();
@ -406,7 +404,7 @@
"; ";
} }
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();
@ -423,10 +421,11 @@
} }
}); });
{% 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">
@ -440,7 +439,8 @@
<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"
data-dismiss="modal">
Close Close
</button> </button>
<button type="button" class="btn btn-danger" id="button_delete_confirm"> <button type="button" class="btn btn-danger" id="button_delete_confirm">
@ -449,8 +449,8 @@
</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">
@ -473,8 +473,8 @@
</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">
@ -492,5 +492,5 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,27 +1,18 @@
{% 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
<small>My Profile</small>
</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('dashboard.dashboard') }}">Dashboard</a></li> <li class="breadcrumb-item"><a href="{{ url_for('dashboard.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">My Profile</li> <li class="breadcrumb-item active">Edit Profile</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -30,13 +21,17 @@
{% 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>
</div>
<!-- /.card-header -->
<div class="card-body">
<div class="nav-tabs-custom mb-2">
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" href="#tabs-personal" data-toggle="tab"> <a class="nav-link active" href="#tabs-personal" data-toggle="tab">
@ -58,63 +53,79 @@
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
<div class="card-body"> </div>
<!-- /.nav-tabs-custom -->
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane fade show active" id="tabs-personal"> <div class="tab-pane fade show active" id="tabs-personal">
<form role="form" method="post" action="{{ user_profile }}"> <form role="form" method="post" action="{{ user_profile }}">
<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">
<label for="firstname">First Name</label> <label for="firstname">First Name</label>
<input type="text" class="form-control" name="firstname" id="firstname" placeholder="{{ current_user.firstname }}" <input type="text" class="form-control" name="firstname"
id="firstname" placeholder="{{ current_user.firstname }}"
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}> {% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="lastname">Last Name</label> <label for="lastname">Last Name</label>
<input type="text" class="form-control" name="lastname" id="lastname" placeholder="{{ current_user.lastname }}" <input type="text" class="form-control" name="lastname"
id="lastname" placeholder="{{ current_user.lastname }}"
{% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}> {% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="email">E-Mail</label> <input type="email" class="form-control" name="email" id="email" placeholder="{{ current_user.email }}" <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 %}> {% if session['authentication_type'] != 'LOCAL' %}disabled{% endif %}>
</div> </div>
{% if session['authentication_type'] == 'LOCAL' %} {% if session['authentication_type'] == 'LOCAL' %}
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary" title="Save Profile">
<i class="fa-solid fa-floppy-disk"></i>&nbsp;Save <i class="fa-solid fa-floppy-disk"></i>&nbsp;Save Profile
</button> </button>
</div> </div>
{% endif %} {% endif %}
</form> </form>
</div> </div>
<!-- /.tab-pane -->
{% if session['authentication_type'] == 'LOCAL' %} {% if session['authentication_type'] == 'LOCAL' %}
<div class="tab-pane fade" id="tabs-password"> <div class="tab-pane fade" id="tabs-password">
{% if not current_user.password %} {% if not current_user.password %}
Your account password is managed via LDAP which isn't supported to change here. Your account password is managed via LDAP which isn't supported to
change here.
{% else %} {% else %}
<form action="{{ user_profile }}" method="post"> <form action="{{ user_profile }}" method="post">
<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">
<label for="password">New Password</label> <label for="password">New Password</label>
<input type="password" class="form-control" name="password" id="newpassword"> <input type="password" class="form-control" name="password"
id="newpassword">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="rpassword">Re-type New Password</label> <label for="rpassword">Re-type New Password</label>
<input type="password" class="form-control" name="rpassword" id="rpassword"> <input type="password" class="form-control" name="rpassword"
id="rpassword">
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary" title="Save Password">
<i class="fa-solid fa-floppy-disk"></i>&nbsp;Save <i class="fa-solid fa-floppy-disk"></i>&nbsp;Save Password
</button> </button>
</div> </div>
</form> </form>
{% endif %} {% endif %}
</div> </div>
<!-- /.tab-pane -->
{% endif %} {% endif %}
<div class="tab-pane fade" id="tabs-authentication"> <div class="tab-pane fade" id="tabs-authentication">
<form action="{{ user_profile }}" method="post"> <form action="{{ user_profile }}" method="post">
<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="checkbox" id="otp_toggle" class="otp_toggle" {% if current_user.otp_secret %}checked{% endif %}> <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> <label for="otp_toggle">Enable Two Factor Authentication</label>
{% if current_user.otp_secret %} {% if current_user.otp_secret %}
<div id="token_information"> <div id="token_information">
@ -123,47 +134,65 @@
</p> </p>
<div style="position: relative; left: 15px"> <div style="position: relative; left: 15px">
Your secret key is: Your secret key is:
<br /> <br/>
<form> <form>
<input type=text id="otp_secret" value={{current_user.otp_secret}} readonly> <input type=text id="otp_secret"
<button type=button style="position:relative; right:28px" onclick="copy_otp_secret_to_clipboard()"> 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> <i class="fa-solid fa-clipboard"></i>
</button> </button>
<br /> <br/>
<font color="red" id="copy_tooltip" style="visibility:collapse">Copied.</font> <span style="color: red;" id="copy_tooltip"
style="visibility:collapse">Copied.</span>
</form> </form>
</div> </div>
You can use Google Authenticator 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"
- <a target="_blank" href="https://apps.apple.com/us/app/google-authenticator/id388497605"><i class="fa-brands fa-app-store-ios"></i>iOS</a>) 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 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"
- <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>) 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. on your smartphone to scan the QR code.
<br /> <br/>
<font color="red"> <p style="color: red; font-style: italic; font-weight: bold;">
<strong> Make sure only you can see this QR Code and
<i>Make sure only you can see this QR Code and secret key and secret key and
nobody can capture them. nobody can capture them.
</i> </p>
</strong>
</font>
</div> </div>
{% endif %} {% endif %}
</div> </div>
<!-- /.form-group -->
</form> </form>
</div> </div>
<!-- /.tab-pane -->
</div> </div>
<!-- /.tab-content -->
</div> </div>
<!-- /.card-body -->
</div> </div>
<!-- /.card -->
</div> </div>
<!-- /.col -->
</div> </div>
<!-- /.row -->
</div> </div>
</div> <!-- /.container-fluid -->
</section> </section>
{% endblock %} {% endblock %}
{% block extrascripts %} {% block extrascripts %}
<script> <script>
// initialize pretty checkboxes // initialize pretty checkboxes
$('.otp_toggle').iCheck({ $('.otp_toggle').iCheck({
@ -172,7 +201,7 @@
}); });
// 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',
@ -181,10 +210,10 @@
}, },
'_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 %}