Merge pull request #41 from ivanfilippov/new_ui

New ui
This commit is contained in:
Ivan Filippov 2016-05-13 17:54:16 -06:00
commit 655410ed83
17 changed files with 422 additions and 245 deletions

View File

@ -20,6 +20,8 @@ if 'LDAP_TYPE' in app.config.keys():
LDAP_PASSWORD = app.config['LDAP_PASSWORD']
LDAP_SEARCH_BASE = app.config['LDAP_SEARCH_BASE']
LDAP_TYPE = app.config['LDAP_TYPE']
LDAP_FILTER = app.config['LDAP_FILTER']
LDAP_USERNAMEFIELD = app.config['LDAP_USERNAMEFIELD']
else:
LDAP_TYPE = False
@ -155,7 +157,8 @@ class User(db.Model):
return False
if LDAP_TYPE == 'ldap':
searchFilter = "cn=%s" % self.username
searchFilter = "(&(%s=%s)%s)" % (LDAP_USERNAMEFIELD, self.username, LDAP_FILTER)
logging.info('Ldap searchFilter "%s"' % searchFilter)
else:
searchFilter = "(&(objectcategory=person)(samaccountname=%s))" % self.username
try:
@ -188,6 +191,7 @@ class User(db.Model):
# this might be changed in the future
self.firstname = result[0][0][1]['givenName'][0]
self.lastname = result[0][0][1]['sn'][0]
self.email = result[0][0][1]['mail'][0]
except:
self.firstname = self.username
self.lastname = ''
@ -214,7 +218,7 @@ class User(db.Model):
We will create a local user (in DB) in order to manage user
profile such as name, roles,...
"""
user = User(username=self.username, firstname=self.firstname, lastname=self.lastname, role_id=self.role_id)
user = User(username=self.username, firstname=self.firstname, lastname=self.lastname, role_id=self.role_id, email=self.email)
db.session.add(user)
db.session.commit()
# assgine user_id to current_user after create in the DB
@ -953,3 +957,22 @@ class Setting(db.Model):
db.session.rollback()
return False
def toggle(self, setting):
setting = str(setting)
current_setting = Setting.query.filter(Setting.name==setting).first()
try:
if current_setting:
if current_setting.value == "True":
current_setting.value = "False"
else:
current_setting.value = "True"
db.session.commit()
return True
else:
logging.error('Setting %s does not exist' % setting)
return False
except:
logging.error('Cannot toggle setting %s' % setting)
logging.debug(traceback.format_exec())
db.session.rollback()
return False

View File

@ -121,7 +121,6 @@ function getdnssec(url){
if (data['status'] == 'error'){
modal.find('.modal-body p').text(data['msg']);
}
else {
dnssec_msg = '';
@ -144,8 +143,7 @@ function getdnssec(url){
dnssec_msg += '</form>';
}
}
modal.find('.modal-body p').replaceWith(dnssec_msg);
modal.find('.modal-body p').html(dnssec_msg);
}
modal.modal('show');
});

View File

@ -37,7 +37,7 @@
<tr class="odd gradeX">
<td><a
href="https://google.com/search?q=site:doc.powerdns.com+{{ statistic['name'] }}"
target="_blank" class="btn btn-xs blue"><i
target="_blank" class="btn btn-flat btn-xs blue"><i
class="fa fa-search"></i></a></td>
<td>{{ statistic['name'] }}</td>
<td>{{ statistic['value'] }}</td>
@ -73,7 +73,7 @@
<tr class="odd gradeX">
<td><a
href="https://google.com/search?q=site:doc.powerdns.com+{{ config['name'] }}"
target="_blank" class="btn btn-xs blue"><i
target="_blank" class="btn btn-flat btn-xs blue"><i
class="fa fa-search"></i></a></td>
<td>{{ config['name'] }}</td>
<td>

View File

@ -96,9 +96,9 @@
<p>Are you sure you want to remove all history?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left"
<button type="button" class="btn btn-flat btn-default pull-left"
data-dismiss="modal">Close</button>
<button type="button" class="btn btn-danger" onclick="applyChanges('', '/admin/history');location.reload();">Clear
<button type="button" class="btn btn-flat btn-danger" onclick="applyChanges('', '/admin/history');location.reload();">Clear
History</button>
</div>
</div>
@ -120,7 +120,7 @@
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-right"
<button type="button" class="btn btn-flat btn-default pull-right"
data-dismiss="modal">Close</button>
</div>
</div>

View File

@ -141,9 +141,9 @@
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left"
<button type="button" class="btn btn-flat btn-default pull-left"
data-dismiss="modal">Close</button>
<button type="button" class="btn btn-danger" id="button_revoke_confirm">Revoke</button>
<button type="button" class="btn btn-flat btn-danger" id="button_revoke_confirm">Revoke</button>
</div>
</div>
<!-- /.modal-content -->
@ -164,9 +164,9 @@
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left"
<button type="button" class="btn btn-flat btn-default pull-left"
data-dismiss="modal">Close</button>
<button type="button" class="btn btn-danger" id="button_delete_confirm">Delete</button>
<button type="button" class="btn btn-flat btn-danger" id="button_delete_confirm">Delete</button>
</div>
</div>
<!-- /.modal-content -->

View File

@ -0,0 +1,137 @@
{% extends "base.html" %} {% block title %}
<title>DNS Control Panel - Settings</title>
{% endblock %} {% block dashboard_stat %}
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
Settings <small>PowerDNS-Admin settings</small>
</h1>
<ol class="breadcrumb">
<li><a href="{{ url_for('dashboard') }}"><i
class="fa fa-dashboard"></i> Home</a></li>
<li class="active">Settings</li>
</ol>
</section>
{% endblock %} {% block content %}
<section class="content">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<h3 class="box-title">Settings Management</h3>
</div>
<div class="box-body">
<table id="tbl_settings" class="table table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Value</th>
<th>Change</th>
</tr>
</thead>
<tbody>
{% for setting in settings %}
<tr class="odd ">
<td>{{ setting.name }}</td>
<td>{{ setting.value }}</td>
<td width="6%">
{% if setting.value == "True" or setting.value == "False" %}
<button type="button" class="btn btn-flat btn-warning setting-toggle-button" id="{{ setting.name }}">
Toggle&nbsp;<i class="fa fa-info"></i>
</button>
{% else %}
<button type="button" class="btn btn-flat btn-warning setting-edit-button" id="{{ setting.name }}">
Edit&nbsp;<i class="fa fa-info"></i>
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</section>
{% endblock %}
{% block extrascripts %}
<script>
// set up history data table
$("#tbl_settings").DataTable({
"paging" : true,
"lengthChange" : false,
"searching" : true,
"ordering" : true,
"info" : true,
"autoWidth" : false
});
$(".setting-toggle-button").click(function() {
var setting = $(this).prop('id');
applyChanges('','/admin/setting/' + setting + '/toggle')
location.reload();
});
// TODO: allow editing of value field
$(".setting-edit-button").click(function() {
var setting = $(this).prop('id');
applyChanges('','/admin/setting/' + setting + '/edit')
location.reload();
});
</script>
{% endblock %}
{% block modals %}
<!-- Clear History Confirmation Box -->
<div class="modal fade modal-warning" id="modal_clear_history">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Confirmation</h4>
</div>
<div class="modal-body">
<p>Are you sure you want to remove all history?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-flat btn-default pull-left"
data-dismiss="modal">Close</button>
<button type="button" class="btn btn-flat btn-danger" onclick="applyChanges('', '/admin/history');location.reload();">Clear
History</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<div class="modal fade" id="modal_history_info">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">History Details</h4>
</div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-flat btn-default pull-right"
data-dismiss="modal">Close</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.modal -->
{% endblock %}

View File

@ -38,7 +38,7 @@
<![endif]-->
{% endblock %}
</head>
<body class="hold-transition skin-blue sidebar-mini">
<body class="hold-transition skin-blue sidebar-mini {% if not fullscreen_layout_setting %}layout-boxed{% endif %}">
<div class="wrapper">
{% block pageheader %}
<header class="main-header">
@ -86,10 +86,10 @@
<!-- Menu Footer-->
<li class="user-footer">
<div class="pull-left">
<a href="#" class="btn btn-default btn-flat">Change password</a>
<a href="{{ url_for('user_profile') }}" class="btn btn-flat btn-default">My Profile</a>
</div>
<div class="pull-right">
<a href="{{ url_for('logout') }}" class="btn btn-default btn-flat">Log out</a>
<a href="{{ url_for('logout') }}" class="btn btn-flat btn-default">Log out</a>
</div>
</li>
</ul>
@ -125,9 +125,10 @@
{% if current_user.role.name == 'Administrator' %}
<li><a href="{{ url_for('domain_add') }}"><i class="fa fa-plus"></i> <span>New Domain</span></a></li>
<li class="header">ADMINISTRATION</li>
<li><a href="{{ url_for('admin') }}"><i class="fa fa-circle-o"></i> <span>Admin Console</span></a></li>
<li><a href="{{ url_for('admin_manageuser') }}"><i class="fa fa-circle-o"></i> <span>User</span></a></li>
<li><a href="{{ url_for('admin_history') }}"><i class="fa fa-circle-o"></i> <span>History</span></a></li>
<li><a href="{{ url_for('admin') }}"><i class="fa fa-wrench"></i> <span>Admin Console</span></a></li>
<li><a href="{{ url_for('admin_manageuser') }}"><i class="fa fa-users"></i> <span>Users</span></a></li>
<li><a href="{{ url_for('admin_history') }}"><i class="fa fa-calendar"></i> <span>History</span></a></li>
<li><a href="{{ url_for('admin_settings') }}"><i class="fa fa-cog"></i> <span>Settings</span></a></li>
{% endif %}
</section>
<!-- /.sidebar -->
@ -301,7 +302,7 @@
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-right"
<button type="button" class="btn btn-flat btn-default pull-right"
data-dismiss="modal">Close</button>
</div>
</div>
@ -324,7 +325,7 @@
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-right"
<button type="button" class="btn btn-flat btn-default pull-right"
data-dismiss="modal">Close</button>
</div>
</div>

View File

@ -27,7 +27,7 @@
</div>
<div class="box-body">
<div class="row">
<div class="col-lg-6 col-xs-12">
<div class="col-lg-6">
<!-- small box -->
<div class="small-box bg-aqua">
<div class="inner">
@ -39,8 +39,8 @@
</div>
</div>
</div>
<div class="col-lg-6 col-xs-12">
<!-- small box -->
<div class="col-lg-6">
<a href="{{ url_for('admin_manageuser') }}">
<div class="small-box bg-green">
<div class="inner">
<h3>{{ users|length }}</h3>
@ -50,11 +50,12 @@
<i class="fa fa-users"></i>
</div>
</div>
</a>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-xs-6">
<!-- small box -->
<div class="col-lg-6">
<a href="{{ url_for('admin_history') }}">
<div class="small-box bg-green">
<div class="inner">
<h3>{{ history_number }}</h3>
@ -64,9 +65,10 @@
<i class="fa fa-calendar"></i>
</div>
</div>
</a>
</div>
<div class="col-lg-6 col-xs-6">
<!-- small box -->
<div class="col-lg-6">
<a href="{{ url_for('admin') }}">
<div class="small-box bg-green">
<div class="inner">
<h3><span style="font-size: 18px">{{ uptime|display_second_to_time }}</span></h3>
@ -76,6 +78,7 @@
<i class="fa fa-clock-o"></i>
</div>
</div>
</a>
</div>
</div>
</div>
@ -177,7 +180,7 @@
</button>
</td>
{% else %}
<td width="18%">
<td width="20%">
<button type="button" class="btn btn-flat btn-success" onclick="window.location.href='{{ url_for('domain', domain_name=domain.name) }}'">
Manage&nbsp;<i class="fa fa-cog"></i>
</button>
@ -240,7 +243,7 @@
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-right"
<button type="button" class="btn btn-flat btn-default pull-right"
data-dismiss="modal">Close</button>
</div>
</div>
@ -263,7 +266,7 @@
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-right"
<button type="button" class="btn btn-flat btn-default pull-right"
data-dismiss="modal">Close</button>
</div>
</div>

View File

@ -24,12 +24,18 @@
<h3 class="box-title">Manage Records for {{ domain.name }}</h3>
</div>
<div class="box-body">
{% if domain.type != 'Slave' %}
<button type="button" class="btn btn-flat btn-primary pull-left button_add_record" id="{{ domain.name }}">
Add Record&nbsp;<i class="fa fa-plus"></i>
</button>
<button type="button" class="btn btn-flat btn-primary pull-right button_apply_changes" id="{{ domain.name }}">
Apply Changes&nbsp;<i class="fa fa-floppy-o"></i>
</button>
{% else %}
<button type="button" class="btn btn-flat btn-primary pull-left button_update_from_master" id="{{ domain.name }}">
Update from Master&nbsp;<i class="fa fa-download"></i>
</button>
{% endif %}
</div>
<div class="box-body">
<table id="tbl_records" class="table table-bordered table-striped">
@ -46,7 +52,7 @@
</thead>
<tbody>
{% for record in records %}
<tr class="odd">
<tr class="odd row_record" id="{{ domain.name }}">
<td>
{{ (record.name,domain.name)|display_record_name }}
</td>
@ -69,8 +75,9 @@
Edit&nbsp;<i class="fa fa-edit"></i>
</button>
{% else %}
<!-- TODO: replace this link for slave records -->
<a href="#" class="btn default btn-xs purple"> <i class="fa fa-exclamation-circle"></i></a>
<button type="button" class="btn btn-flat btn-warning"">
&nbsp;&nbsp;<i class="fa fa-exclamation-circle"></i>&nbsp;&nbsp;
</button>
{% endif %}
</td>
<td width="6%">
@ -79,16 +86,20 @@
Delete&nbsp;<i class="fa fa-trash"></i>
</button>
{% else %}
<!-- TODO: replace this link for slave records -->
<a href="#" class="btn default btn-xs red"> <i class="fa fa-exclamation-circle"></i></a>
<button type="button" class="btn btn-flat btn-warning"">
&nbsp;&nbsp;<i class="fa fa-exclamation-circle"></i>&nbsp;&nbsp;
</button>
{% endif %}
{% else %}
<!-- TODO: replace these links for slave records -->
<td width="6%">
<a href="#" class="btn default btn-xs purple"> <i class="fa fa-exclamation-circle"></i></a>
<button type="button" class="btn btn-flat btn-warning"">
&nbsp;&nbsp;<i class="fa fa-exclamation-circle"></i>&nbsp;&nbsp;
</button>
</td>
<td width="6%">
<a href="#" class="btn default btn-xs purple"> <i class="fa fa-exclamation-circle"></i></a>
<button type="button" class="btn btn-flat btn-warning"">
&nbsp;&nbsp;<i class="fa fa-exclamation-circle"></i>&nbsp;&nbsp;
</button>
</td>
{% endif %}
</td>
@ -131,7 +142,8 @@
});
// handle delete button
$(document).on("click", ".button_delete", function() {
$(document).on("click", ".button_delete", function(e) {
e.stopPropagation();
var modal = $("#modal_delete");
var table = $("#tbl_records").DataTable();
var record = $(this).prop('id');
@ -145,11 +157,18 @@
});
// handle edit button
$(document).on("click", ".button_edit", function() {
var nRow = $(this).parents('tr')[0];
$(document).on("click", ".button_edit, .row_record", function(e) {
e.stopPropagation();
if ($(this).is('tr')) {
var nRow = $(this)[0];
} else {
var nRow = $(this).parents('tr')[0];
}
var table = $("#tbl_records").DataTable();
if (nEditing !== null && nEditing != nRow && nNew == false) {
if (nEditing == nRow) {
/* click on row already being edited, do nothing */
} else if (nEditing !== null && nEditing != nRow && nNew == false) {
/* Currently editing - but not this row - restore the old before continuing to edit mode */
restoreRow(table, nEditing);
editRow(table, nRow);
@ -201,6 +220,7 @@
//handle cancel button
$(document).on("click", ".button_cancel", function (e) {
e.stopPropagation();
var oTable = $("#tbl_records").DataTable();
if (nNew) {
oTable.row(nEditing).remove().draw();
@ -214,12 +234,18 @@
//handle save button
$(document).on("click", ".button_save", function (e) {
e.stopPropagation();
var table = $("#tbl_records").DataTable();
saveRow(table, nEditing);
nEditing = null;
nNew = false;
});
//handle update_from_master button
$(document).on("click", ".button_update_from_master", function (e) {
var domain = $(this).prop('id');
applyChanges({'domain': domain}, '/domain/' + domain + '/update');
});
</script>
{% endblock %}
{% block modals %}
@ -237,9 +263,9 @@
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left"
<button type="button" class="btn btn-flat btn-default pull-left"
data-dismiss="modal">Close</button>
<button type="button" class="btn btn-danger" id="button_delete_confirm">Delete</button>
<button type="button" class="btn btn-flat btn-danger" id="button_delete_confirm">Delete</button>
</div>
</div>
<!-- /.modal-content -->

View File

@ -92,8 +92,8 @@
<!-- /.box-body -->
<div class="box-footer">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="submit" class="btn btn-default" onclick="window.location.href='{{ url_for('dashboard') }}'">Cancel</button>
<button type="submit" class="btn btn-flat btn-primary">Submit</button>
<button type="submit" class="btn btn-flat btn-default" onclick="window.location.href='{{ url_for('dashboard') }}'">Cancel</button>
</div>
</form>
</div>
@ -129,26 +129,27 @@
(OFF) - Not set
</li>
<li>
INCEPTION-INCREMENT -
INCEPTION-INCREMENT - Uses YYYYMMDDSS format for SOA serial numbers. If the SOA serial from the backend is within two days after inception, it gets incremented by two (the backend should keep SS below 98).
</li>
<li>
INCEPTION -
INCEPTION - Sets the SOA serial to the last inception time in YYYYMMDD01 format. Uses localtime to find the day for inception time. <strong>Not recomended.</strong>
</li>
<li>
INCREMENT-WEEK -
INCREMENT-WEEK - Sets the SOA serial to the number of weeks since the epoch, which is the last inception time in weeks. <strong>Not recomended.</strong>
</li>
<li>
INCREMENT-WEEKS -
INCREMENT-WEEKS - Increments the serial with the number of weeks since the UNIX epoch. This should work in every setup; but the result won't look like YYYYMMDDSS anymore.
</li>
<li>
EPOCH -
EPOCH - Sets the SOA serial to the number of seconds since the epoch.
</li>
<li>
INCEPTION-EPOCH -
INCEPTION-EPOCH - Sets the new SOA serial number to the maximum of the old SOA serial number, and age in seconds of the last inception.
</li>
</ul>
</dd>
</dl>
<p>Find more details at <a href="https://docs.powerdns.com/md/">https://docs.powerdns.com/md/</a></p>
</div>
</div>
</div>

View File

@ -46,7 +46,7 @@
<div class="box-body">
<div class="col-xs-offset-2">
<div class="form-group">
<button type="submit" class="btn btn-primary"><i class="fa fa-check"></i> Save</button>
<button type="submit" class="btn btn-flat btn-primary"><i class="fa fa-check"></i> Save</button>
</div>
</div>
</div>
@ -107,9 +107,9 @@ $('.delete_domain').click(function() {
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left"
<button type="button" class="btn btn-flat btn-default pull-left"
data-dismiss="modal">Close</button>
<button type="button" class="btn btn-danger" id="button_delete_confirm">
<button type="button" class="btn btn-flat btn-danger" id="button_delete_confirm">
Delete</button>
</div>
</div>

View File

@ -27,7 +27,7 @@
<body class="hold-transition login-page">
<div class="login-box">
<div class="login-logo">
<a href="{{ url_for('index') }}"><b>PowerDNS</b>-Admin</a>
<a href="{{ url_for('index') }}">Sign In {{ login_title }}</a>
</div>
<!-- /.login-logo -->
<div class="login-box-body">
@ -56,18 +56,26 @@
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
{% if ldap_enabled %}
{% if ldap_enabled and basic_enabled %}
<div class="form-group">
<select class="form-control" name="auth_method">
<option value="LOCAL">Local Authentication</option>
<option value="LOCAL">LOCAL Authentication</option>
<option value="LDAP">LDAP Authentication</option>
</select>
</div>
{% else %}
<div class="form-group">
<input type="hidden" name="auth_method" value="LOCAL">
{% elif ldap_enabled and not basic_enabled %}
<div class="form-group">
<input type="hidden" name="auth_method" value="LDAP">
</div>
{% endif %}
{% elif basic_enabled and not ldap_enabled %}
<div class="form-group">
<input type="hidden" name="auth_method" value="LOCAL">
</div>
{% else %}
<div class="form-group">
<input type="hidden" name="auth_method" value="LOCAL">
</div>
{% endif %}
<div class="row">
<div class="col-xs-8">
@ -79,7 +87,7 @@
</div>
<!-- /.col -->
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat">Sign In</button>
<button type="submit" class="btn btn-flat btn-primary btn-block">Sign In</button>
</div>
<!-- /.col -->
</div>

View File

@ -72,11 +72,11 @@
</div>
<div class="row">
<div class="col-xs-4 pull-left">
<button type="button" class="btn btn-block btn-flat"
<button type="button" class="btn btn-flat btn-block"
id="button_back">Back</button>
</div>
<div class="col-xs-4 pull-right">
<button type="submit" class="btn btn-primary btn-block btn-flat">Register</button>
<button type="submit" class="btn btn-flat btn-primary btn-block">Register</button>
</div>
<!-- /.col -->
</div>

View File

@ -1,177 +1,120 @@
{% extends "base.html" %}
{% block head %}
{{ super() }}
<!-- BEGIN PAGE LEVEL STYLES -->
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='global/plugins/jquery-multi-select/css/multi-select.css') }}"/>
<!-- BEGIN THEME STYLES -->
<!-- BEGIN THEME STYLES -->
<!-- DOC: To use 'rounded corners' style just load 'components-rounded.css' stylesheet instead of 'components.css' in the below style tag -->
<link href="{{ url_for('static', filename='global/css/components-md.css') }}" id="style_components" rel="stylesheet" type="text/css"/>
<link href="{{ url_for('static', filename='global/css/plugins-md.css') }}" rel="stylesheet" type="text/css"/>
<link href="{{ url_for('static', filename='admin/layout2/css/layout.css') }}" rel="stylesheet" type="text/css"/>
<link href="{{ url_for('static', filename='admin/layout2/css/themes/grey.css') }}" rel="stylesheet" type="text/css" id="style_color"/>
<link href="{{ url_for('static', filename='admin/layout2/css/custom.css') }}" rel="stylesheet" type="text/css"/>
<!-- END THEME STYLES -->
{% endblock %}
{% block title %}<title>DNS Control Panel - User Profile</title>{% endblock %}
{% block title %}<title>DNS Control Panel - My Profile</title>{% endblock %}
{% block dashboard_stat %}
<!-- BEGIN PAGE HEADER-->
<h3 class="page-title">
User Profile</h3>
<div class="page-bar">
<ul class="page-breadcrumb">
<li>
<i class="fa fa-home"></i>
<a href="{{ url_for('dashboard') }}">Home</a>
<i class="fa fa-angle-right"></i>
</li>
<li>
<a href="#">User Profile</a>
</li>
</ul>
</div>
<!-- END PAGE HEADER-->
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
Profile
<small>Edit my profile</small>
</h1>
<ol class="breadcrumb">
<li><a href="{{ url_for('dashboard') }}"><i class="fa fa-dashboard"></i>Home</a></li>
<li class="active">My Profile</li>
</ol>
</section>
{% endblock %}
{% block content %}
<div class="clearfix">
</div>
<div class="row">
<div class="col-md-12">
<!-- BEGIN PROFILE CONTENT -->
<div class="profile-content">
<div class="row">
<div class="col-md-12">
<div class="portlet light">
<div class="portlet-title tabbable-line">
<div class="caption caption-md">
<i class="icon-globe theme-font hide"></i>
<span class="caption-subject font-blue-madison bold uppercase">Profile Account</span>
</div>
<ul class="nav nav-tabs">
<li class="active">
<a href="#tab_1_1" data-toggle="tab">Personal Info</a>
</li>
<li>
<a href="#tab_1_2" data-toggle="tab">Change Avatar</a>
</li>
<li>
<a href="#tab_1_3" data-toggle="tab">Change Password</a>
</li>
</ul>
</div>
<div class="portlet-body">
<div class="tab-content">
<!-- PERSONAL INFO TAB -->
<div class="tab-pane active" id="tab_1_1">
<form role="form" action="{{ user_profile }}" method="POST">
<div class="form-group">
<label class="control-label">First Name</label>
<input name="firstname" type="text" placeholder="{{ current_user.firstname }}" class="form-control"/>
</div>
<div class="form-group">
<label class="control-label">Last Name</label>
<input name="lastname" type="text" placeholder="{{ current_user.lastname }}" class="form-control"/>
</div>
<div class="form-group">
<label class="control-label">Email</label>
<input name="email" type="text" placeholder="{{ current_user.email }}" class="form-control"/>
</div>
<div class="margiv-top-10">
<button type="submit" class="btn green-haze"> Save Changes</button>
<a href="javascript:;" class="btn default">
Cancel </a>
</div>
</form>
</div>
<!-- END PERSONAL INFO TAB -->
<!-- CHANGE AVATAR TAB -->
<div class="tab-pane" id="tab_1_2">
<form action="{{ user_profile }}" method="POST" enctype="multipart/form-data">
<div class="form-group">
<div class="fileinput fileinput-new" data-provides="fileinput">
<div class="fileinput-new thumbnail" style="width: 200px; height: 210px;">
{% if current_user.avatar %}
<img src="{{ url_for('user_avatar', filename=current_user.avatar) }}" alt=""/ style="width:200px;height:200px;">
{% else %}
<img src="http://www.placehold.it/200x200/EFEFEF/AAAAAA&amp;text=no+image" alt=""/>
{% endif %}
</div>
<div>
<span class="btn default btn-file">
<span class="fileinput-new">
Select image </span>
<span class="fileinput-exists">
Change </span>
<input type="file" name="file">
</span>
</div>
</div>
<div class="clearfix margin-top-10">
<span class="label label-danger">NOTE! </span>
<span>&nbsp;Only support <strong>.PNG, .JPG, .JPEG</strong>. Best size is <strong>200x200</strong>. </span>
</div>
</div>
<div class="margin-top-10">
<button type="submit" class="btn green-haze"> Submit</button>
</div>
</form>
</div>
<!-- END CHANGE AVATAR TAB -->
<!-- CHANGE PASSWORD TAB -->
<div class="tab-pane" id="tab_1_3">
{% if not current_user.password %}
Your account password is managed via LDAP which isn't supported to change here.
{% else %}
<form class="password-form" action="{{ user_profile }}" method="POST">
<div class="form-group">
<label class="control-label">New Password</label>
<input name="password" id="newpassword" type="password" class="form-control"/>
</div>
<div class="form-group">
<label class="control-label">Re-type New Password</label>
<input name="rpassword" type="password" class="form-control"/>
</div>
<div class="margin-top-10">
<button type="submit" class="btn green-haze"> Change Password</button>
</div>
</form>
{% endif %}
</div>
<!-- END CHANGE PASSWORD TAB -->
</div>
</div>
</div>
</div>
</div>
</div>
<!-- END PROFILE CONTENT -->
</div>
</div>
<section class="content">
<div class="row">
<div class="col-lg-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Edit my profile</h3>
</div>
<div class="box-body">
<!-- Custom Tabs -->
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<li class="active"><a href="#personal_tab" data-toggle="tab">Personal
Info</a></li>
<li><a href="#avatar_tab" data-toggle="tab">Change
Avatar</a></li>
<li><a href="#password_tab" data-toggle="tab">Change
Password</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="personal_tab">
<form role="form" method="post" action="{{ user_profile }}">
<div class="form-group">
<label for="firstname">First Name</label> <input type="text"
class="form-control" name="firstname" id="firstname"
placeholder="{{ current_user.firstname }}">
</div>
<div class="form-group">
<label for="lastname">Last Name</label> <input type="text"
class="form-control" name="lastname" id="lastname"
placeholder="{{ current_user.lastname }}">
</div>
<div class="form-group">
<label for="email">E-mail</label> <input type="text"
class="form-control" name="email" id="email"
placeholder="{{ current_user.email }}">
</div>
<div class="form-group">
<button type="submit" class="btn btn-flat btn-primary">Submit</button>
</div>
</form>
</div>
<div class="tab-pane" id="avatar_tab">
<form action="{{ user_profile }}" method="post"
enctype="multipart/form-data">
<div class="form-group">
<div class="form-group">
<div class="thumbnail" style="width: 200px; height: 210px;">
{% if current_user.avatar %} <img
src="{{ url_for('user_avatar', filename=current_user.avatar) }}"
alt="" / style="width: 200px; height: 200px;"> {%
else %} <img
src="http://www.placehold.it/200x200/EFEFEF/AAAAAA&amp;text=no+image"
alt="" /> {% endif %}
</div>
<div>
<label for="file">Select image</label> <input type="file"
id="file" name="file">
</div>
</div>
<div>
<span class="label label-danger">NOTE! </span> <span>&nbsp;Only
supports <strong>.PNG, .JPG, .JPEG</strong>. The best size
to use is <strong>200x200</strong>.
</span>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-flat btn-primary">Submit</button>
</div>
</form>
</div>
<div class="tab-pane" id="password_tab">
{% if not current_user.password %} Your account password is
managed via LDAP which isn't supported to change here. {% else
%}
<form action="{{ user_profile }}" method="post">
<div class="form-group">
<label for="password">New Password</label> <input
type="password" class="form-control" name="password"
id="newpassword" />
</div>
<div class="form-group">
<label for="rpassword">Re-type New Password</label> <input
type="password" class="form-control" name="rpassword"
id="rpassword" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-flat btn-primary">Change
password</button>
</div>
</form>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block scripts %}
{{ super() }}
<!-- BEGIN PAGE LEVEL PLUGINS -->
<script type="text/javascript" src="{{ url_for('static', filename='global/plugins/bootstrap-select/bootstrap-select.min.js') }}"></script>
<script src="{{ url_for('static', filename='global/plugins/jquery-validation/js/jquery.validate.min.js') }}" type="text/javascript"></script>
<!-- END PAGE LEVEL PLUGINS -->
<!-- BEGIN PAGE LEVEL SCRIPTS -->
<script src="{{ url_for('static', filename='global/scripts/metronic.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='admin/layout2/scripts/layout.js') }}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='admin/pages/scripts/user_profile.js') }}" type="text/javascript"></script>
<!-- END PAGE LEVEL SCRIPTS -->
<script>
jQuery(document).ready(function() {
Metronic.init(); // init metronic core componets
Layout.init(); // init layout
UserProfile.init();
});
</script>
<!-- END JAVASCRIPTS -->
{% block extrascripts %}
<!-- TODO: add password and password confirmation comparisson check -->
{% endblock %}

View File

@ -11,11 +11,17 @@ from werkzeug import secure_filename
from lib import utils
from app import app, login_manager
from .models import User, Role, Domain, DomainUser, Record, Server, History, Anonymous, Setting
from distutils.util import strtobool
jinja2.filters.FILTERS['display_record_name'] = utils.display_record_name
jinja2.filters.FILTERS['display_master_name'] = utils.display_master_name
jinja2.filters.FILTERS['display_second_to_time'] = utils.display_time
@app.context_processor
def inject_fullscreen_layout_setting():
fullscreen_layout_setting = Setting.query.filter(Setting.name == 'fullscreen_layout').first()
return dict(fullscreen_layout_setting=strtobool(fullscreen_layout_setting.value))
# START USER AUTHENTICATION HANDLER
@app.before_request
def before_request():
@ -63,13 +69,17 @@ def register():
@app.route('/login', methods=['GET', 'POST'])
@login_manager.unauthorized_handler
def login():
# these parameters will be needed in multiple paths
LDAP_ENABLED = True if 'LDAP_TYPE' in app.config.keys() else False
LOGIN_TITLE = app.config['LOGIN_TITLE'] if 'LOGIN_TITLE' in app.config.keys() else ''
BASIC_ENABLED = app.config['BASIC_ENABLED']
SIGNUP_ENABLED = app.config['SIGNUP_ENABLED']
if g.user is not None and current_user.is_authenticated:
return redirect(url_for('dashboard'))
if request.method == 'GET':
LDAP_ENABLED = True if 'LDAP_TYPE' in app.config.keys() else False
return render_template('login.html', ldap_enabled=LDAP_ENABLED)
return render_template('login.html', ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
# process login
username = request.form['username']
@ -93,10 +103,10 @@ def login():
try:
auth = user.is_validate(method=auth_method)
if auth == False:
return render_template('login.html', error='Invalid credentials')
return render_template('login.html', error='Invalid credentials', ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
except Exception, e:
error = e.message['desc'] if 'desc' in e.message else e
return render_template('login.html', error=error)
return render_template('login.html', error=error, ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
login_user(user, remember = remember_me)
return redirect(request.args.get('next') or url_for('index'))
@ -113,7 +123,7 @@ def login():
try:
result = user.create_local_user()
if result == True:
return render_template('login.html', username=username, password=password)
return render_template('login.html', username=username, password=password, ldap_enabled=LDAP_ENABLED, login_title=LOGIN_TITLE, basic_enabled=BASIC_ENABLED, signup_enabled=SIGNUP_ENABLED)
else:
return render_template('register.html', error=result)
except Exception, e:
@ -434,6 +444,23 @@ def admin_history():
histories = History.query.all()
return render_template('admin_history.html', histories=histories)
@app.route('/admin/settings', methods=['GET'])
@login_required
@admin_role_required
def admin_settings():
if request.method == 'GET':
settings = Setting.query.filter(Setting.name != 'maintenance')
return render_template('admin_settings.html', settings=settings)
@app.route('/admin/setting/<string:setting>/toggle', methods=['POST'])
@login_required
@admin_role_required
def admin_settings_toggle(setting):
result = Setting().toggle(setting)
if (result):
return make_response(jsonify( { 'status': 'ok', 'msg': 'Toggled setting successfully.' } ), 200)
else:
return make_response(jsonify( { 'status': 'error', 'msg': 'Can toggle setting.' } ), 500)
@app.route('/user/profile', methods=['GET', 'POST'])
@login_required

View File

@ -6,6 +6,7 @@ WTF_CSRF_ENABLED = True
SECRET_KEY = 'We are the world'
BIND_ADDRESS = '127.0.0.1'
PORT = 9393
LOGIN_TITLE = "PDNS"
# TIMEOUT - for large zones
TIMEOUT = 10
@ -28,6 +29,13 @@ LDAP_URI = 'ldaps://your-ldap-server:636'
LDAP_USERNAME = 'cn=dnsuser,ou=users,ou=services,dc=duykhanh,dc=me'
LDAP_PASSWORD = 'dnsuser'
LDAP_SEARCH_BASE = 'ou=System Admins,ou=People,dc=duykhanh,dc=me'
# Additional options only if LDAP_TYPE=ldap
LDAP_USERNAMEFIELD = 'uid'
LDAP_FILTER = '(objectClass=inetorgperson)'
#Default Auth
BASIC_ENABLED = True
SIGNUP_ENABLED = True
# POWERDNS CONFIG
PDNS_STATS_URL = 'http://172.16.214.131:8081/'

View File

@ -10,9 +10,11 @@ db.create_all()
admin_role = Role('Administrator', 'Administrator')
user_role = Role('User', 'User')
maintenance_setting = Setting('maintenance', 'False')
fullscreen_layout_setting = Setting('fullscreen_layout', 'True')
db.session.add(admin_role)
db.session.add(user_role)
db.session.add(maintenance_setting)
db.session.add(fullscreen_layout_setting)
db.session.commit()
if not os.path.exists(SQLALCHEMY_MIGRATE_REPO):
api.create(SQLALCHEMY_MIGRATE_REPO, 'database repository')