diff --git a/src/application/config/constants.php b/src/application/config/constants.php index 994944c4..deefe73e 100644 --- a/src/application/config/constants.php +++ b/src/application/config/constants.php @@ -70,5 +70,7 @@ define('PAGE_SERVICES', 'services'); define('PAGE_USERS', 'users'); define('PAGE_SYSTEM_SETTINGS', 'system_settings'); define('PAGE_USER_SETTINGS', 'user_settings'); + +define('MIN_PASSWORD_LENGTH', 7); /* End of file constants.php */ /* Location: ./application/config/constants.php */ \ No newline at end of file diff --git a/src/application/controllers/backend.php b/src/application/controllers/backend.php index e12587af..20a38410 100644 --- a/src/application/controllers/backend.php +++ b/src/application/controllers/backend.php @@ -143,17 +143,12 @@ class Backend extends CI_Controller { $this->load->model('settings_model'); $this->load->model('user_model'); - $this->load->library('session'); - - // @task Apply data for testing this page (this must be done during the login process). - $this->session->set_userdata('user_id', 18); - $this->session->set_userdata('user_slug', DB_SLUG_ADMIN); - + $this->load->library('session'); $user_id = $this->session->userdata('user_id'); $view['base_url'] = $this->config->item('base_url'); $view['company_name'] = $this->settings_model->get_setting('company_name'); - $view['user_slug'] = $this->session->userdata('user_slug'); + $view['role_slug'] = $this->session->userdata('role_slug'); $view['system_settings'] = $this->settings_model->get_settings(); $view['user_settings'] = $this->user_model->get_settings($user_id); diff --git a/src/application/controllers/backend_api.php b/src/application/controllers/backend_api.php index b864c18d..9c7a555f 100644 --- a/src/application/controllers/backend_api.php +++ b/src/application/controllers/backend_api.php @@ -703,6 +703,13 @@ class Backend_api extends CI_Controller { try { $this->load->model('providers_model'); $provider = json_decode($_POST['provider'], true); + + if (!isset($provider['working_plan'])) { + $this->load->model('settings_model'); + $provider['settings']['working_plan'] = $this->settings_model + ->get_setting('company_working_plan'); + } + $this->providers_model->add($provider); echo json_encode(AJAX_SUCCESS); } catch(Exception $exc) { @@ -822,6 +829,26 @@ class Backend_api extends CI_Controller { )); } } + + /** + * [AJAX] This method checks whether the username already exists in the database. + * + * @param string $_POST['username'] Record's username to validate. + * @param bool $_POST['record_exists'] Whether the record already exists in database. + */ + public function ajax_validate_username() { + try { + // We will use the function in the admins_model because it is sufficient for + // the rest user types for now (providers, secretaries). + $this->load->model('admins_model'); + $is_valid = $this->admins_model->validate_username($_POST['username'], $_POST['record_exists']); + echo json_encode($is_valid); + } catch(Exception $exc) { + echo json_encode(array( + 'exceptions' => array(exceptionToJavaScript($exc)) + )); + } + } } /* End of file backend_api.php */ diff --git a/src/application/controllers/user.php b/src/application/controllers/user.php index c3bb6a05..4966930f 100644 --- a/src/application/controllers/user.php +++ b/src/application/controllers/user.php @@ -13,11 +13,24 @@ class User extends CI_Controller { public function login() { $view['base_url'] = $this->config->item('base_url'); $view['dest_url'] = $this->session->userdata('dest_url'); + + if (!$view['dest_url']) { + $view['dest_url'] = $view['base_url'] . 'backend'; + } + $this->load->view('user/login', $view); } public function logout() { + $this->session->unset_userdata('user_id'); + $this->session->unset_userdata('user_email'); + $this->session->unset_userdata('role_slug'); + $this->session->unset_userdata('username'); + $this->session->unset_userdata('dest_url'); + $view['base_url'] = $this->config->item('base_url'); + + $this->load->view('user/logout', $view); } public function forgot_password() { @@ -37,17 +50,14 @@ class User extends CI_Controller { 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); + $user_data = $this->user_model->check_login($_POST['username'], $_POST['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); + echo json_encode(AJAX_FAILURE); } } catch(Exception $exc) { diff --git a/src/application/helpers/general_helper.php b/src/application/helpers/general_helper.php index 8f31c5a0..871c0cf6 100644 --- a/src/application/helpers/general_helper.php +++ b/src/application/helpers/general_helper.php @@ -29,13 +29,38 @@ function date3339($timestamp=0) { * 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. + * this method to produce the hashed password. * + * @param string $salt Salt value for current user. This value is stored on the database and + * is used when generating the password 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. +function hash_password($salt, $password) { + $salt = strtoupper($salt); + $password = strtoupper($password); + $half = (int)(strlen($salt) / 2); + $hash = hash('sha256', substr($salt, 0, $half ) . $password . substr($salt, $half)); + + for ($i = 0; $i < 100000; $i++) { + $hash = hash('sha256', $hash); + } + + return $hash; // @task include salt and hash more times. +} + +/** + * Generate a new password salt. + * + * This method will not check if the salt is unique in database. This must be done + * from the calling procedure. + * + * @return string Returns a salt string. + */ +function generate_salt() { + $max_length = 100; + $salt = hash('sha256', (uniqid(rand(), true))); + return substr($salt, 0, $max_length); } /* End of file general_helper.php */ diff --git a/src/application/models/admins_model.php b/src/application/models/admins_model.php index 07f3f22c..38149563 100644 --- a/src/application/models/admins_model.php +++ b/src/application/models/admins_model.php @@ -85,6 +85,8 @@ class Admins_Model extends CI_Model { * @throws Exception When the insert operation fails. */ public function insert($admin) { + $this->load->helper('general'); + $admin['id_roles'] = $this->get_admin_role_id(); $settings = $admin['settings']; unset($admin['settings']); @@ -97,6 +99,8 @@ class Admins_Model extends CI_Model { $admin['id'] = intval($this->db->insert_id()); $settings['id_users'] = $admin['id']; + $settings['salt'] = generate_salt(); + $settings['password'] = hash_password($settings['salt'], $settings['password']); // Insert admin settings. if (!$this->db->insert('ea_user_settings', $settings)) { @@ -117,10 +121,17 @@ class Admins_Model extends CI_Model { * @throws Exception When the update operation fails. */ public function update($admin) { + $this->load->helper('general'); + $settings = $admin['settings']; unset($admin['settings']); $settings['id_users'] = $admin['id']; + if (isset($settings['password'])) { + $salt = $this->db->get_where('ea_user_settings', array('id_users' => $admin['id']))->row()->salt; + $settings['password'] = hash_password($salt, $settings['password']); + } + $this->db->where('id', $admin['id']); if (!$this->db->update('ea_users', $admin)) { throw new Exception('Could not update admin record.'); @@ -193,6 +204,24 @@ class Admins_Model extends CI_Model { throw new Exception('Invalid email address provided : ' . $admin['email']); } + // Validate admin username + if (isset($admin['settings']['username'])) { + $num_rows = $this->db->get_where('ea_user_settings', + array('username' => $admin['settings']['username']))->num_rows(); + if ($num_rows > 0) { + throw new Exception('Username already exists, please select another ' + . 'and try again (username: ' . $admin['settings']['username'] . ')'); + } + } + + // Validate admin password + if (isset($admin['settings']['password'])) { + if (strlen($admin['settings']['password']) < MIN_PASSWORD_LENGTH) { + throw new Exception('The user password must be at least ' + . MIN_PASSWORD_LENGTH . ' characters long.'); + } + } + return TRUE; } catch (Exception $exc) { return FALSE; @@ -328,6 +357,22 @@ class Admins_Model extends CI_Model { public function get_admin_role_id() { return intval($this->db->get_where('ea_roles', array('slug' => DB_SLUG_ADMIN))->row()->id); } + + /** + * Validate Records Username + * + * @param string $username The provider records username. + * @param bool $record_exists Whether the record exists or not. + * @return bool Returns the validation result. + */ + public function validate_username($username, $record_exists) { + $num_rows = $this->db->get_where('ea_user_settings', array('username' => $username))->num_rows(); + if ($num_rows == 0 && $record_exists == FALSE || $num_rows == 1 && $record_exists == TRUE) { + return true; + } else { + return false; + } + } } /* End of file admins_model.php */ diff --git a/src/application/models/providers_model.php b/src/application/models/providers_model.php index 6ef9d0af..d84513a9 100644 --- a/src/application/models/providers_model.php +++ b/src/application/models/providers_model.php @@ -98,6 +98,8 @@ class Providers_Model extends CI_Model { * @throws Exception When the insert operation fails. */ public function insert($provider) { + $this->load->helper('general'); + // Get provider role id. $provider['id_roles'] = $this->get_providers_role_id(); @@ -112,6 +114,9 @@ class Providers_Model extends CI_Model { throw new Exception('Could not insert provider into the database'); } + $settings['salt'] = generate_salt(); + $settings['password'] = hash_password($settings['salt'], $settings['password']); + $provider['id'] = $this->db->insert_id(); $this->save_settings($settings, $provider['id']); $this->save_services($services, $provider['id']); @@ -128,12 +133,19 @@ class Providers_Model extends CI_Model { * @throws Exception When the update operation fails. */ public function update($provider) { + $this->load->helper('general'); + // Store service and settings (must not be present on the $provider array). $services = $provider['services']; unset($provider['services']); $settings = $provider['settings']; unset($provider['settings']); + if (isset($settings['password'])) { + $salt = $this->db->get_where('ea_user_settings', array('id_users' => $provider['id']))->row()->salt; + $settings['password'] = hash_password($salt, $settings['password']); + } + // Update provider record. $this->db->where('id', $provider['id']); if (!$this->db->update('ea_users', $provider)) { @@ -224,6 +236,24 @@ class Providers_Model extends CI_Model { throw new Exception('Invalid provider settings given: ' . print_r($provider, TRUE)); } + // Validate admin username + if (isset($provider['settings']['username'])) { + $num_rows = $this->db->get_where('ea_user_settings', + array('username' => $provider['settings']['username']))->num_rows(); + if ($num_rows > 0) { + throw new Exception('Username already exists, please select another ' + . 'and try again (username: ' . $provider['settings']['username'] . ')'); + } + } + + // Validate admin password + if (isset($provider['settings']['password'])) { + if (strlen($provider['settings']['password']) < MIN_PASSWORD_LENGTH) { + throw new Exception('The user password must be at least ' + . MIN_PASSWORD_LENGTH . ' characters long.'); + } + } + return TRUE; } catch (Exception $exc) { return FALSE; diff --git a/src/application/models/secretaries_model.php b/src/application/models/secretaries_model.php index 492db372..97a7a808 100644 --- a/src/application/models/secretaries_model.php +++ b/src/application/models/secretaries_model.php @@ -86,6 +86,8 @@ class Secretaries_Model extends CI_Model { * @throws Exception When the insert operation fails. */ public function insert($secretary) { + $this->load->helper('general'); + $providers = $secretary['providers']; unset($secretary['providers']); $settings = $secretary['settings']; @@ -98,6 +100,8 @@ class Secretaries_Model extends CI_Model { } $secretary['id'] = intval($this->db->insert_id()); + $settings['salt'] = generate_salt(); + $settings['password'] = hash_password($settings['salt'], $settings['password']); $this->save_providers($providers, $secretary['id']); $this->save_settings($settings, $secretary['id']); @@ -113,11 +117,18 @@ class Secretaries_Model extends CI_Model { * @throws Exception When the update operation fails. */ public function update($secretary) { + $this->load->helper('general'); + $providers = $secretary['providers']; unset($secretary['providers']); $settings = $secretary['settings']; unset($secretary['settings']); + if (isset($settings['password'])) { + $salt = $this->db->get_where('ea_user_settings', array('id_users' => $secretary['id']))->row()->salt; + $settings['password'] = hash_password($salt, $settings['password']); + } + $this->db->where('id', $secretary['id']); if (!$this->db->update('ea_users', $secretary)){ throw new Exception('Could not update secretary record.'); @@ -193,6 +204,24 @@ class Secretaries_Model extends CI_Model { throw new Exception('Invalid email address provided : ' . $secretary['email']); } + // Validate admin username + if (isset($secretary['settings']['username'])) { + $num_rows = $this->db->get_where('ea_user_settings', + array('username' => $secretary['settings']['username']))->num_rows(); + if ($num_rows > 0) { + throw new Exception('Username already exists, please select another ' + . 'and try again (username: ' . $secretary['settings']['username'] . ')'); + } + } + + // Validate admin password + if (isset($secretary['settings']['password'])) { + if (strlen($secretary['settings']['password']) < MIN_PASSWORD_LENGTH) { + throw new Exception('The user password must be at least ' + . MIN_PASSWORD_LENGTH . ' characters long.'); + } + } + return TRUE; } catch (Exception $exc) { return FALSE; diff --git a/src/application/models/user_model.php b/src/application/models/user_model.php index 0fb003e8..53a2173e 100644 --- a/src/application/models/user_model.php +++ b/src/application/models/user_model.php @@ -35,6 +35,13 @@ class User_Model extends CI_Model { $user_settings['id_users'] = $user['id']; unset($user['settings']); + // Prepare user password (hash). + if (isset($user_settings['password'])) { + $this->load->helper('general'); + $salt = $this->db->get_where('ea_user_settings', array('id_users' => $user['id']))->row()->salt; + $user_settings['password'] = hash_password($salt, $user_settings['password']); + } + if (!$this->db->update('ea_users', $user, array('id' => $user['id']))) { return FALSE; } @@ -45,6 +52,43 @@ class User_Model extends CI_Model { return TRUE; } + + /** + * Retrieve user's salt from database. + * + * @param string $username This will be used to find the user record. + * @return string Returns the salt db value. + */ + public function get_salt($username) { + $user = $this->db->get_where('ea_user_settings', array('username' => $username))->row_array(); + return ($user) ? $user['salt'] : ''; + } + + /** + * Performs the check of the given user credentials. + * + * @param string $username Given user's name. + * @param type $password Given user's password (not hashed yet). + * @return array|null Returns the session data of the logged in user or null on + * failure. + */ + public function check_login($username, $password) { + $this->load->helper('general'); + $salt = $this->user_model->get_salt($username); + $password = hash_password($salt, $password); + + $user_data = $this->db + ->select('ea_users.id AS user_id, ea_users.email AS user_email, ' + . 'ea_roles.slug AS role_slug, ea_user_settings.username') + ->from('ea_users') + ->join('ea_roles', 'ea_roles.id = ea_users.id_roles', 'innder') + ->join('ea_user_settings', 'ea_user_settings.id_users = ea_users.id') + ->where('ea_user_settings.username', $username) + ->where('ea_user_settings.password', $password) + ->get()->row_array(); + + return ($user_data) ? $user_data : NULL; + } } /* End of file user_model.php */ diff --git a/src/application/views/appointments/book_success.php b/src/application/views/appointments/book_success.php index 5320ea81..3ce128bb 100644 --- a/src/application/views/appointments/book_success.php +++ b/src/application/views/appointments/book_success.php @@ -149,7 +149,7 @@ 'Your appointment has successfully been added to ' + 'your Google Calendar account.
' + '' + - 'Click here to view your appoinmtent on Google ' + + 'Click here to view your appointment on Google ' + 'Calendar.' + '' + '

' + diff --git a/src/application/views/backend/header.php b/src/application/views/backend/header.php index 42324473..634f70bf 100644 --- a/src/application/views/backend/header.php +++ b/src/application/views/backend/header.php @@ -103,7 +103,7 @@ - + Logout diff --git a/src/application/views/backend/settings.php b/src/application/views/backend/settings.php index 7487582b..d0d50330 100644 --- a/src/application/views/backend/settings.php +++ b/src/application/views/backend/settings.php @@ -8,7 +8,7 @@
-

Login Required

-

Welcome! You will need to login in order to view this page.

+

Backend Section

+

Welcome! You will need to login in order to view backend pages.


-
+ @@ -112,7 +120,7 @@

- + Forgot Your Password?
diff --git a/src/application/views/user/logout.php b/src/application/views/user/logout.php index b6eddc13..f6d8af5c 100644 --- a/src/application/views/user/logout.php +++ b/src/application/views/user/logout.php @@ -1,7 +1,78 @@ - + + + + + + + + + + + + + + + + + + + +
+

Logout

+

+ You have been successfully logged out! Click on one of the following buttons to + navigate to a different page. +

+ +
+ + + + Book Appointment + + + + + Backend Section + +
+ + \ No newline at end of file diff --git a/src/assets/css/backend.css b/src/assets/css/backend.css index d1a45a5d..aa1c5779 100644 --- a/src/assets/css/backend.css +++ b/src/assets/css/backend.css @@ -160,7 +160,7 @@ body .modal-header h3 { } #calendar .fc-header-title h2 { - font-size: 22px; + font-size: 20px; margin: 0px; line-height: 32px; } diff --git a/src/assets/js/backend.js b/src/assets/js/backend.js index 99b32ed7..561b26ab 100644 --- a/src/assets/js/backend.js +++ b/src/assets/js/backend.js @@ -25,10 +25,6 @@ var Backend = { /** * Backend Constants */ - EXCEPTIONS_TITLE: 'Unexpected Issues', - EXCEPTIONS_MESSAGE: 'The operation could not complete due to unexpected issues. ', - WARNINGS_TITLE: 'Unexpected Warnings', - WARNINGS_MESSAGE: 'The operation completed but some warnings appeared. ', DB_SLUG_ADMIN: 'admin', DB_SLUG_PROVIDER: 'provider', DB_SLUG_SECRETARY: 'secretary', @@ -91,31 +87,5 @@ var Backend = { $('#notification').html(notificationHtml); $('#notification').show('blind'); - }, - - /** - * All backend js code has the same way of dislaying exceptions that are raised on the - * server during an ajax call. - * - * @param {object} response Contains the server response. If exceptions or warnings are - * found, user friendly messages are going to be displayed to the user. - * @returns {bool} Returns whether the the ajax callback should continue the execution or - * stop, due to critical server exceptions. - */ - handleAjaxExceptions: function(response) { - if (response.exceptions) { - response.exceptions = GeneralFunctions.parseExceptions(response.exceptions); - GeneralFunctions.displayMessageBox(Backend.EXCEPTIONS_TITLE, Backend.EXCEPTIONS_MESSAGE); - $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions)); - return false; - } - - if (response.warnings) { - response.warnings = GeneralFunctions.parseExceptions(response.warnings); - GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE); - $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings)); - } - - return true; } }; \ No newline at end of file diff --git a/src/assets/js/backend_services.js b/src/assets/js/backend_services.js index c7d52627..1c940324 100644 --- a/src/assets/js/backend_services.js +++ b/src/assets/js/backend_services.js @@ -300,7 +300,7 @@ var BackendServices = { console.log('Update Available Categories Response:', response); /////////////////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; GlobalVariables.categories = response; var $select = $('#service-category'); @@ -334,7 +334,7 @@ ServicesHelper.prototype.save = function(service) { $.post(postUrl, postData, function(response) { console.log('Save Service Response:', response); - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Service saved successfully!'); BackendServices.helper.resetForm(); @@ -356,7 +356,7 @@ ServicesHelper.prototype.delete = function(id) { console.log('Delete service response:', response); //////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Service deleted successfully!'); @@ -439,7 +439,7 @@ ServicesHelper.prototype.filter = function(key) { console.log('Filter services response:', response); ///////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; BackendServices.helper.filterResults = response; $('#services .filter-results').html(''); @@ -492,7 +492,7 @@ CategoriesHelper.prototype.filter = function(key) { console.log('Filter Categories Response:', response); /////////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; BackendServices.helper.filterResults = response; $('#categories .filter-results').html(''); @@ -518,7 +518,7 @@ CategoriesHelper.prototype.save = function(category) { console.log('Save Service Category Response:', response); /////////////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Service saved successfully!'); BackendServices.helper.resetForm(); @@ -541,7 +541,7 @@ CategoriesHelper.prototype.delete = function(id) { console.log('Delete category response:', response); //////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Category deleted successfully!'); diff --git a/src/assets/js/backend_settings.js b/src/assets/js/backend_settings.js index 130721f1..da0a670c 100644 --- a/src/assets/js/backend_settings.js +++ b/src/assets/js/backend_settings.js @@ -394,7 +394,7 @@ SystemSettings.prototype.save = function(settings) { console.log('Save General Settings Response:', response); /////////////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Settings saved successfully!'); }, 'json'); @@ -520,7 +520,6 @@ UserSettings.prototype.get = function() { 'notes': $('#notes').val(), 'settings': { 'username': $('#username').val(), - 'password': $('#password').val(), 'notifications': $('#user-notifications').hasClass('active') } }; @@ -555,7 +554,7 @@ UserSettings.prototype.save = function(settings) { console.log('Save User Settings Response: ', response); ////////////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Settings saved successfully!'); }, 'json'); diff --git a/src/assets/js/backend_users.js b/src/assets/js/backend_users.js index c29040aa..5d7782ad 100644 --- a/src/assets/js/backend_users.js +++ b/src/assets/js/backend_users.js @@ -2,9 +2,12 @@ * This namespace handles the js functionality of the users backend page. It uses three other * classes (defined below) in order to handle the admin, provider and secretary record types. * - * @namespace BackendUsers. + * @namespace BackendUsers */ var BackendUsers = { + MIN_PASSWORD_LENGTH: 7, + + /** * Contains the current tab record methods for the page. * @@ -76,7 +79,7 @@ var BackendUsers = { console.log('Get all db providers response:', response); ////////////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; GlobalVariables.providers = response; @@ -95,6 +98,26 @@ var BackendUsers = { $('.filter-key').val(''); }); + $('#admin-username').focusout(function() { + // Validate username. + var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_validate_username'; + var postData = { + 'username': $('#admin-username').val(), + 'record_exists': ($('#admin-id').val() != '') ? true : false + }; + $.post(postUrl, postData, function(response) { + /////////////////////////////////////////////////////// + console.log('Validate Username Response:', response); + /////////////////////////////////////////////////////// + if (!GeneralFunctions.handleAjaxExceptions(response)) return; + if (!response) { + $('#admin-username').css('border', '2px solid red'); + $('#admins .form-message').text('Username already exists.'); + $('#admins .form-message').show(); + } + }, 'json'); + }); + /** * Event: Filter Admins Button "Click" * @@ -113,8 +136,8 @@ var BackendUsers = { * Display the selected admin data to the user. */ $(document).on('click', '.admin-row', function() { - if ($('#admin .filter-admins').prop('disabled')) { - $('#admin .filter-results').css('color', '#AAA'); + if ($('#admins .filter-admins').prop('disabled')) { + $('#admins .filter-results').css('color', '#AAA'); return; // exit because we are currently on edit mode } @@ -243,8 +266,8 @@ var BackendUsers = { * Display the selected provider data to the user. */ $(document).on('click', '.provider-row', function() { - if ($('#provider .filter-providers').prop('disabled')) { - $('#provider .filter-results').css('color', '#AAA'); + if ($('#providers .filter-providers').prop('disabled')) { + $('#providers .filter-results').css('color', '#AAA'); return; // exit because we are currently on edit mode } @@ -383,8 +406,8 @@ var BackendUsers = { * Display the selected secretary data to the user. */ $(document).on('click', '.secretary-row', function() { - if ($('#secretary .filter-secretaries').prop('disabled')) { - $('#secretary .filter-results').css('color', '#AAA'); + if ($('#secretaries .filter-secretaries').prop('disabled')) { + $('#secretaries .filter-results').css('color', '#AAA'); return; // exit because we are currently on edit mode } @@ -522,9 +545,9 @@ var AdminsHelper = function() { * then the update operation is going to be executed. */ AdminsHelper.prototype.save = function(admin) { - ////////////////////////////////////////// + //////////////////////////////////////////// console.log('Admin data to save:', admin); - ////////////////////////////////////////// + //////////////////////////////////////////// var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_save_admin'; var postData = { 'admin': JSON.stringify(admin) }; @@ -533,7 +556,7 @@ AdminsHelper.prototype.save = function(admin) { //////////////////////////////////////////////// console.log('Save Admin Response:', response); //////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Admin saved successfully!'); BackendUsers.helper.resetForm(); BackendUsers.helper.filter($('#admins .filter-key').val()); @@ -550,10 +573,10 @@ AdminsHelper.prototype.delete = function(id) { var postData = { 'admin_id': id }; $.post(postUrl, postData, function(response) { - //////////////////////////////////////////////////// + ////////////////////////////////////////////////// console.log('Delete admin response:', response); - //////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + ////////////////////////////////////////////////// + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Admin deleted successfully!'); BackendUsers.helper.resetForm(); BackendUsers.helper.filter($('#admins .filter-key').val()); @@ -589,6 +612,12 @@ AdminsHelper.prototype.validate = function(admin) { throw 'Passwords mismatch!'; } + if ($('#admin-password').val().length < BackendUsers.MIN_PASSWORD_LENGTH) { + $('#admin-password, #admin-password-confirm').css('border', '2px solid red'); + throw 'Password must be at least ' + BackendUsers.MIN_PASSWORD_LENGTH + + ' characters long.'; + } + // Validate user email. if (!GeneralFunctions.validateEmail($('#admin-email').val())) { $('#admin-email').css('border', '2px solid red'); @@ -657,11 +686,11 @@ AdminsHelper.prototype.filter = function(key) { var postData = { 'key': key }; $.post(postUrl, postData, function(response) { - ///////////////////////////////////////////////////// + /////////////////////////////////////////////////// console.log('Filter admins response:', response); - ///////////////////////////////////////////////////// + /////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; BackendUsers.helper.filterResults = response; @@ -705,18 +734,18 @@ var ProvidersHelper = function() { * then the update operation is going to be executed. */ ProvidersHelper.prototype.save = function(provider) { - ////////////////////////////////////////// + ////////////////////////////////////////////////// console.log('Provider data to save:', provider); - ////////////////////////////////////////// + ////////////////////////////////////////////////// var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_save_provider'; var postData = { 'provider': JSON.stringify(provider) }; $.post(postUrl, postData, function(response) { - //////////////////////////////////////////////// + /////////////////////////////////////////////////// console.log('Save Provider Response:', response); - //////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + /////////////////////////////////////////////////// + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Provider saved successfully!'); BackendUsers.helper.resetForm(); BackendUsers.helper.filter($('#providers .filter-key').val()); @@ -733,10 +762,10 @@ ProvidersHelper.prototype.delete = function(id) { var postData = { 'provider_id': id }; $.post(postUrl, postData, function(response) { - //////////////////////////////////////////////////// + ///////////////////////////////////////////////////// console.log('Delete provider response:', response); - //////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + ///////////////////////////////////////////////////// + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Provider deleted successfully!'); BackendUsers.helper.resetForm(); BackendUsers.helper.filter($('#providers .filter-key').val()); @@ -772,12 +801,34 @@ ProvidersHelper.prototype.validate = function(provider) { throw 'Passwords mismatch!'; } + if ($('#provider-password').val().length < BackendUsers.MIN_PASSWORD_LENGTH) { + $('#provider-password, #provider-password-confirm').css('border', '2px solid red'); + throw 'Password must be at least ' + BackendUsers.MIN_PASSWORD_LENGTH + + ' characters long.'; + } + // Validate user email. if (!GeneralFunctions.validateEmail($('#provider-email').val())) { $('#provider-email').css('border', '2px solid red'); throw 'Invalid email address!'; } + // Validate username. + var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_validate_username'; + var postData = { + 'username': $('#provider-username').val(), + 'record_exists': ($('#provider-id').val() != '') ? true : false + }; + $.post(postUrl, postData, function(response) { + /////////////////////////////////////////////////////// + console.log('Validate Username Response:', response); + /////////////////////////////////////////////////////// + if (!GeneralFunctions.handleAjaxExceptions(response)) return; + if (!response) { + throw('Username already exists, please enter another one and try again.'); + } + }); + return true; } catch(exc) { $('#providers .form-message').text(exc); @@ -851,11 +902,11 @@ ProvidersHelper.prototype.filter = function(key) { var postData = { 'key': key }; $.post(postUrl, postData, function(response) { - ///////////////////////////////////////////////////// + ////////////////////////////////////////////////////// console.log('Filter providers response:', response); - ///////////////////////////////////////////////////// + ////////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; BackendUsers.helper.filterResults = response; @@ -899,18 +950,18 @@ var SecretariesHelper = function() { * then the update operation is going to be executed. */ SecretariesHelper.prototype.save = function(secretary) { - ////////////////////////////////////////// + //////////////////////////////////////////////////// console.log('Secretary data to save:', secretary); - ////////////////////////////////////////// + //////////////////////////////////////////////////// var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_save_secretary'; var postData = { 'secretary': JSON.stringify(secretary) }; $.post(postUrl, postData, function(response) { - //////////////////////////////////////////////// + //////////////////////////////////////////////////// console.log('Save Secretary Response:', response); - //////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + //////////////////////////////////////////////////// + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Secretary saved successfully!'); BackendUsers.helper.resetForm(); BackendUsers.helper.filter($('#secretaries .filter-key').val()); @@ -927,10 +978,10 @@ SecretariesHelper.prototype.delete = function(id) { var postData = { 'secretary_id': id }; $.post(postUrl, postData, function(response) { - //////////////////////////////////////////////////// + ////////////////////////////////////////////////////// console.log('Delete secretary response:', response); - //////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + ////////////////////////////////////////////////////// + if (!GeneralFunctions.handleAjaxExceptions(response)) return; Backend.displayNotification('Secretary deleted successfully!'); BackendUsers.helper.resetForm(); BackendUsers.helper.filter($('#secretaries .filter-key').val()); @@ -966,12 +1017,34 @@ SecretariesHelper.prototype.validate = function(secretary) { throw 'Passwords mismatch!'; } + if ($('#secretary-password').val().length < BackendUsers.MIN_PASSWORD_LENGTH) { + $('#secretary-password, #secretary-password-confirm').css('border', '2px solid red'); + throw 'Password must be at least ' + BackendUsers.MIN_PASSWORD_LENGTH + + ' characters long.'; + } + // Validate user email. if (!GeneralFunctions.validateEmail($('#secretary-email').val())) { $('#secretary-email').css('border', '2px solid red'); throw 'Invalid email address!'; } + // Validate username. + var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_validate_username'; + var postData = { + 'username': $('#secretary-username').val(), + 'record_exists': ($('#secretary-id').val() != '') ? true : false + }; + $.post(postUrl, postData, function(response) { + /////////////////////////////////////////////////////// + console.log('Validate Username Response:', response); + /////////////////////////////////////////////////////// + if (!GeneralFunctions.handleAjaxExceptions(response)) return; + if (!response) { + throw('Username already exists, please enter another one and try again.'); + } + }); + return true; } catch(exc) { $('#secretaries .form-message').text(exc); @@ -1045,11 +1118,11 @@ SecretariesHelper.prototype.filter = function(key) { var postData = { 'key': key }; $.post(postUrl, postData, function(response) { - ///////////////////////////////////////////////////// + //////////////////////////////////////////////////////// console.log('Filter secretaries response:', response); - ///////////////////////////////////////////////////// + //////////////////////////////////////////////////////// - if (!Backend.handleAjaxExceptions(response)) return; + if (!GeneralFunctions.handleAjaxExceptions(response)) return; BackendUsers.helper.filterResults = response; diff --git a/src/assets/js/general_functions.js b/src/assets/js/general_functions.js index ef5d6a41..14f91457 100644 --- a/src/assets/js/general_functions.js +++ b/src/assets/js/general_functions.js @@ -6,6 +6,14 @@ * @namespace GeneralFunctions */ var GeneralFunctions = { + /** + * General Functions Constants + */ + EXCEPTIONS_TITLE: 'Unexpected Issues', + EXCEPTIONS_MESSAGE: 'The operation could not complete due to unexpected issues. ', + WARNINGS_TITLE: 'Unexpected Warnings', + WARNINGS_MESSAGE: 'The operation completed but some warnings appeared. ', + /** * This functions displays a message box in * the admin array. It is usefull when user @@ -234,5 +242,31 @@ var GeneralFunctions = { */ ucaseFirstLetter: function(str){ return str.charAt(0).toUpperCase() + str.slice(1); + }, + + /** + * All backend js code has the same way of dislaying exceptions that are raised on the + * server during an ajax call. + * + * @param {object} response Contains the server response. If exceptions or warnings are + * found, user friendly messages are going to be displayed to the user. + * @returns {bool} Returns whether the the ajax callback should continue the execution or + * stop, due to critical server exceptions. + */ + handleAjaxExceptions: function(response) { + if (response.exceptions) { + response.exceptions = GeneralFunctions.parseExceptions(response.exceptions); + GeneralFunctions.displayMessageBox(GeneralFunctions.EXCEPTIONS_TITLE, GeneralFunctions.EXCEPTIONS_MESSAGE); + $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions)); + return false; + } + + if (response.warnings) { + response.warnings = GeneralFunctions.parseExceptions(response.warnings); + GeneralFunctions.displayMessageBox(GeneralFunctions.WARNINGS_TITLE, GeneralFunctions.WARNINGS_MESSAGE); + $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings)); + } + + return true; } }; \ No newline at end of file