diff --git a/src/application/config/constants.php b/src/application/config/constants.php index aa25e87f..994944c4 100644 --- a/src/application/config/constants.php +++ b/src/application/config/constants.php @@ -58,5 +58,17 @@ define('AJAX_FAILURE', 'FAILURE'); define('SETTINGS_SYSTEM', 'SETTINGS_SYSTEM'); 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 */ /* 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 2668b725..e12587af 100644 --- a/src/application/controllers/backend.php +++ b/src/application/controllers/backend.php @@ -1,6 +1,11 @@ load->library('session'); + } + /** * Display the main backend page. * @@ -13,7 +18,8 @@ class Backend extends CI_Controller { * appear when the page loads. */ 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('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. */ 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('customers_model'); @@ -75,7 +82,8 @@ class Backend extends CI_Controller { * from the backend services page. */ 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('services_model'); @@ -99,7 +107,8 @@ class Backend extends CI_Controller { * the page where the admin defines which service can each provider provide. */ 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('secretaries_model'); @@ -127,6 +136,10 @@ class Backend extends CI_Controller { * installation (core settings like company name, book timeout etc). */ 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('user_model'); @@ -148,6 +161,48 @@ class Backend extends CI_Controller { $this->load->view('backend/settings', $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 */ diff --git a/src/application/controllers/backend_api.php b/src/application/controllers/backend_api.php index 79a08689..b864c18d 100644 --- a/src/application/controllers/backend_api.php +++ b/src/application/controllers/backend_api.php @@ -811,10 +811,8 @@ class Backend_api extends CI_Controller { $settings = json_decode($_POST['settings'], true); $this->settings_model->save_settings($settings); } else if ($_POST['type'] == SETTINGS_USER) { - $this->load->library('session'); $this->load->model('user_model'); - $user_id = $this->session->userdata('user_id'); - $this->user_model->save_settings($_POST['settings'], $user_id); + $this->user_model->save_settings(json_decode($_POST['settings'], true)); } echo json_encode(AJAX_SUCCESS); diff --git a/src/application/controllers/user.php b/src/application/controllers/user.php new file mode 100644 index 00000000..c3bb6a05 --- /dev/null +++ b/src/application/controllers/user.php @@ -0,0 +1,62 @@ +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 */ \ No newline at end of file diff --git a/src/application/helpers/general_helper.php b/src/application/helpers/general_helper.php index 0066474a..8f31c5a0 100644 --- a/src/application/helpers/general_helper.php +++ b/src/application/helpers/general_helper.php @@ -25,5 +25,18 @@ function date3339($timestamp=0) { 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 */ /* Location: ./application/helpers/general_helper.php */ \ No newline at end of file diff --git a/src/application/models/user_model.php b/src/application/models/user_model.php index fbacc999..0fb003e8 100644 --- a/src/application/models/user_model.php +++ b/src/application/models/user_model.php @@ -18,22 +18,32 @@ class User_Model extends CI_Model { * @return array Returns an array with user settings. */ public function get_settings($user_id) { - $settings = $this->db->get_where('ea_user_settings', array('id_users' => $user_id))->row_array(); - unset($settings['id_users']); - return $settings; + $user = $this->db->get_where('ea_users', array('id' => $user_id))->row_array(); + $user['settings'] = $this->db->get_where('ea_user_settings', array('id_users' => $user_id))->row_array(); + unset($user['settings']['id_users']); + return $user; } /** * This method saves the user settings into the database. * - * @param array $settings Contains the current users settings. - * @param numeric $user_id User record id of the settings. + * @param array $user Contains the current users settings. * @return bool Returns the operation result. */ - public function save_settings($settings, $user_id) { - $settings['id_users'] = $user_id; - $this->db->where('id_users', $user_id); - return $this->db->update('ea_user_settings', $settings); + public function save_settings($user) { + $user_settings = $user['settings']; + $user_settings['id_users'] = $user['id']; + 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; } } diff --git a/src/application/views/backend/settings.php b/src/application/views/backend/settings.php index 9a86721c..7487582b 100644 --- a/src/application/views/backend/settings.php +++ b/src/application/views/backend/settings.php @@ -91,7 +91,7 @@ record. After that you can add break periods. - +
@@ -102,38 +102,38 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + +
Day
@@ -206,16 +206,16 @@ - + - + - - + + @@ -237,17 +237,17 @@ Miscellaneous - + - - + + - - + +
- diff --git a/src/application/views/user/forgot_password.php b/src/application/views/user/forgot_password.php new file mode 100644 index 00000000..b6eddc13 --- /dev/null +++ b/src/application/views/user/forgot_password.php @@ -0,0 +1,7 @@ + diff --git a/src/application/views/user/login.php b/src/application/views/user/login.php new file mode 100644 index 00000000..36febafa --- /dev/null +++ b/src/application/views/user/login.php @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + +
+

Login Required

+

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

+
+ +
+ + + + + + +

+ + + + Forgot Your Password? +
+
+ + \ No newline at end of file diff --git a/src/application/views/user/logout.php b/src/application/views/user/logout.php new file mode 100644 index 00000000..b6eddc13 --- /dev/null +++ b/src/application/views/user/logout.php @@ -0,0 +1,7 @@ + diff --git a/src/application/views/user/no_privileges.php b/src/application/views/user/no_privileges.php new file mode 100644 index 00000000..b6eddc13 --- /dev/null +++ b/src/application/views/user/no_privileges.php @@ -0,0 +1,7 @@ + diff --git a/src/assets/css/backend.css b/src/assets/css/backend.css index c61dfd64..d1a45a5d 100644 --- a/src/assets/css/backend.css +++ b/src/assets/css/backend.css @@ -384,12 +384,12 @@ body .modal-header h3 { cursor: pointer; } -#business-logic .working-hours td input[type="text"] { +#business-logic .working-plan td input[type="text"] { margin-bottom: 0; cursor: pointer; } -#business-logic .working-hours label.checkbox { +#business-logic .working-plan label.checkbox { margin-top: 5px; margin-bottom: 0; } diff --git a/src/assets/js/backend.js b/src/assets/js/backend.js index 22ca9270..99b32ed7 100644 --- a/src/assets/js/backend.js +++ b/src/assets/js/backend.js @@ -36,23 +36,21 @@ var Backend = { /** * Place the backend footer always on the bottom of the page. - * - * @task Re-enable this method. */ placeFooterToBottom: function() { -// var $footer = $('#footer'); -// -// if (window.innerHeight > $('body').height()) { -// $footer.css({ -// 'position': 'absolute', -// 'width': '100%', -// 'bottom': '0px' -// }); -// } else { -// $footer.css({ -// 'position': 'static' -// }); -// } + var $footer = $('#footer'); + + if (window.innerHeight > $('body').height()) { + $footer.css({ + 'position': 'absolute', + 'width': '100%', + 'bottom': '0px' + }); + } else { + $footer.css({ + 'position': 'static' + }); + } }, /** diff --git a/src/assets/js/backend_settings.js b/src/assets/js/backend_settings.js index cfc7728c..130721f1 100644 --- a/src/assets/js/backend_settings.js +++ b/src/assets/js/backend_settings.js @@ -42,7 +42,7 @@ var BackendSettings = { initialize: function(bindEventHandlers) { if (bindEventHandlers == undefined) bindEventHandlers = true; - // Apply values from database. + // Apply setting values from database. $.each(GlobalVariables.settings.system, function(index, setting) { $('input[data-field="' + setting.name + '"]').val(setting.value); }); @@ -93,19 +93,50 @@ var BackendSettings = { }); // Make break cells editable. - BackendSettings.breakDayEditable($('#breaks .break-day')); - BackendSettings.breakTimeEditable($('#breaks').find('.break-start, .break-end')); + BackendSettings.editableBreakDay($('#breaks .break-day')); + BackendSettings.editableBreakTime($('#breaks').find('.break-start, .break-end')); // Set timepickers where needed. - $('.working-hours input').timepicker({ - timeFormat: 'HH:mm' + $('.working-plan input').timepicker({ + '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({ - 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. 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() { /** * Event: Tab "Click" @@ -121,6 +156,11 @@ var BackendSettings = { * Change the visible tab contents. */ $('.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'); $(this).addClass('active'); $('.tab-content').hide(); @@ -134,6 +174,13 @@ var BackendSettings = { } else if ($(this).hasClass('user-tab')) { $('#user').show(); 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() { var settings = BackendSettings.settings.get(); 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. */ - $('.working-hours input[type="checkbox"]').click(function() { + $('.working-plan input[type="checkbox"]').click(function() { var id = $(this).attr('id'); if ($(this).prop('checked') == true) { @@ -195,8 +245,8 @@ var BackendSettings = { // Bind editable and event handlers. tr = $('#breaks tr').get()[1]; - BackendSettings.breakDayEditable($(tr).find('.break-day')); - BackendSettings.breakTimeEditable($(tr).find('.break-start, .break-end')); + BackendSettings.editableBreakDay($(tr).find('.break-day')); + BackendSettings.editableBreakTime($(tr).find('.break-start, .break-end')); $(tr).find('.edit-break').trigger('click'); }); @@ -252,6 +302,13 @@ var BackendSettings = { * Save the editable values and restore the table to its initial state. */ $(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; $(this).parent().parent().find('.editable .submit-editable').trigger('click'); BackendSettings.enableSubmit = false; @@ -266,7 +323,7 @@ var BackendSettings = { * * @param {object} $selector The cells to be initialized. */ - breakDayEditable: function($selector) { + editableBreakDay: function($selector) { $selector.editable(function(value, settings) { return value; }, { @@ -293,7 +350,7 @@ var BackendSettings = { * * @param {object} $selector The cells to be initialized. */ - breakTimeEditable: function($selector) { + editableBreakTime: function($selector) { $selector.editable(function(value, settings) { // Do not return the value because the user needs to press the "Save" button. return value; @@ -362,13 +419,27 @@ SystemSettings.prototype.get = function() { // Business Logic Tab var workingPlan = {}; - $('.working-hours input[type="checkbox"').each(function() { + $('.working-plan input[type="checkbox"').each(function() { var id = $(this).attr('id'); if ($(this).prop('checked') == true) { workingPlan[id] = {} workingPlan[id].start = $('#' + id + '-start').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 { workingPlan[id] = null; @@ -380,6 +451,11 @@ SystemSettings.prototype.get = function() { 'value': JSON.stringify(workingPlan) }); + settings.push({ + 'name': 'book_advance_timeout', + 'value': $('#book-advance-timeout').val() + }); + return settings; }; @@ -390,7 +466,32 @@ SystemSettings.prototype.get = function() { * @returns {bool} Returns the validation result. */ 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. * - * @returns {array} Returns the user settings array. + * @returns {object} Returns the user settings array. */ 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. */ 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. */ 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; + } }; diff --git a/src/assets/js/backend_users.js b/src/assets/js/backend_users.js index 6944bc2b..c29040aa 100644 --- a/src/assets/js/backend_users.js +++ b/src/assets/js/backend_users.js @@ -589,6 +589,12 @@ AdminsHelper.prototype.validate = function(admin) { 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; } catch(exc) { $('#admins .form-message').text(exc); @@ -611,6 +617,8 @@ AdminsHelper.prototype.resetForm = function() { $('#admins .form-message').hide(); $('#admin-notifications').removeClass('active'); $('#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!'; } + // Validate user email. + if (!GeneralFunctions.validateEmail($('#provider-email').val())) { + $('#provider-email').css('border', '2px solid red'); + throw 'Invalid email address!'; + } + return true; } catch(exc) { $('#providers .form-message').text(exc); @@ -788,6 +802,8 @@ ProvidersHelper.prototype.resetForm = function() { $('#provider-notifications').prop('disabled', true); $('#provider-services input[type="checkbox"]').prop('checked', false); $('#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!'; } + // Validate user email. + if (!GeneralFunctions.validateEmail($('#secretary-email').val())) { + $('#secretary-email').css('border', '2px solid red'); + throw 'Invalid email address!'; + } + return true; } catch(exc) { $('#secretaries .form-message').text(exc); @@ -974,6 +996,8 @@ SecretariesHelper.prototype.resetForm = function() { $('#secretary-notifications').prop('disabled', true); $('#secretary-providers input[type="checkbox"]').prop('checked', false); $('#secretary-providers input[type="checkbox"]').prop('disabled', true); + $('#secretaries .required').css('border', ''); + $('#secretary-password, #secretary-password-confirm').css('border', ''); }; /**