mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-11-09 23:20:27 +00:00
Adjustment in user_profile template. Add avatar uploading support
This commit is contained in:
parent
28882b57b1
commit
5d06195795
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,4 +25,5 @@ config.py
|
|||||||
logfile.log
|
logfile.log
|
||||||
|
|
||||||
db_repository/*
|
db_repository/*
|
||||||
|
upload/avatar/*
|
||||||
tmp/*
|
tmp/*
|
||||||
|
@ -35,9 +35,10 @@ class User(db.Model):
|
|||||||
firstname = db.Column(db.String(64))
|
firstname = db.Column(db.String(64))
|
||||||
lastname = db.Column(db.String(64))
|
lastname = db.Column(db.String(64))
|
||||||
email = db.Column(db.String(128))
|
email = db.Column(db.String(128))
|
||||||
|
avatar = db.Column(db.String(128))
|
||||||
role_id = db.Column(db.Integer, db.ForeignKey('role.id'))
|
role_id = db.Column(db.Integer, db.ForeignKey('role.id'))
|
||||||
|
|
||||||
def __init__(self, id=None, username=None, password=None, plain_text_password=None, firstname=None, lastname=None, role_id=None, email=None, reload_info=True):
|
def __init__(self, id=None, username=None, password=None, plain_text_password=None, firstname=None, lastname=None, role_id=None, email=None, avatar=None, reload_info=True):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
@ -46,6 +47,7 @@ class User(db.Model):
|
|||||||
self.lastname = lastname
|
self.lastname = lastname
|
||||||
self.role_id = role_id
|
self.role_id = role_id
|
||||||
self.email = email
|
self.email = email
|
||||||
|
self.avatar = avatar
|
||||||
|
|
||||||
if reload_info:
|
if reload_info:
|
||||||
user_info = self.get_user_info_by_id() if id else self.get_user_info_by_username()
|
user_info = self.get_user_info_by_id() if id else self.get_user_info_by_username()
|
||||||
@ -238,6 +240,8 @@ class User(db.Model):
|
|||||||
user.email = self.email
|
user.email = self.email
|
||||||
if self.plain_text_password:
|
if self.plain_text_password:
|
||||||
user.password = self.get_hashed_password(self.plain_text_password)
|
user.password = self.get_hashed_password(self.plain_text_password)
|
||||||
|
if self.avatar:
|
||||||
|
user.avatar = self.avatar
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
54
app/static/admin/pages/scripts/user_profile.js
Normal file
54
app/static/admin/pages/scripts/user_profile.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
var UserProfile = function() {
|
||||||
|
var handleUpdatePassword = function() {
|
||||||
|
$('.password-form').validate({
|
||||||
|
errorElement: 'span', //default input error message container
|
||||||
|
errorClass: 'help-block', // default input error message class
|
||||||
|
focusInvalid: false, // do not focus the last invalid input
|
||||||
|
ignore: "",
|
||||||
|
rules: {
|
||||||
|
password: {
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
rpassword: {
|
||||||
|
equalTo: "#newpassword"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
invalidHandler: function(event, validator) { //display error alert on form submit
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
highlight: function(element) { // hightlight error inputs
|
||||||
|
$(element)
|
||||||
|
.closest('.form-group').addClass('has-error'); // set error class to the control group
|
||||||
|
},
|
||||||
|
|
||||||
|
success: function(label) {
|
||||||
|
label.closest('.form-group').removeClass('has-error');
|
||||||
|
label.remove();
|
||||||
|
},
|
||||||
|
|
||||||
|
submitHandler: function(form) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.password-form input').keypress(function(e) {
|
||||||
|
if (e.which == 13) {
|
||||||
|
if ($('.password-form').validate().form()) {
|
||||||
|
$('.password-form').submit();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
//main function to initiate the module
|
||||||
|
init: function() {
|
||||||
|
handleUpdatePassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}();
|
@ -98,7 +98,11 @@
|
|||||||
<!-- DOC: Apply "dropdown-dark" class after below "dropdown-extended" to change the dropdown styte -->
|
<!-- DOC: Apply "dropdown-dark" class after below "dropdown-extended" to change the dropdown styte -->
|
||||||
<li class="dropdown dropdown-user">
|
<li class="dropdown dropdown-user">
|
||||||
<a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" data-close-others="true">
|
<a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" data-close-others="true">
|
||||||
|
{% if current_user.avatar %}
|
||||||
|
<img alt="" class="img-circle" src="{{ url_for('user_avatar', filename=current_user.avatar) }}"/>
|
||||||
|
{% else %}
|
||||||
<img alt="" class="img-circle" src="{{ url_for('static', filename='admin/layout2/img/avatar.png') }}"/>
|
<img alt="" class="img-circle" src="{{ url_for('static', filename='admin/layout2/img/avatar.png') }}"/>
|
||||||
|
{% endif %}
|
||||||
<span class="username username-hide-on-mobile">
|
<span class="username username-hide-on-mobile">
|
||||||
{% if current_user.is_authenticated() %} {{ current_user.firstname }} {% endif %}</span>
|
{% if current_user.is_authenticated() %} {{ current_user.firstname }} {% endif %}</span>
|
||||||
<i class="fa fa-angle-down"></i>
|
<i class="fa fa-angle-down"></i>
|
||||||
|
@ -91,13 +91,15 @@
|
|||||||
<!-- END PERSONAL INFO TAB -->
|
<!-- END PERSONAL INFO TAB -->
|
||||||
<!-- CHANGE AVATAR TAB -->
|
<!-- CHANGE AVATAR TAB -->
|
||||||
<div class="tab-pane" id="tab_1_2">
|
<div class="tab-pane" id="tab_1_2">
|
||||||
<form action="{{ user_profile }}" role="form">
|
<form action="{{ user_profile }}" method="POST" enctype="multipart/form-data">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="fileinput fileinput-new" data-provides="fileinput">
|
<div class="fileinput fileinput-new" data-provides="fileinput">
|
||||||
<div class="fileinput-new thumbnail" style="width: 200px; height: 150px;">
|
<div class="fileinput-new thumbnail" style="width: 200px; height: 210px;">
|
||||||
<img src="http://www.placehold.it/200x150/EFEFEF/AAAAAA&text=no+image" alt=""/>
|
{% if current_user.avatar %}
|
||||||
</div>
|
<img src="{{ url_for('user_avatar', filename=current_user.avatar) }}" alt=""/ style="width:200px;height:200px;">
|
||||||
<div class="fileinput-preview fileinput-exists thumbnail" style="max-width: 200px; max-height: 150px;">
|
{% else %}
|
||||||
|
<img src="http://www.placehold.it/200x200/EFEFEF/AAAAAA&text=no+image" alt=""/>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="btn default btn-file">
|
<span class="btn default btn-file">
|
||||||
@ -105,7 +107,7 @@
|
|||||||
Select image </span>
|
Select image </span>
|
||||||
<span class="fileinput-exists">
|
<span class="fileinput-exists">
|
||||||
Change </span>
|
Change </span>
|
||||||
<input type="file" name="...">
|
<input type="file" name="file">
|
||||||
</span>
|
</span>
|
||||||
<a href="javascript:;" class="btn default fileinput-exists" data-dismiss="fileinput">
|
<a href="javascript:;" class="btn default fileinput-exists" data-dismiss="fileinput">
|
||||||
Remove </a>
|
Remove </a>
|
||||||
@ -113,14 +115,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="clearfix margin-top-10">
|
<div class="clearfix margin-top-10">
|
||||||
<span class="label label-danger">NOTE! </span>
|
<span class="label label-danger">NOTE! </span>
|
||||||
<span>Attached image thumbnail is supported in Latest Firefox, Chrome, Opera, Safari and Internet Explorer 10 only </span>
|
<span> Only support <strong>.PNG, .JPG, .JPEG</strong>. Best size is <strong>200x200</strong>. </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="margin-top-10">
|
<div class="margin-top-10">
|
||||||
<a href="javascript:;" class="btn green-haze">
|
<button type="submit" class="btn green-haze"> Submit</button>
|
||||||
Submit </a>
|
|
||||||
<a href="javascript:;" class="btn default">
|
|
||||||
Cancel </a>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -130,23 +129,17 @@
|
|||||||
{% 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 class="password-form" action="{{ user_profile }}" method="POST">
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label">Current Password</label>
|
|
||||||
<input type="password" class="form-control"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label">New Password</label>
|
<label class="control-label">New Password</label>
|
||||||
<input type="password" class="form-control"/>
|
<input name="password" id="newpassword" type="password" class="form-control"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label">Re-type New Password</label>
|
<label class="control-label">Re-type New Password</label>
|
||||||
<input name="newpassword" type="password" class="form-control"/>
|
<input name="rpassword" type="password" class="form-control"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="margin-top-10">
|
<div class="margin-top-10">
|
||||||
<button type="submit" class="btn green-haze"> Change Password</button>
|
<button type="submit" class="btn green-haze"> Change Password</button>
|
||||||
<a href="javascript:;" class="btn default">
|
|
||||||
Cancel </a>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -167,18 +160,19 @@
|
|||||||
{{ super() }}
|
{{ super() }}
|
||||||
<!-- BEGIN PAGE LEVEL PLUGINS -->
|
<!-- BEGIN PAGE LEVEL PLUGINS -->
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='global/plugins/bootstrap-select/bootstrap-select.min.js') }}"></script>
|
<script type="text/javascript" src="{{ url_for('static', filename='global/plugins/bootstrap-select/bootstrap-select.min.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='global/plugins/select2/select2.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='global/plugins/jquery-validation/js/jquery.validate.min.js') }}" type="text/javascript"></script>
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='global/plugins/jquery-multi-select/js/jquery.multi-select.js') }}"></script>
|
|
||||||
<!-- END PAGE LEVEL PLUGINS -->
|
<!-- END PAGE LEVEL PLUGINS -->
|
||||||
|
|
||||||
<!-- BEGIN PAGE LEVEL SCRIPTS -->
|
<!-- BEGIN PAGE LEVEL SCRIPTS -->
|
||||||
<script src="{{ url_for('static', filename='global/scripts/metronic.js') }}" type="text/javascript"></script>
|
<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/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 -->
|
<!-- END PAGE LEVEL SCRIPTS -->
|
||||||
<script>
|
<script>
|
||||||
jQuery(document).ready(function() {
|
jQuery(document).ready(function() {
|
||||||
Metronic.init(); // init metronic core componets
|
Metronic.init(); // init metronic core componets
|
||||||
Layout.init(); // init layout
|
Layout.init(); // init layout
|
||||||
|
UserProfile.init();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<!-- END JAVASCRIPTS -->
|
<!-- END JAVASCRIPTS -->
|
||||||
|
30
app/views.py
30
app/views.py
@ -1,10 +1,12 @@
|
|||||||
|
import os
|
||||||
import json
|
import json
|
||||||
import jinja2
|
import jinja2
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from flask.ext.login import login_user, logout_user, current_user, login_required
|
from flask.ext.login import login_user, logout_user, current_user, login_required
|
||||||
from flask import Flask, g, request, make_response, jsonify, render_template, session, redirect, url_for, flash
|
from flask import Flask, g, request, make_response, jsonify, render_template, session, redirect, url_for, send_from_directory
|
||||||
|
from werkzeug import secure_filename
|
||||||
|
|
||||||
from lib import utils
|
from lib import utils
|
||||||
from app import app, login_manager
|
from app import app, login_manager
|
||||||
@ -403,15 +405,37 @@ def user_profile():
|
|||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return render_template('user_profile.html')
|
return render_template('user_profile.html')
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
# get new profile info
|
||||||
firstname = request.form['firstname'] if 'firstname' in request.form else ''
|
firstname = request.form['firstname'] if 'firstname' in request.form else ''
|
||||||
lastname = request.form['lastname'] if 'lastname' in request.form else ''
|
lastname = request.form['lastname'] if 'lastname' in request.form else ''
|
||||||
email = request.form['email'] if 'email' in request.form else ''
|
email = request.form['email'] if 'email' in request.form else ''
|
||||||
new_password = request.form['newpassword'] if 'newpassword' in request.form else ''
|
new_password = request.form['password'] if 'password' in request.form else ''
|
||||||
|
|
||||||
user = User(username=current_user.username, plain_text_password=new_password, firstname=firstname, lastname=lastname, email=email, reload_info=False)
|
# get new avatar
|
||||||
|
save_file_name = None
|
||||||
|
if 'file' in request.files:
|
||||||
|
file = request.files['file']
|
||||||
|
if file:
|
||||||
|
filename = secure_filename(file.filename)
|
||||||
|
file_extension = filename.rsplit('.', 1)[1]
|
||||||
|
|
||||||
|
if file_extension.lower() in ['jpg', 'jpeg', 'png']:
|
||||||
|
save_file_name = current_user.username + '.' + file_extension
|
||||||
|
file.save(os.path.join(app.config['UPLOAD_DIR'], 'avatar', save_file_name))
|
||||||
|
|
||||||
|
|
||||||
|
# update user profile
|
||||||
|
user = User(username=current_user.username, plain_text_password=new_password, firstname=firstname, lastname=lastname, email=email, avatar=save_file_name, reload_info=False)
|
||||||
user.update_profile()
|
user.update_profile()
|
||||||
|
|
||||||
return render_template('user_profile.html')
|
return render_template('user_profile.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/user/avatar/<string:filename>')
|
||||||
|
def user_avatar(filename):
|
||||||
|
return send_from_directory(os.path.join(app.config['UPLOAD_DIR'], 'avatar'), filename)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET', 'POST'])
|
@app.route('/', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def index():
|
def index():
|
||||||
|
@ -10,6 +10,9 @@ PORT = 9393
|
|||||||
LOG_LEVEL = 'DEBUG'
|
LOG_LEVEL = 'DEBUG'
|
||||||
LOG_FILE = 'logfile.log'
|
LOG_FILE = 'logfile.log'
|
||||||
|
|
||||||
|
# Upload
|
||||||
|
UPLOAD_DIR = os.path.join(basedir, 'upload')
|
||||||
|
|
||||||
# DATABASE CONFIG
|
# DATABASE CONFIG
|
||||||
SQLALCHEMY_DATABASE_URI = 'mysql://root:123456@192.168.59.103/pdns'
|
SQLALCHEMY_DATABASE_URI = 'mysql://root:123456@192.168.59.103/pdns'
|
||||||
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
|
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
|
||||||
|
4
upload/avatar/.gitignore
vendored
Normal file
4
upload/avatar/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
Loading…
Reference in New Issue
Block a user