* Finished with backend/settings page.

* Started user login and privileges management.
This commit is contained in:
alextselegidis@gmail.com 2013-09-20 13:58:11 +00:00
parent 4278e3d334
commit fcc142a11b
15 changed files with 563 additions and 73 deletions

View file

@ -58,5 +58,17 @@ define('AJAX_FAILURE', 'FAILURE');
define('SETTINGS_SYSTEM', 'SETTINGS_SYSTEM'); define('SETTINGS_SYSTEM', 'SETTINGS_SYSTEM');
define('SETTINGS_USER', 'SETTINGS_USER'); define('SETTINGS_USER', 'SETTINGS_USER');
define('PRIV_VIEW', 1);
define('PRIV_ADD', 2);
define('PRIV_EDIT', 4);
define('PRIV_DELETE', 8);
define('PAGE_APPOINTMENTS', 'appointments');
define('PAGE_CUSTOMERS', 'customers');
define('PAGE_SERVICES', 'services');
define('PAGE_USERS', 'users');
define('PAGE_SYSTEM_SETTINGS', 'system_settings');
define('PAGE_USER_SETTINGS', 'user_settings');
/* End of file constants.php */ /* End of file constants.php */
/* Location: ./application/config/constants.php */ /* Location: ./application/config/constants.php */

View file

@ -1,6 +1,11 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Backend extends CI_Controller { class Backend extends CI_Controller {
public function __construct() {
parent::__construct();
$this->load->library('session');
}
/** /**
* Display the main backend page. * Display the main backend page.
* *
@ -13,7 +18,8 @@ class Backend extends CI_Controller {
* appear when the page loads. * appear when the page loads.
*/ */
public function index($appointment_hash = '') { public function index($appointment_hash = '') {
// @task Require user to be logged in the application. $this->session->set_userdata('dest_url', $this->config->item('base_url') . 'backend');
if (!$this->hasPrivileges(PAGE_APPOINTMENTS)) return;
$this->load->model('appointments_model'); $this->load->model('appointments_model');
$this->load->model('providers_model'); $this->load->model('providers_model');
@ -47,7 +53,8 @@ class Backend extends CI_Controller {
* In this page the user can manage all the customer records of the system. * In this page the user can manage all the customer records of the system.
*/ */
public function customers() { public function customers() {
// @task Require user to be logged in the application. $this->session->set_userdata('dest_url', $this->config->item('base_url') . 'backend/customers');
if (!$this->hasPrivileges(PAGE_CUSTOMERS)) return;
$this->load->model('providers_model'); $this->load->model('providers_model');
$this->load->model('customers_model'); $this->load->model('customers_model');
@ -75,7 +82,8 @@ class Backend extends CI_Controller {
* from the backend services page. * from the backend services page.
*/ */
public function services() { public function services() {
// @task Require user to be logged in the application. $this->session->set_userdata('dest_url', $this->config->item('base_url') . 'backend/services');
if (!$this->hasPrivileges(PAGE_SERVICES)) return;
$this->load->model('customers_model'); $this->load->model('customers_model');
$this->load->model('services_model'); $this->load->model('services_model');
@ -99,7 +107,8 @@ class Backend extends CI_Controller {
* the page where the admin defines which service can each provider provide. * the page where the admin defines which service can each provider provide.
*/ */
public function users() { public function users() {
// @task Require user to be logged in the application. $this->session->set_userdata('dest_url', $this->config->item('base_url') . 'backend/users');
if (!$this->hasPrivileges(PAGE_USERS)) return;
$this->load->model('providers_model'); $this->load->model('providers_model');
$this->load->model('secretaries_model'); $this->load->model('secretaries_model');
@ -127,6 +136,10 @@ class Backend extends CI_Controller {
* installation (core settings like company name, book timeout etc). * installation (core settings like company name, book timeout etc).
*/ */
public function settings() { public function settings() {
$this->session->set_userdata('dest_url', $this->config->item('base_url') . 'backend/settings');
if (!$this->hasPrivileges(PAGE_SYSTEM_SETTINGS)
&& !$this->hasPrivileges(PAGE_USER_SETTINGS)) return;
$this->load->model('settings_model'); $this->load->model('settings_model');
$this->load->model('user_model'); $this->load->model('user_model');
@ -148,6 +161,48 @@ class Backend extends CI_Controller {
$this->load->view('backend/settings', $view); $this->load->view('backend/settings', $view);
$this->load->view('backend/footer', $view); $this->load->view('backend/footer', $view);
} }
/**
* Check whether current user is logged in and has the required privileges to
* view a page.
*
* The backend page requires different privileges from the users to display pages. Not all
* pages are avaiable to all users. For example secretaries should not be able to edit the
* system users.
*
* @see Constant Definition In application/config/constants.php
*
* @param string $page This argument must match the roles field names of each section
* (eg "appointments", "users" ...).
* @param bool $redirect (OPTIONAL - TRUE) If the user has not the required privileges
* (either not logged in or insufficient role privileges) then the user will be redirected
* to another page. Set this argument to FALSE when using ajax.
* @return bool Returns whether the user has the required privileges to view the page or
* not. If the user is not logged in then he will be prompted to log in. If he hasn't the
* required privileges then an info message will be displayed.
*/
private function hasPrivileges($page, $redirect = TRUE) {
// Check if user is logged in.
$user_id = $this->session->userdata('user_id');
if ($user_id == FALSE) { // User not logged in, display the login view.
if ($redirect) {
header('Location: ' . $this->config->item('base_url') . 'user/login');
}
return FALSE;
}
// Check if the user has the required privileges for viewing the selected page.
$role_slug = $this->session->userdata('role_slug');
$role_priv = $this->db->get_where('ea_roles', array('slug' => $role_slug))->row_array();
if ($role_priv[$page] < PRIV_VIEW) { // User does not have the permission to view the page.
if ($redirect) {
header('Location: ' . $this->config->item('base_url') . 'user/no_privileges');
}
return FALSE;
}
return TRUE;
}
} }
/* End of file backend.php */ /* End of file backend.php */

View file

@ -811,10 +811,8 @@ class Backend_api extends CI_Controller {
$settings = json_decode($_POST['settings'], true); $settings = json_decode($_POST['settings'], true);
$this->settings_model->save_settings($settings); $this->settings_model->save_settings($settings);
} else if ($_POST['type'] == SETTINGS_USER) { } else if ($_POST['type'] == SETTINGS_USER) {
$this->load->library('session');
$this->load->model('user_model'); $this->load->model('user_model');
$user_id = $this->session->userdata('user_id'); $this->user_model->save_settings(json_decode($_POST['settings'], true));
$this->user_model->save_settings($_POST['settings'], $user_id);
} }
echo json_encode(AJAX_SUCCESS); echo json_encode(AJAX_SUCCESS);

View file

@ -0,0 +1,62 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class User extends CI_Controller {
public function __construct() {
parent::__construct();
$this->load->library('session');
}
public function index() {
header('Location: ' . $this->config->item('base_url') . 'user/login');
}
public function login() {
$view['base_url'] = $this->config->item('base_url');
$view['dest_url'] = $this->session->userdata('dest_url');
$this->load->view('user/login', $view);
}
public function logout() {
}
public function forgot_password() {
}
public function no_privileges() {
// can't view the requested page.
}
/**
* [AJAX] Check whether the user has entered the correct login credentials.
*/
public function ajax_check_login() {
try {
if (!isset($_POST['username']) || !isset($_POST['password'])) {
throw new Exception('Invalid credentials given!');
}
$this->load->helper('general');
$this->load->model('user_model');
$hash_password = $this->hash_password($_POST['password']);
$user_data = $this->user_model->check_login($_POST['username'], $hash_password);
if ($user_data) {
$this->session->set_userdata($user_data); // Save data on user's session.
echo json_encode(AJAX_SUCCESS);
} else {
echo json_encode(AJAX_SUCCESS);
}
} catch(Exception $exc) {
echo json_encode(array(
'exceptions' => array(exceptionToJavaScript($exc))
));
}
}
}
/* End of file user.php */
/* Location: ./application/controllers/user.php */

View file

@ -25,5 +25,18 @@ function date3339($timestamp=0) {
return $date; return $date;
} }
/**
* Generate a hash of password string.
*
* For user security, all system passwords are stored in hash string into the database. Use
* this method to produce the hash.
*
* @param string $password Given string password.
* @return string Returns the hash string of the given password.
*/
function hash_password($password) {
return md5($password); // @task include salt and hash more times.
}
/* End of file general_helper.php */ /* End of file general_helper.php */
/* Location: ./application/helpers/general_helper.php */ /* Location: ./application/helpers/general_helper.php */

View file

@ -18,22 +18,32 @@ class User_Model extends CI_Model {
* @return array Returns an array with user settings. * @return array Returns an array with user settings.
*/ */
public function get_settings($user_id) { public function get_settings($user_id) {
$settings = $this->db->get_where('ea_user_settings', array('id_users' => $user_id))->row_array(); $user = $this->db->get_where('ea_users', array('id' => $user_id))->row_array();
unset($settings['id_users']); $user['settings'] = $this->db->get_where('ea_user_settings', array('id_users' => $user_id))->row_array();
return $settings; unset($user['settings']['id_users']);
return $user;
} }
/** /**
* This method saves the user settings into the database. * This method saves the user settings into the database.
* *
* @param array $settings Contains the current users settings. * @param array $user Contains the current users settings.
* @param numeric $user_id User record id of the settings.
* @return bool Returns the operation result. * @return bool Returns the operation result.
*/ */
public function save_settings($settings, $user_id) { public function save_settings($user) {
$settings['id_users'] = $user_id; $user_settings = $user['settings'];
$this->db->where('id_users', $user_id); $user_settings['id_users'] = $user['id'];
return $this->db->update('ea_user_settings', $settings); unset($user['settings']);
if (!$this->db->update('ea_users', $user, array('id' => $user['id']))) {
return FALSE;
}
if (!$this->db->update('ea_user_settings', $user_settings, array('id_users' => $user['id']))) {
return FALSE;
}
return TRUE;
} }
} }

View file

@ -91,7 +91,7 @@
record.</strong> After that you can add break periods. record.</strong> After that you can add break periods.
</span> </span>
<table class="working-hours table table-striped"> <table class="working-plan table table-striped">
<thead> <thead>
<tr> <tr>
<th>Day</th> <th>Day</th>
@ -102,38 +102,38 @@
<tbody> <tbody>
<tr> <tr>
<td><label class="checkbox"><input type="checkbox" id="monday" />Monday</label></td> <td><label class="checkbox"><input type="checkbox" id="monday" />Monday</label></td>
<td><input type="text" id="monday-start" /></td> <td><input type="text" id="monday-start" class="work-start" /></td>
<td><input type="text" id="monday-end" /></td> <td><input type="text" id="monday-end" class="work-end" /></td>
</tr> </tr>
<tr> <tr>
<td><label class="checkbox"><input type="checkbox" id="tuesday" />Tuesday</label></td> <td><label class="checkbox"><input type="checkbox" id="tuesday" />Tuesday</label></td>
<td><input type="text" id="tuesday-start" /></td> <td><input type="text" id="tuesday-start" class="work-start" /></td>
<td><input type="text" id="tuesday-end" /></td> <td><input type="text" id="tuesday-end" class="work-end" /></td>
</tr> </tr>
<tr> <tr>
<td><label class="checkbox"><input type="checkbox" id="wednesday" />Wednesday</label></td> <td><label class="checkbox"><input type="checkbox" id="wednesday" />Wednesday</label></td>
<td><input type="text" id="wednesday-start" /></td> <td><input type="text" id="wednesday-start" class="work-start" /></td>
<td><input type="text" id="wednesday-end" /></td> <td><input type="text" id="wednesday-end" class="work-end" /></td>
</tr> </tr>
<tr> <tr>
<td><label class="checkbox"><input type="checkbox" id="thursday" />Thursday</label></td> <td><label class="checkbox"><input type="checkbox" id="thursday" />Thursday</label></td>
<td><input type="text" id="thursday-start" /></td> <td><input type="text" id="thursday-start" class="work-start" /></td>
<td><input type="text" id="thursday-end" /></td> <td><input type="text" id="thursday-end" class="work-end" /></td>
</tr> </tr>
<tr> <tr>
<td><label class="checkbox"><input type="checkbox" id="friday" />Friday</label></td> <td><label class="checkbox"><input type="checkbox" id="friday" />Friday</label></td>
<td><input type="text" id="friday-start" /></td> <td><input type="text" id="friday-start" class="work-start" /></td>
<td><input type="text" id="friday-end" /></td> <td><input type="text" id="friday-end" class="work-end" /></td>
</tr> </tr>
<tr> <tr>
<td><label class="checkbox"><input type="checkbox" id="saturday" />Saturday</label></td> <td><label class="checkbox"><input type="checkbox" id="saturday" />Saturday</label></td>
<td><input type="text" id="saturday-start" /></td> <td><input type="text" id="saturday-start" class="work-start" /></td>
<td><input type="text" id="saturday-end" /></td> <td><input type="text" id="saturday-end" class="work-end" /></td>
</tr> </tr>
<tr> <tr>
<td><label class="checkbox"><input type="checkbox" id="sunday" />Sunday</label></td> <td><label class="checkbox"><input type="checkbox" id="sunday" />Sunday</label></td>
<td><input type="text" id="sunday-start" /></td> <td><input type="text" id="sunday-start" class="work-start" /></td>
<td><input type="text" id="sunday-end" /></td> <td><input type="text" id="sunday-end" class="work-end" /></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -206,16 +206,16 @@
<input type="text" id="first-name" class="span9" /> <input type="text" id="first-name" class="span9" />
<label for="last-name">Last Name *</label> <label for="last-name">Last Name *</label>
<input type="text" id="last-name" class="span9" /> <input type="text" id="last-name" class="span9 required" />
<label for="email">Email *</label> <label for="email">Email *</label>
<input type="text" id="email" class="span9" /> <input type="text" id="email" class="span9 required" />
<label for="mobile-number">Mobile Number</label> <label for="mobile-number">Mobile Number</label>
<input type="text" id="mobile-number" class="span9" /> <input type="text" id="mobile-number" class="span9" />
<label for="phone-number">Phone Number</label> <label for="phone-number">Phone Number *</label>
<input type="text" id="phone-number" class="span9" /> <input type="text" id="phone-number" class="span9 required" />
<label for="address">Address</label> <label for="address">Address</label>
<input type="text" id="address" class="span9" /> <input type="text" id="address" class="span9" />
@ -237,17 +237,17 @@
<legend>Miscellaneous</legend> <legend>Miscellaneous</legend>
<label for="username">Username *</label> <label for="username">Username *</label>
<input type="text" id="username" /> <input type="text" id="username" class="required" />
<label for="password">Password *</label> <label for="password">Password</label>
<input type="text" id="password" /> <input type="password" id="password" />
<label for="retype-password">Retype Password *</label> <label for="retype-password">Retype Password</label>
<input type="text" id="retype-password" /> <input type="password" id="retype-password" />
<br> <br>
<button type="button" class="btn" data-toggle="button"> <button type="button" id="user-notifications" class="btn" data-toggle="button">
<i class="icon-asterisk"></i> <i class="icon-asterisk"></i>
Receive Email Notifications Receive Email Notifications
</button> </button>

View file

@ -0,0 +1,7 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
?>

View file

@ -0,0 +1,121 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<?php // INCLUDE JS FILES ?>
<script
type="text/javascript"
src="<?php echo $this->config->base_url(); ?>assets/js/libs/jquery/jquery.min.js"></script>
<script
type="text/javascript"
src="<?php echo $this->config->base_url(); ?>assets/js/libs/bootstrap/bootstrap.min.js"></script>
<script
type="text/javascript"
src="<?php echo $this->config->base_url(); ?>assets/js/libs/date.js"></script>
<script
type="text/javascript"
src="<?php echo $this->config->base_url(); ?>assets/js/general_functions.js"></script>
<?php // INCLUDE CSS FILES ?>
<link
rel="stylesheet"
type="text/css"
href="<?php echo $this->config->base_url(); ?>assets/css/libs/bootstrap/bootstrap.css">
<link
rel="stylesheet"
type="text/css"
href="<?php echo $this->config->base_url(); ?>assets/css/libs/bootstrap/bootstrap-responsive.css">
<?php // SET FAVICON FOR PAGE ?>
<link
rel="icon"
type="image/x-icon"
href="<?php echo $this->config->base_url(); ?>assets/images/favicon.ico">
<style>
body {
background-color: #CAEDF3;
}
#login-frame {
width: 630px;
margin: 150px auto 0 auto;
background: #FFF;
border: 1px solid #DDDADA;
padding: 70px;
}
#login-icon {
float: right;
margin-top: 17px;
}
label {
font-weight: bold;
}
.forgot-password {
margin-left: 20px;
}
</style>
<script>
$(document).ready(function() {
var GlobalVariables = {
'baseUrl': <?php echo '"' . $base_url . '"'; ?>,
'destUrl': <?php echo '"' . $dest_url . '"'; ?>
};
/**
* Event: Login Button "Click"
*
* Make an ajax call to the server and check whether the user's credentials are right.
* If yes then redirect him to his desired page, otherwise display a message.
*/
$('#login').click(function() {
var postUrl = GlobalVariables.baseUrl . 'user/ajax_check_login';
var postData = {
'username': $('#username').val(),
'password': $('#password').val()
};
$.post(postUrl, postData, function(response) {
//////////////////////////////////////////////////
console.log('Check Login Response: ', response);
//////////////////////////////////////////////////
if (response == true) {
location(GlobalVariables.destUrl);
} else {
$('.alert').text('Login failed, please enter the correct credentials '
+ 'and try again.');
$('.alert').removeClass('hidden');
}
});
});
});
</script>
</head>
<body>
<div id="login-frame" class="frame-container">
<h2>Login Required</h2>
<p>Welcome! You will need to login in order to view this page.</p>
<hr>
<div class="alert hidden"></div>
<form>
<label for="username">Username</label>
<input type="text" id="username" placeholder="Enter your username here ..." />
<label for="password">Password</label>
<input type="password" id="password" placeholder="Enter your password here ..." />
<br><br>
<button type="button" id="login" class="btn btn-primary btn-large">Login</button>
<a href="<?php echo $base_url; ?>user/forgot_password" class="forgot-password">Forgot Your Password?</a>
</form>
</div>
</body>
</html>

View file

@ -0,0 +1,7 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
?>

View file

@ -0,0 +1,7 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
?>

View file

@ -384,12 +384,12 @@ body .modal-header h3 {
cursor: pointer; cursor: pointer;
} }
#business-logic .working-hours td input[type="text"] { #business-logic .working-plan td input[type="text"] {
margin-bottom: 0; margin-bottom: 0;
cursor: pointer; cursor: pointer;
} }
#business-logic .working-hours label.checkbox { #business-logic .working-plan label.checkbox {
margin-top: 5px; margin-top: 5px;
margin-bottom: 0; margin-bottom: 0;
} }

View file

@ -36,23 +36,21 @@ var Backend = {
/** /**
* Place the backend footer always on the bottom of the page. * Place the backend footer always on the bottom of the page.
*
* @task Re-enable this method.
*/ */
placeFooterToBottom: function() { placeFooterToBottom: function() {
// var $footer = $('#footer'); var $footer = $('#footer');
//
// if (window.innerHeight > $('body').height()) { if (window.innerHeight > $('body').height()) {
// $footer.css({ $footer.css({
// 'position': 'absolute', 'position': 'absolute',
// 'width': '100%', 'width': '100%',
// 'bottom': '0px' 'bottom': '0px'
// }); });
// } else { } else {
// $footer.css({ $footer.css({
// 'position': 'static' 'position': 'static'
// }); });
// } }
}, },
/** /**

View file

@ -42,7 +42,7 @@ var BackendSettings = {
initialize: function(bindEventHandlers) { initialize: function(bindEventHandlers) {
if (bindEventHandlers == undefined) bindEventHandlers = true; if (bindEventHandlers == undefined) bindEventHandlers = true;
// Apply values from database. // Apply setting values from database.
$.each(GlobalVariables.settings.system, function(index, setting) { $.each(GlobalVariables.settings.system, function(index, setting) {
$('input[data-field="' + setting.name + '"]').val(setting.value); $('input[data-field="' + setting.name + '"]').val(setting.value);
}); });
@ -93,19 +93,50 @@ var BackendSettings = {
}); });
// Make break cells editable. // Make break cells editable.
BackendSettings.breakDayEditable($('#breaks .break-day')); BackendSettings.editableBreakDay($('#breaks .break-day'));
BackendSettings.breakTimeEditable($('#breaks').find('.break-start, .break-end')); BackendSettings.editableBreakTime($('#breaks').find('.break-start, .break-end'));
// Set timepickers where needed. // Set timepickers where needed.
$('.working-hours input').timepicker({ $('.working-plan input').timepicker({
timeFormat: 'HH:mm' 'timeFormat': 'HH:mm',
'onSelect': function(datetime, inst) {
// Start time must be earlier than end time.
var start = Date.parse($(this).parent().parent().find('.work-start').val());
var end = Date.parse($(this).parent().parent().find('.work-end').val());
if (start > end) {
$(this).parent().parent().find('.work-end').val(start.addHours(1).toString('HH:mm'));
}
}
}); });
// Book Advance Timeout Spinner // Book Advance Timeout Spinner
$('#book-advance-timeout').spinner({ $('#book-advance-timeout').spinner({
min: 0 'min': 0
}); });
// Load user settings into form
$('#user-id').val(GlobalVariables.settings.user.id);
$('#first-name').val(GlobalVariables.settings.user.first_name);
$('#last-name').val(GlobalVariables.settings.user.last_name);
$('#email').val(GlobalVariables.settings.user.email);
$('#mobile-number').val(GlobalVariables.settings.user.mobile_number);
$('#phone-number').val(GlobalVariables.settings.user.phone_number);
$('#address').val(GlobalVariables.settings.user.address);
$('#city').val(GlobalVariables.settings.user.city);
$('#state').val(GlobalVariables.settings.user.state);
$('#zip-code').val(GlobalVariables.settings.user.zip_code);
$('#notes').val(GlobalVariables.settings.user.notes);
$('#username').val(GlobalVariables.settings.user.settings.username);
$('#password, #retype-password').val('');
if (GlobalVariables.settings.user.settings.notifications == true) {
$('#user-notifications').addClass('active');
} else {
$('#user-notifications').removeClass('active');
}
// Set default settings helper. // Set default settings helper.
BackendSettings.settings = new SystemSettings(); BackendSettings.settings = new SystemSettings();
@ -114,6 +145,10 @@ var BackendSettings = {
} }
}, },
/**
* Bind the backend/settings default event handlers. This method depends on the
* backend/settings html, so do not use this method on a different page.
*/
bindEventHandlers: function() { bindEventHandlers: function() {
/** /**
* Event: Tab "Click" * Event: Tab "Click"
@ -121,6 +156,11 @@ var BackendSettings = {
* Change the visible tab contents. * Change the visible tab contents.
*/ */
$('.tab').click(function() { $('.tab').click(function() {
// Bootstrap has a bug with toggle buttons. Their toggle state is lost when the
// button is hidden or shown. Show before anything else we need to store the toggle
// and apply it whenever the user tab is clicked..
var areNotificationsActive = $('#user-notifications').hasClass('active');
$('.active').removeClass('active'); $('.active').removeClass('active');
$(this).addClass('active'); $(this).addClass('active');
$('.tab-content').hide(); $('.tab-content').hide();
@ -134,6 +174,13 @@ var BackendSettings = {
} else if ($(this).hasClass('user-tab')) { } else if ($(this).hasClass('user-tab')) {
$('#user').show(); $('#user').show();
BackendSettings.settings = new UserSettings(); BackendSettings.settings = new UserSettings();
// Apply toggle state to user notifications button.
if (areNotificationsActive) {
$('#user-notifications').addClass('active');
} else {
$('#user-notifications').removeClass('active');
}
} }
}); });
@ -145,6 +192,9 @@ var BackendSettings = {
$('.save-settings').click(function() { $('.save-settings').click(function() {
var settings = BackendSettings.settings.get(); var settings = BackendSettings.settings.get();
BackendSettings.settings.save(settings); BackendSettings.settings.save(settings);
//////////////////////////////////////////////
console.log('Settings To Save: ', settings);
//////////////////////////////////////////////
}); });
/** /**
@ -152,7 +202,7 @@ var BackendSettings = {
* *
* Enable or disable the time selection for each day. * Enable or disable the time selection for each day.
*/ */
$('.working-hours input[type="checkbox"]').click(function() { $('.working-plan input[type="checkbox"]').click(function() {
var id = $(this).attr('id'); var id = $(this).attr('id');
if ($(this).prop('checked') == true) { if ($(this).prop('checked') == true) {
@ -195,8 +245,8 @@ var BackendSettings = {
// Bind editable and event handlers. // Bind editable and event handlers.
tr = $('#breaks tr').get()[1]; tr = $('#breaks tr').get()[1];
BackendSettings.breakDayEditable($(tr).find('.break-day')); BackendSettings.editableBreakDay($(tr).find('.break-day'));
BackendSettings.breakTimeEditable($(tr).find('.break-start, .break-end')); BackendSettings.editableBreakTime($(tr).find('.break-start, .break-end'));
$(tr).find('.edit-break').trigger('click'); $(tr).find('.edit-break').trigger('click');
}); });
@ -252,6 +302,13 @@ var BackendSettings = {
* Save the editable values and restore the table to its initial state. * Save the editable values and restore the table to its initial state.
*/ */
$(document).on('click', '.save-break', function() { $(document).on('click', '.save-break', function() {
// Break's start time must always be prior to break's end.
var start = Date.parse($(this).parent().parent().find('.break-start input').val());
var end = Date.parse($(this).parent().parent().find('.break-end input').val());
if (start > end) {
$(this).parent().parent().find('.break-end input').val(start.addHours(1).toString('HH:mm'));
}
BackendSettings.enableSubmit = true; BackendSettings.enableSubmit = true;
$(this).parent().parent().find('.editable .submit-editable').trigger('click'); $(this).parent().parent().find('.editable .submit-editable').trigger('click');
BackendSettings.enableSubmit = false; BackendSettings.enableSubmit = false;
@ -266,7 +323,7 @@ var BackendSettings = {
* *
* @param {object} $selector The cells to be initialized. * @param {object} $selector The cells to be initialized.
*/ */
breakDayEditable: function($selector) { editableBreakDay: function($selector) {
$selector.editable(function(value, settings) { $selector.editable(function(value, settings) {
return value; return value;
}, { }, {
@ -293,7 +350,7 @@ var BackendSettings = {
* *
* @param {object} $selector The cells to be initialized. * @param {object} $selector The cells to be initialized.
*/ */
breakTimeEditable: function($selector) { editableBreakTime: function($selector) {
$selector.editable(function(value, settings) { $selector.editable(function(value, settings) {
// Do not return the value because the user needs to press the "Save" button. // Do not return the value because the user needs to press the "Save" button.
return value; return value;
@ -362,13 +419,27 @@ SystemSettings.prototype.get = function() {
// Business Logic Tab // Business Logic Tab
var workingPlan = {}; var workingPlan = {};
$('.working-hours input[type="checkbox"').each(function() { $('.working-plan input[type="checkbox"').each(function() {
var id = $(this).attr('id'); var id = $(this).attr('id');
if ($(this).prop('checked') == true) { if ($(this).prop('checked') == true) {
workingPlan[id] = {} workingPlan[id] = {}
workingPlan[id].start = $('#' + id + '-start').val(); workingPlan[id].start = $('#' + id + '-start').val();
workingPlan[id].end = $('#' + id + '-end').val(); workingPlan[id].end = $('#' + id + '-end').val();
workingPlan[id].breaks = {}; workingPlan[id].breaks = [];
$('#breaks tr').each(function(index, tr) {
var day = $(tr).find('.break-day').text().toLowerCase();
if (day == id) {
var start = $(tr).find('.break-start').text();
var end = $(tr).find('.break-end').text();
workingPlan[id].breaks.push({
'start': start,
'end': end
});
}
});
} else { } else {
workingPlan[id] = null; workingPlan[id] = null;
@ -380,6 +451,11 @@ SystemSettings.prototype.get = function() {
'value': JSON.stringify(workingPlan) 'value': JSON.stringify(workingPlan)
}); });
settings.push({
'name': 'book_advance_timeout',
'value': $('#book-advance-timeout').val()
});
return settings; return settings;
}; };
@ -390,7 +466,32 @@ SystemSettings.prototype.get = function() {
* @returns {bool} Returns the validation result. * @returns {bool} Returns the validation result.
*/ */
SystemSettings.prototype.validate = function() { SystemSettings.prototype.validate = function() {
$('#general .required').css('border', '');
try {
// Validate required fields.
var missingRequired = false;
$('#general .required').each(function() {
if ($(this).val() == '' || $(this).val() == undefined) {
$(this).css('border', '2px solid red');
missingRequired = true;
}
});
if (missingRequired) {
throw 'Fields with * are required.';
}
// Validate company email address.
if (!GeneralFunctions.validateEmail($('#company-email').val())) {
$('#company-email').css('border', '2px solid red');
throw 'Invalid email address!';
}
return true;
} catch(exc) {
Backend.displayNotification(exc);
return false;
}
}; };
/** /**
@ -402,10 +503,33 @@ var UserSettings = function() {};
/** /**
* Get the settings data for the user settings. * Get the settings data for the user settings.
* *
* @returns {array} Returns the user settings array. * @returns {object} Returns the user settings array.
*/ */
UserSettings.prototype.get = function() { UserSettings.prototype.get = function() {
var user = {
'id': $('#user-id').val(),
'first_name': $('#first-name').val(),
'last_name': $('#last-name').val(),
'email': $('#email').val(),
'mobile_number': $('#mobile-number').val(),
'phone_number': $('#phone-number').val(),
'address': $('#address').val(),
'city': $('#city').val(),
'state': $('#state').val(),
'zip_code': $('#zip-code').val(),
'notes': $('#notes').val(),
'settings': {
'username': $('#username').val(),
'password': $('#password').val(),
'notifications': $('#user-notifications').hasClass('active')
}
};
if ($('#password').val() != '') {
user.settings.password = $('#password').val();
}
return user;
}; };
/** /**
@ -414,7 +538,27 @@ UserSettings.prototype.get = function() {
* @param {array} settings Contains the user settings. * @param {array} settings Contains the user settings.
*/ */
UserSettings.prototype.save = function(settings) { UserSettings.prototype.save = function(settings) {
if (!BackendSettings.settings.validate(settings)) {
Backend.displayNotification('User settings are invalid! Please review your settings '
+ 'and try again.');
return; // Validation failed, do not procceed.
}
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_save_settings';
var postData = {
'type': BackendSettings.SETTINGS_USER,
'settings': JSON.stringify(settings)
};
$.post(postUrl, postData, function(response) {
//////////////////////////////////////////////////////////
console.log('Save User Settings Response: ', response);
//////////////////////////////////////////////////////////
if (!Backend.handleAjaxExceptions(response)) return;
Backend.displayNotification('Settings saved successfully!');
}, 'json');
}; };
/** /**
@ -424,5 +568,37 @@ UserSettings.prototype.save = function(settings) {
* @returns {bool} Returns the validation result. * @returns {bool} Returns the validation result.
*/ */
UserSettings.prototype.validate = function() { UserSettings.prototype.validate = function() {
$('#user .required').css('border', '');
$('#user').find('#password, #retype-password').css('border', '');
try {
// Validate required fields.
var missingRequired = false;
$('#user .required').each(function() {
if ($(this).val() == '' || $(this).val() == undefined) {
$(this).css('border', '2px solid red');
missingRequired = true;
}
});
if (missingRequired) {
throw 'Fields with * are required.';
}
// Validate passwords (if provided).
if ($('#password').val() != $('#retype-password').val()) {
$('#password, #retype-password').css('border', '2px solid red');
throw 'Passwords mismatch!';
}
// Validate user email.
if (!GeneralFunctions.validateEmail($('#email').val())) {
$('#email').css('border', '2px solid red');
throw 'Invalid email address!';
}
return true;
} catch(exc) {
Backend.displayNotification(exc);
return false;
}
}; };

View file

@ -589,6 +589,12 @@ AdminsHelper.prototype.validate = function(admin) {
throw 'Passwords mismatch!'; throw 'Passwords mismatch!';
} }
// Validate user email.
if (!GeneralFunctions.validateEmail($('#admin-email').val())) {
$('#admin-email').css('border', '2px solid red');
throw 'Invalid email address!';
}
return true; return true;
} catch(exc) { } catch(exc) {
$('#admins .form-message').text(exc); $('#admins .form-message').text(exc);
@ -611,6 +617,8 @@ AdminsHelper.prototype.resetForm = function() {
$('#admins .form-message').hide(); $('#admins .form-message').hide();
$('#admin-notifications').removeClass('active'); $('#admin-notifications').removeClass('active');
$('#admin-notifications').prop('disabled', true); $('#admin-notifications').prop('disabled', true);
$('#admins .required').css('border', '');
$('#admin-password, #admin-password-confirm').css('border', '');
}; };
/** /**
@ -764,6 +772,12 @@ ProvidersHelper.prototype.validate = function(provider) {
throw 'Passwords mismatch!'; throw 'Passwords mismatch!';
} }
// Validate user email.
if (!GeneralFunctions.validateEmail($('#provider-email').val())) {
$('#provider-email').css('border', '2px solid red');
throw 'Invalid email address!';
}
return true; return true;
} catch(exc) { } catch(exc) {
$('#providers .form-message').text(exc); $('#providers .form-message').text(exc);
@ -788,6 +802,8 @@ ProvidersHelper.prototype.resetForm = function() {
$('#provider-notifications').prop('disabled', true); $('#provider-notifications').prop('disabled', true);
$('#provider-services input[type="checkbox"]').prop('checked', false); $('#provider-services input[type="checkbox"]').prop('checked', false);
$('#provider-services input[type="checkbox"]').prop('disabled', true); $('#provider-services input[type="checkbox"]').prop('disabled', true);
$('#providers .required').css('border', '');
$('#provider-password, #provider-password-confirm').css('border', '');
}; };
/** /**
@ -950,6 +966,12 @@ SecretariesHelper.prototype.validate = function(secretary) {
throw 'Passwords mismatch!'; throw 'Passwords mismatch!';
} }
// Validate user email.
if (!GeneralFunctions.validateEmail($('#secretary-email').val())) {
$('#secretary-email').css('border', '2px solid red');
throw 'Invalid email address!';
}
return true; return true;
} catch(exc) { } catch(exc) {
$('#secretaries .form-message').text(exc); $('#secretaries .form-message').text(exc);
@ -974,6 +996,8 @@ SecretariesHelper.prototype.resetForm = function() {
$('#secretary-notifications').prop('disabled', true); $('#secretary-notifications').prop('disabled', true);
$('#secretary-providers input[type="checkbox"]').prop('checked', false); $('#secretary-providers input[type="checkbox"]').prop('checked', false);
$('#secretary-providers input[type="checkbox"]').prop('disabled', true); $('#secretary-providers input[type="checkbox"]').prop('disabled', true);
$('#secretaries .required').css('border', '');
$('#secretary-password, #secretary-password-confirm').css('border', '');
}; };
/** /**