From 9f400b05e6ae886584ae2fb7e92d61f4c1e0d68f Mon Sep 17 00:00:00 2001 From: Ivan Filippov Date: Fri, 13 May 2016 19:42:39 -0600 Subject: [PATCH 1/7] Add screnshots to show new UI. --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f7a15a5..d6cc49c 100644 --- a/README.md +++ b/README.md @@ -64,5 +64,9 @@ Run the application and enjoy! (flask)$ ./run.py ``` -### Screenshot -![Alt text](http://i.imgur.com/wA5qy2d.png) +### Screenshots +![login page](https://github.com/ngoduykhanh/PowerDNS-Admin/wiki/images/readme_screenshots/fullscreen-login.png?raw=true) +![dashboard](https://github.com/ngoduykhanh/PowerDNS-Admin/wiki/images/readme_screenshots/fullscreen-dashboard.png?raw=true) +![create domain page](https://github.com/ngoduykhanh/PowerDNS-Admin/wiki/images/readme_screenshots/fullscreen-domaincreate.png?raw=true) +![manage domain page](https://github.com/ngoduykhanh/PowerDNS-Admin/wiki/images/readme_screenshots/fullscreen-domainmanage.png?raw=true) + From 0c694816e2d95436e9a9ce70daaad06e9e4b6e6f Mon Sep 17 00:00:00 2001 From: Ivan Filippov Date: Sun, 15 May 2016 12:47:02 -0600 Subject: [PATCH 2/7] Added 'record_helper' setting. New option for reload after applyChanges This setting enabled a helper pop-up that assists with filling out MX and SRV records. This option is toggleable on the Settings page. --- app/static/custom/js/custom.js | 7 ++- app/templates/admin_settings.html | 6 +-- app/templates/domain.html | 90 +++++++++++++++++++++++++++++++ app/views.py | 7 ++- create_db.py | 2 + 5 files changed, 105 insertions(+), 7 deletions(-) diff --git a/app/static/custom/js/custom.js b/app/static/custom/js/custom.js index 71f77b7..a0b319d 100644 --- a/app/static/custom/js/custom.js +++ b/app/static/custom/js/custom.js @@ -1,4 +1,4 @@ -function applyChanges(data, url, showResult) { +function applyChanges(data, url, showResult, refreshPage) { var success = false; $.ajax({ type : "POST", @@ -14,6 +14,9 @@ function applyChanges(data, url, showResult) { modal.find('.modal-body p').text("Applied changes successfully"); modal.modal('show'); } + if (refreshPage) { + location.reload(true); + } }, error : function(jqXHR, status) { @@ -91,7 +94,7 @@ function editRow(oTable, nRow) { jqTds[1].innerHTML = ''; jqTds[2].innerHTML = ''; jqTds[3].innerHTML = ''; - jqTds[4].innerHTML = ''; + jqTds[4].innerHTML = ''; jqTds[5].innerHTML = ''; jqTds[6].innerHTML = ''; diff --git a/app/templates/admin_settings.html b/app/templates/admin_settings.html index 9d10b18..b2dffd3 100644 --- a/app/templates/admin_settings.html +++ b/app/templates/admin_settings.html @@ -73,15 +73,13 @@ $(".setting-toggle-button").click(function() { var setting = $(this).prop('id'); - applyChanges('','/admin/setting/' + setting + '/toggle') - location.reload(); + applyChanges('','/admin/setting/' + setting + '/toggle', false, true) }); // TODO: allow editing of value field $(".setting-edit-button").click(function() { var setting = $(this).prop('id'); - applyChanges('','/admin/setting/' + setting + '/edit') - location.reload(); + applyChanges('','/admin/setting/' + setting + '/edit', false, true) }); {% endblock %} diff --git a/app/templates/domain.html b/app/templates/domain.html index d9f7e8a..27c5472 100644 --- a/app/templates/domain.html +++ b/app/templates/domain.html @@ -246,6 +246,75 @@ var domain = $(this).prop('id'); applyChanges({'domain': domain}, '/domain/' + domain + '/update'); }); + + {% if record_helper_setting %} + //handle wacky record types + $(document).on("focus", "#current_edit_record_data", function (e) { + var record_type = $(this).parents("tr").find('#record_type').val(); + var record_data = $(this); + if (record_type == "MX") { + var modal = $("#modal_custom_record"); + if (record_data.val() == "") { + var form = " \ + \ + \ + \ + "; + } else { + var parts = record_data.val().split(" "); + var form = " \ + \ + \ + \ + "; + } + modal.find('.modal-body p').html(form); + modal.find('#button_save').click(function() { + mx_server = modal.find('#mx_server').val(); + mx_priority = modal.find('#mx_priority').val(); + data = mx_priority + " " + mx_server; + record_data.val(data); + modal.modal('hide'); + }) + modal.modal('show'); + } else if (record_type == "SRV") { + var modal = $("#modal_custom_record"); + if (record_data.val() == "") { + var form = " \ + \ + \ + \ + \ + \ + \ + \ + "; + } else { + var parts = record_data.val().split(" "); + var form = " \ + \ + \ + \ + \ + \ + \ + \ + "; + } + modal.find('.modal-body p').html(form); + modal.find('#button_save').click(function() { + srv_priority = modal.find('#srv_priority').val(); + srv_weight = modal.find('#srv_weight').val(); + srv_port = modal.find('#srv_port').val(); + srv_target = modal.find('#srv_target').val(); + data = srv_priority + " " + srv_weight + " " + srv_port + " " + srv_target; + record_data.val(data); + modal.modal('hide'); + }) + modal.modal('show'); + } + }); + {% endif %} {% endblock %} {% block modals %} @@ -295,4 +364,25 @@ + {% endblock %} diff --git a/app/views.py b/app/views.py index f269d40..577fc76 100644 --- a/app/views.py +++ b/app/views.py @@ -22,6 +22,11 @@ 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)) +@app.context_processor +def inject_record_helper_setting(): + record_helper_setting = Setting.query.filter(Setting.name == 'record_helper').first() + return dict(record_helper_setting=strtobool(record_helper_setting.value)) + # START USER AUTHENTICATION HANDLER @app.before_request def before_request(): @@ -460,7 +465,7 @@ def admin_settings_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) + return make_response(jsonify( { 'status': 'error', 'msg': 'Unable to toggle setting.' } ), 500) @app.route('/user/profile', methods=['GET', 'POST']) @login_required diff --git a/create_db.py b/create_db.py index 7cbce74..c634fe2 100755 --- a/create_db.py +++ b/create_db.py @@ -11,10 +11,12 @@ admin_role = Role('Administrator', 'Administrator') user_role = Role('User', 'User') maintenance_setting = Setting('maintenance', 'False') fullscreen_layout_setting = Setting('fullscreen_layout', 'True') +record_helper_setting = Setting('record_helper_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.add(record_helper_setting) db.session.commit() if not os.path.exists(SQLALCHEMY_MIGRATE_REPO): api.create(SQLALCHEMY_MIGRATE_REPO, 'database repository') From dc1efdc3a3f1918f9ae8c86c2cca9829b62dabbe Mon Sep 17 00:00:00 2001 From: Ivan Filippov Date: Sun, 15 May 2016 13:12:33 -0600 Subject: [PATCH 3/7] Reload dashboard only after domain deletion finished. Fixes #40. --- app/static/admin/pages/scripts/my-button-action.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/static/admin/pages/scripts/my-button-action.js b/app/static/admin/pages/scripts/my-button-action.js index 2c23ae7..1f2f8b7 100644 --- a/app/static/admin/pages/scripts/my-button-action.js +++ b/app/static/admin/pages/scripts/my-button-action.js @@ -29,8 +29,10 @@ var MyButtonAction = function () { bootbox.confirm("Are you sure you want to delete this domain?", function(result) { if (result == true){ var domain = document.getElementById('delete_domain').value; - $.get("/admin/domain/"+ domain +"/delete"); - window.location.href = '/'; + $.get("/admin/domain/"+ domain +"/delete").always(function() { + window.location.href = '/'; + }); + } }); }); From 2cc5ee77d739101c2a75b22b30dbdea4516b2a17 Mon Sep 17 00:00:00 2001 From: Ivan Filippov Date: Sun, 15 May 2016 12:51:08 -0600 Subject: [PATCH 4/7] Removed unneeded modals from settings template. --- app/templates/admin_settings.html | 50 ------------------------------- 1 file changed, 50 deletions(-) diff --git a/app/templates/admin_settings.html b/app/templates/admin_settings.html index b2dffd3..d8874f0 100644 --- a/app/templates/admin_settings.html +++ b/app/templates/admin_settings.html @@ -83,53 +83,3 @@ }); {% endblock %} -{% block modals %} - - - - -{% endblock %} From 7f8b69765ad5c640aeef4a5f3974061ff430c842 Mon Sep 17 00:00:00 2001 From: Ivan Filippov Date: Sun, 15 May 2016 14:29:15 -0600 Subject: [PATCH 5/7] Add 'create user' template and functionality. Fixes #39 --- app/templates/admin_createuser.html | 91 +++++++++++++++++++++++++++++ app/templates/admin_manageuser.html | 10 +++- app/views.py | 26 ++++++++- 3 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 app/templates/admin_createuser.html diff --git a/app/templates/admin_createuser.html b/app/templates/admin_createuser.html new file mode 100644 index 0000000..3abd689 --- /dev/null +++ b/app/templates/admin_createuser.html @@ -0,0 +1,91 @@ +{% extends "base.html" %} +{% block title %}DNS Control Panel - Create User{% endblock %} + +{% block dashboard_stat %} + +
+

+ User + Create new +

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

Create new user

+
+ + +
+
+
+ + +
+
+ + +
+
+ + + {% if duplicate_email %} + This e-mail address is already in use. + {% endif %} +
+ +
+ + + {% if duplicate_username %} + This username is already in use. + {% endif %} +
+
+ + + {% if blank_password %} + The password cannot be blank. + {% endif %} +
+
+ +
+
+
+
+
+
+

Help with creating a new user

+
+
+

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

+

Newly created users do not have access to any domains. You will need to grant access to the user once it is created via the domain management buttons on the dashboard.

+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/app/templates/admin_manageuser.html b/app/templates/admin_manageuser.html index a50a695..ca60c41 100644 --- a/app/templates/admin_manageuser.html +++ b/app/templates/admin_manageuser.html @@ -19,6 +19,13 @@

User Management

+
@@ -97,9 +104,8 @@ modal.find('.modal-body p').text(info); modal.find('#button_delete_confirm').click(function() { var postdata = {'action': 'delete_user', 'data': username} - applyChanges(postdata, '/admin/manageuser'); + applyChanges(postdata, '/admin/manageuser', false, true); modal.modal('hide'); - location.reload(); }) modal.modal('show'); diff --git a/app/views.py b/app/views.py index 577fc76..8724f2b 100644 --- a/app/views.py +++ b/app/views.py @@ -373,13 +373,37 @@ def admin(): return render_template('admin.html', domains=domains, users=users, configs=configs, statistics=statistics, uptime=uptime, history_number=history_number) +@app.route('/admin/user/create', methods=['GET', 'POST']) +@login_required +@admin_role_required +def admin_createuser(): + if request.method == 'GET': + return render_template('admin_createuser.html') + + if request.method == 'POST': + fdata = request.form + + user = User(username=fdata['username'], plain_text_password=fdata['password'], firstname=fdata['firstname'], lastname=fdata['lastname'], email=fdata['email']) + + if fdata['password'] == "": + return render_template('admin_createuser.html', user=user, blank_password=True) + + result = user.create_local_user(); + + if result == 'Email already existed': + return render_template('admin_createuser.html', user=user, duplicate_email=True) + + if result == 'Username already existed': + return render_template('admin_createuser.html', user=user, duplicate_username=True) + + return redirect(url_for('admin_manageuser')) @app.route('/admin/manageuser', methods=['GET', 'POST']) @login_required @admin_role_required def admin_manageuser(): if request.method == 'GET': - users = User.query.all() + users = User.query.order_by(User.username).all() return render_template('admin_manageuser.html', users=users) if request.method == 'POST': From 3a79fe66c858b981b025dd973e224f5231fccf8c Mon Sep 17 00:00:00 2001 From: Ivan Filippov Date: Sun, 15 May 2016 16:01:57 -0600 Subject: [PATCH 6/7] Re-organized error templates. Enabled default error handling. --- app/templates/base.html | 124 ++++------------------------ app/templates/{ => errors}/400.html | 0 app/templates/{ => errors}/401.html | 0 app/templates/{ => errors}/404.html | 0 app/templates/{ => errors}/500.html | 0 app/views.py | 24 +++++- 6 files changed, 36 insertions(+), 112 deletions(-) rename app/templates/{ => errors}/400.html (100%) rename app/templates/{ => errors}/401.html (100%) rename app/templates/{ => errors}/404.html (100%) rename app/templates/{ => errors}/500.html (100%) diff --git a/app/templates/base.html b/app/templates/base.html index be0dfcc..a34f615 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -57,6 +57,7 @@ @@ -103,7 +105,7 @@ @@ -149,102 +151,8 @@ {% endblock %} - - {% block content %} -
- -
-
- -
-
-

150

-

Domains

-
-
- -
- More info -
-
- -
- -
-
-

53%

-

Users

-
-
- -
- More info -
-
- -
- -
-
-

44

-

Histories

-
-
- -
- More info -
-
- -
- -
-
-

65

-

Uptime

-
-
- -
- More info -
-
- -
- - -
- -
- - - -
- - -
- -
- -
- - -
{% endblock %} -
diff --git a/app/templates/400.html b/app/templates/errors/400.html similarity index 100% rename from app/templates/400.html rename to app/templates/errors/400.html diff --git a/app/templates/401.html b/app/templates/errors/401.html similarity index 100% rename from app/templates/401.html rename to app/templates/errors/401.html diff --git a/app/templates/404.html b/app/templates/errors/404.html similarity index 100% rename from app/templates/404.html rename to app/templates/errors/404.html diff --git a/app/templates/500.html b/app/templates/errors/500.html similarity index 100% rename from app/templates/500.html rename to app/templates/errors/500.html diff --git a/app/views.py b/app/views.py index 8724f2b..89adad5 100644 --- a/app/views.py +++ b/app/views.py @@ -59,13 +59,29 @@ def admin_role_required(f): # END CUSTOMIZE DECORATOR # START VIEWS +@app.errorhandler(400) +def http_bad_request(e): + return redirect(url_for('error', code=400)) + +@app.errorhandler(401) +def http_unauthorized(e): + return redirect(url_for('error', code=401)) + +@app.errorhandler(404) +def http_internal_server_error(e): + return redirect(url_for('error', code=404)) + +@app.errorhandler(500) +def http_page_not_found(e): + return redirect(url_for('error', code=500)) + @app.route('/error/') def error(code, msg=None): supported_code = ('400', '401', '404', '500') if code in supported_code: - return render_template('%s.html' % code, msg=msg), int(code) + return render_template('errors/%s.html' % code, msg=msg), int(code) else: - return render_template('404.html'), 404 + return render_template('errors/404.html'), 404 @app.route('/register', methods=['GET']) def register(): @@ -200,7 +216,7 @@ def domain_add(): soa_edit_api = request.form.getlist('radio_type_soa_edit_api')[0] if ' ' in domain_name or not domain_name or not domain_type: - return render_template('400.html', msg="Please correct your input"), 400 + return render_template('errors/400.html', msg="Please correct your input"), 400 if domain_type == 'slave': if request.form.getlist('domain_master_address'): @@ -216,7 +232,7 @@ def domain_add(): history.add() return redirect(url_for('dashboard')) else: - return render_template('400.html', msg=result['msg']), 400 + return render_template('errors/400.html', msg=result['msg']), 400 except: return redirect(url_for('error', code=500)) return render_template('domain_add.html') From 504b772fbab145c4a0634eab53bb7170468d3770 Mon Sep 17 00:00:00 2001 From: Ivan Filippov Date: Fri, 27 May 2016 00:03:59 -0600 Subject: [PATCH 7/7] Fix some spelling mistakes in README. Add supported version information. --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f7a15a5..fe8ba57 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,15 @@ PowerDNS Web-GUI - Built by Flask - Multiple domain management - Local / LDAP user authentication - User management -- User access management base on domain +- User access management based on domain - User activity logging - Dashboard and pdns service statistics ## Setup +### PowerDNS Version Support: +PowerDNS-Admin supports PowerDNS autoritative server versions **3.4.2** and higher but does **not** yet support PowerDNS 4.0.0 + ### pdns Service I assume that you have already installed powerdns service. Make sure that your `/etc/pdns/pdns.conf` has these contents ``` @@ -30,7 +33,7 @@ MariaDB [(none)]> GRANT ALL PRIVILEGES ON powerdnsadmin.* TO powerdnsadmin@'%' I ### PowerDNS-Admin -In this installation guide, I am using CentOS 7 and run my python stuffs with *virtualenv*. If you don't have it, let install: +In this installation guide, I am using CentOS 7 and run my python stuffs with *virtualenv*. If you don't have it, lets install it: ``` $ sudo yum install python-pip $ sudo pip install virtualenv @@ -55,7 +58,7 @@ Web application configuration is stored in `config.py` file. Let's clone it from Create database after having proper configs ``` -(flask)% ./createdb.py +(flask)% ./create_db.py ```