diff --git a/config/vufind/config.ini b/config/vufind/config.ini index 3bdefa43909cf4f0e5559f338d2711255583f40e..3df7cf9d0367b17b97868fe4835f8af0b73e4442 100644 --- a/config/vufind/config.ini +++ b/config/vufind/config.ini @@ -216,6 +216,19 @@ hideLogin = false ; (only applies when method = Database above). hash_passwords = false +; Allow users to recover passwords via email (if supported by Auth method) +; You can set the subject of recovery emails in your +; language files under the term "recovery_email_subject" +recover_password = false +; Time (seconds) before another recovery attempt can be made +recover_interval = 60 +; Length of time before a recovery hash can no longer be used (expires) +; Default: Two weeks +recover_hash_lifetime = 1209600 + +; Allow users to set change their passwords (if supported by Auth method) +change_password = true + ; Set this to false if you would like to store catalog passwords in plain text encrypt_ils_password = false diff --git a/languages/en.ini b/languages/en.ini index c81f5be82a98eaae3d041b486396155e6e607715..7786ee793330e840306c777347773690853571c0 100644 --- a/languages/en.ini +++ b/languages/en.ini @@ -146,6 +146,7 @@ cat_establish_account = "In order to establish your account profile, please ente cat_password_abbrev = "Catalog Password" cat_username_abbrev = "Catalog Username" CD = CD +Change Password = "Change Password" Check Hold = "Check Hold" Check Recall = "Check Recall" Checked Out = "Checked Out" @@ -185,6 +186,7 @@ confirm_hold_cancel_all_text = "Do you wish to cancel all your current holds?" confirm_hold_cancel_selected_text = "Do you wish to cancel your selected holds?" confirm_ill_request_cancel_all_text = "Do you wish to cancel all your current interlibrary loan requests?" confirm_ill_request_cancel_selected_text = "Do you wish to cancel your selected interlibrary loan requests?" +confirm_new_password = "Confirm New Password" confirm_storage_retrieval_request_cancel_all_text = "Do you wish to cancel all your current storage retrieval requests?" confirm_storage_retrieval_request_cancel_selected_text = "Do you wish to cancel your selected storage retrieval requests?" Contents = Contents @@ -199,6 +201,7 @@ course_reserves_empty_list = "No matching Course Reserves found." Cover Image = "Cover Image" Create a List = "Create a List" Create New Account = "Create New Account" +Create New Password = "Create New Password" Created = Created Date = Date date_day_placeholder = "D" @@ -525,6 +528,8 @@ New Item Search Results = "New Item Search Results" New Items = "New Items" New Title = "New Title" Newspaper = Newspaper +new_password = "New Password" +new_password_success = "Your password has successfully been changed" Next = Next No citations are available for this record = "No citations are available for this record" No Cover Image = "No Cover Image" @@ -579,6 +584,7 @@ no_items_selected = "No Items were Selected" Number = Number OAI Server = "OAI Server" of = of +old_password = "Old Password" On Reserve - Ask at Circulation Desk = "On Reserve - Ask at Circulation Desk" On Reserve = "On Reserve" Online Access = "Online Access" @@ -612,6 +618,7 @@ Please contact the Library Reference Department for assistance = "Please contact Please enable JavaScript. = "Please enable JavaScript." Posted by = "Posted by" posted_on = "on" +Preferences = Preferences Preferred Library = "Preferred Library" Prev = Prev Preview = "Preview" @@ -639,6 +646,19 @@ Read the full review online... = "Read the full review online..." Recall This = "Recall This" Record Citations = "Record Citations" Record Count = "Record Count" +recovery_by_email = "Recover by email" +recovery_by_username = "Recover by username" +recovery_disabled = "Password recovery not enabled" +recovery_email_notification = "A request was just made to recover the password for your account with %%library%%." +recovery_email_sent = "Password recovery instructions have been sent to the email address registered with this account." +recovery_email_subject = "VuFind Account Recovery" +recovery_email_url_pretext = "Please navigate to this url to set a new password: %%url%%" +recovery_expired_hash = "This recovery link has expired" +recovery_invalid_hash = "Recovery link not recognized" +recovery_new_disabled = "You are not allowed to change your password at this time" +recovery_title = "Password Recovery" +recovery_too_soon = "Too many recovery requests have been made, please try again later" +recovery_user_not_found = "We could not find your account" Region = Region Related Author = "Related Author" Related Items = "Related Items" diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index cb6821b0acc90acc3d981188984fa3457d014e97..b60712b1ef33fe49474e51309e45d29d118da179 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -580,13 +580,14 @@ $staticRoutes = array( 'Install/FixSecurity', 'Install/FixSolr', 'Install/Home', 'Install/PerformSecurityFix', 'Install/ShowSQL', 'LibGuides/Home', 'LibGuides/Results', - 'MyResearch/Account', 'MyResearch/CheckedOut', 'MyResearch/Delete', - 'MyResearch/DeleteList', 'MyResearch/Edit', 'MyResearch/Email', - 'MyResearch/Favorites', 'MyResearch/Fines', + 'MyResearch/Account', 'MyResearch/ChangePassword', 'MyResearch/CheckedOut', + 'MyResearch/Delete', 'MyResearch/DeleteList', 'MyResearch/Edit', + 'MyResearch/Email', 'MyResearch/Favorites', 'MyResearch/Fines', 'MyResearch/Holds', 'MyResearch/Home', - 'MyResearch/ILLRequests', - 'MyResearch/Logout', 'MyResearch/Profile', - 'MyResearch/SaveSearch', 'MyResearch/StorageRetrievalRequests', + 'MyResearch/ILLRequests', 'MyResearch/Logout', + 'MyResearch/NewPassword', 'MyResearch/Profile', + 'MyResearch/Recover', 'MyResearch/SaveSearch', + 'MyResearch/StorageRetrievalRequests', 'MyResearch/Verify', 'Primo/Advanced', 'Primo/Home', 'Primo/Search', 'QRCode/Show', 'QRCode/Unavailable', 'OAI/Server', 'Pazpar2/Home', 'Pazpar2/Search', 'Records/Home', diff --git a/module/VuFind/sql/mysql.sql b/module/VuFind/sql/mysql.sql index 954c5253e8d290a9ce59cb4542b6a74b37af2ea4..5cfb9252dc3b426e1cc9b864ca958d20241bb54c 100644 --- a/module/VuFind/sql/mysql.sql +++ b/module/VuFind/sql/mysql.sql @@ -178,6 +178,7 @@ CREATE TABLE `user` ( `major` varchar(100) NOT NULL DEFAULT '', `home_library` varchar(100) NOT NULL DEFAULT '', `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `verify_hash` varchar(42) NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/module/VuFind/sql/postgres.sql b/module/VuFind/sql/postgres.sql index eb4c419725ab3e08121442f0aa30703260e0b384..2d8c9aff0556a75358aac4f7ce3528f587c9796c 100644 --- a/module/VuFind/sql/postgres.sql +++ b/module/VuFind/sql/postgres.sql @@ -110,6 +110,7 @@ college varchar(100) NOT NULL DEFAULT '', major varchar(100) NOT NULL DEFAULT '', home_library varchar(100) NOT NULL DEFAULT '', created timestamp NOT NULL DEFAULT '1970-01-01 00:00:00', +verify_hash varchar(42) NOT NULL DEFAULT '', PRIMARY KEY (id), UNIQUE (username) ); diff --git a/module/VuFind/src/VuFind/Auth/AbstractBase.php b/module/VuFind/src/VuFind/Auth/AbstractBase.php index 464452378e581004c9c06afa883406491a0f966b..54d74e5174cdcf0303534ea61fbece64b7917e91 100644 --- a/module/VuFind/src/VuFind/Auth/AbstractBase.php +++ b/module/VuFind/src/VuFind/Auth/AbstractBase.php @@ -144,6 +144,23 @@ abstract class AbstractBase implements \VuFind\Db\Table\DbTableAwareInterface ); } + /** + * Update a user's password from the request. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object containing + * new account details. + * + * @throws AuthException + * @return \VuFind\Db\Row\User New user row. + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function updatePassword($request) + { + throw new AuthException( + 'Account password updating not supported by ' . get_class($this) + ); + } + /** * Get the URL to establish a session (needed when the internal VuFind login * form is inadequate). Returns false when no session initiator is needed. @@ -184,6 +201,17 @@ abstract class AbstractBase implements \VuFind\Db\Table\DbTableAwareInterface return false; } + /** + * Does this authentication method support password changing + * + * @return bool + */ + public function supportsPasswordChange() + { + // By default, password changing is not supported. + return false; + } + /** * Does the class allow for authentication from more than one auth method? * If so return an array that lists the classes for the methods allowed. diff --git a/module/VuFind/src/VuFind/Auth/Database.php b/module/VuFind/src/VuFind/Auth/Database.php index 4938747b6d140ec40531123234c9b32a98affe09..cbd231fd56b19af1257d68f8d03e7a99062c7c85 100644 --- a/module/VuFind/src/VuFind/Auth/Database.php +++ b/module/VuFind/src/VuFind/Auth/Database.php @@ -119,18 +119,8 @@ class Database extends AbstractBase } // Validate Input - // Needs a username - if (trim($params['username']) == '') { - throw new AuthException('Username cannot be blank'); - } - // Needs a password - if (trim($params['password']) == '') { - throw new AuthException('Password cannot be blank'); - } - // Passwords don't match - if ($params['password'] != $params['password2']) { - throw new AuthException('Passwords do not match'); - } + $this->validateUsernameAndPassword($params); + // Invalid Email Check $validator = new \Zend\Validator\EmailAddress(); if (!$validator->isValid($params['email'])) { @@ -170,6 +160,64 @@ class Database extends AbstractBase return $table->getByUsername($params['username'], false); } + /** + * Update a user's password from the request. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object containing + * new account details. + * + * @throws AuthException + * @return \VuFind\Db\Row\User New user row. + */ + public function updatePassword($request) + { + // Ensure that all expected parameters are populated to avoid notices + // in the code below. + $params = array( + 'username' => '', 'password' => '', 'password2' => '' + ); + foreach ($params as $param => $default) { + $params[$param] = $request->getPost()->get($param, $default); + } + + // Validate Input + $this->validateUsernameAndPassword($params); + + // Create the row and send it back to the caller: + $table = $this->getUserTable(); + $user = $table->getByUsername($params['username'], false); + if ($this->passwordHashingEnabled()) { + $bcrypt = new Bcrypt(); + $user->pass_hash = $bcrypt->create($params['password']); + } else { + $user->password = $params['password']; + } + $user->save(); + return $user; + } + + /** + * Make sure username and password aren't blank + * Make sure passwords match + * + * @param array $params + */ + protected function validateUsernameAndPassword($params) + { + // Needs a username + if (trim($params['username']) == '') { + throw new AuthException('Username cannot be blank'); + } + // Needs a password + if (trim($params['password']) == '') { + throw new AuthException('Password cannot be blank'); + } + // Passwords don't match + if ($params['password'] != $params['password2']) { + throw new AuthException('Passwords do not match'); + } + } + /** * Check that the user's password matches the provided value. * @@ -240,4 +288,14 @@ class Database extends AbstractBase { return true; } + + /** + * Does this authentication method support password changing + * + * @return bool + */ + public function supportsPasswordChange() + { + return true; + } } \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Auth/Manager.php b/module/VuFind/src/VuFind/Auth/Manager.php index b27a2968e2cd22d6e37953df1a22d120714ae0f6..f217c64488fdf6481c58149784fd31d14b34406e 100644 --- a/module/VuFind/src/VuFind/Auth/Manager.php +++ b/module/VuFind/src/VuFind/Auth/Manager.php @@ -156,7 +156,7 @@ class Manager implements ServiceLocatorAwareInterface /** * Does the current configuration support account creation? * - *@param string $authMethod optional; check this auth method rather than + * @param string $authMethod optional; check this auth method rather than * the one in config file * * @return bool @@ -169,6 +169,43 @@ class Manager implements ServiceLocatorAwareInterface return $this->getAuth()->supportsCreation(); } + /** + * Does the current configuration support password recovery? + * + * @param string $authMethod optional; check this auth method rather than + * the one in config file + * + * @return bool + */ + public function supportsRecovery($authMethod=null) + { + if ($authMethod != null) { + $this->setActiveAuthClass($authMethod); + } + if ($this->getAuth()->supportsPasswordChange()) { + return isset($this->config->Authentication->recover_password) + && $this->config->Authentication->recover_password; + } + return false; + } + + /** + * Is new passwords currently allowed? + * + * @return bool + */ + public function supportsPasswordChange($authMethod=null) + { + if ($authMethod != null) { + $this->setActiveAuthClass($authMethod); + } + if ($this->getAuth()->supportsPasswordChange()) { + return isset($this->config->Authentication->change_password) + && $this->config->Authentication->change_password; + } + return false; + } + /** * Get the URL to establish a session (needed when the internal VuFind login * form is inadequate). Returns false when no session initiator is needed. @@ -385,6 +422,22 @@ class Manager implements ServiceLocatorAwareInterface return $user; } + /** + * Update a user's password from the request. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object containing + * new account details. + * + * @throws AuthException + * @return \VuFind\Db\Row\User New user row. + */ + public function updatePassword($request) + { + $user = $this->getAuth()->updatePassword($request); + $this->updateSession($user); + return $user; + } + /** * Try to log in the user using current query parameters; return User object * on success, throws exception on failure. @@ -528,5 +581,4 @@ class Manager implements ServiceLocatorAwareInterface $this->authToProxy = $method; $this->authProxied = false; } - } diff --git a/module/VuFind/src/VuFind/Controller/MyResearchController.php b/module/VuFind/src/VuFind/Controller/MyResearchController.php index 3713c052146877c09869ac2a0631daa07ac8b9d7..2fcfe668c94461c51fb56e64a5c2a7c1bfd2be61 100644 --- a/module/VuFind/src/VuFind/Controller/MyResearchController.php +++ b/module/VuFind/src/VuFind/Controller/MyResearchController.php @@ -28,6 +28,7 @@ namespace VuFind\Controller; use VuFind\Exception\Auth as AuthException, + VuFind\Exception\Mail as MailException, VuFind\Exception\ListPermission as ListPermissionException, VuFind\Exception\RecordMissing as RecordMissingException, Zend\Stdlib\Parameters; @@ -1109,4 +1110,249 @@ class MyResearchController extends AbstractBase $url = $this->getServerUrl('myresearch-home'); return $this->getAuthManager()->getSessionInitiator($url); } + + /** + * Send account recovery email + * + * @return View object + */ + public function recoverAction() + { + // Make sure we're configured to do this + if (!$this->getAuthManager()->supportsRecovery()) { + $this->flashMessenger()->setNamespace('error') + ->addMessage('recovery_disabled'); + return $this->redirect()->toRoute('myresearch-home'); + } + if ($this->getUser()) { + return $this->redirect()->toRoute('myresearch-home'); + } + // Database + $table = $this->getTable('User'); + $user = false; + // Check if we have a submitted form, and use the information to get + // the user's information + if ($email = $this->params()->fromPost('email')) { + $user = $table->getByEmail($email); + } elseif ($username = $this->params()->fromPost('username')) { + $user = $table->getByUsername($username, false); + } + // If we have a submitted form + if (false != $user) { + $this->sendRecoveryEmail($user, $this->getConfig()); + } else if ($this->params()->fromPost('submit')) { + $this->flashMessenger()->setNamespace('error') + ->addMessage('recovery_user_not_found'); + } + return $this->createViewModel(); + } + + /** + * Helper function for recoverAction + * + * @param \VuFind\Db\Row\User $user User object we're recovering + * @param \VuFind\Config $config Configuration object + * + * @return void (sends email or adds error message) + */ + protected function sendRecoveryEmail($user, $config) + { + // If we can't find a user + if (null == $user) { + $this->flashMessenger()->setNamespace('error') + ->addMessage('recovery_user_not_found'); + } else { + // Make sure we've waiting long enough + $hashtime = $this->getHashAge($user->verify_hash); + $recoveryInterval = isset($config->Authentication->recover_interval) + ? $config->Authentication->recover_interval + : 60; + if (time()-$hashtime < $recoveryInterval) { + $this->flashMessenger()->setNamespace('error') + ->addMessage('recovery_too_soon'); + } else { + // Attempt to send the email + try { + // Create a fresh hash + $user->updateHash(); + $config = $this->getConfig(); + $renderer = $this->getViewRenderer(); + // Custom template for emails (text-only) + $message = $renderer->render( + 'Email/recover-password.phtml', + array( + 'library' => $config->Site->title, + 'url' => $this->getServerUrl('myresearch-verify') + . '?hash=' + . $user->verify_hash + ) + ); + $this->getServiceLocator()->get('VuFind\Mailer')->send( + $user->email, + $config->Site->email, + $this->translate('recovery_email_subject'), + $message + ); + $this->flashMessenger()->setNamespace('info') + ->addMessage('recovery_email_sent'); + } catch (MailException $e) { + $this->flashMessenger()->setNamespace('error') + ->addMessage($e->getMessage()); + } + } + } + } + + /** + * Receive a hash and display the new password form if it's valid + * + * @return view + */ + public function verifyAction() + { + // If we have a submitted form + $hash = $this->params()->fromQuery('hash'); + // Submitted form + if (null != $hash) { + $hashtime = $this->getHashAge($hash); + $config = $this->getConfig(); + // Check if hash is expired + $hashLifetime = isset($config->Authentication->recover_hash_lifetime) + ? $config->Authentication->recover_hash_lifetime + : 1209600; // Two weeks + if (time()-$hashtime > $hashLifetime) { + $this->flashMessenger()->setNamespace('error') + ->addMessage('recovery_expired_hash'); + return $this->forwardTo('MyResearch', 'Login'); + } else { + $table = $this->getTable('User'); + $user = $table->getByVerifyHash($hash); + // If the hash is valid, forward user to create new password + if (null != $user) { + $view = $this->createViewModel(); + $view->hash = $hash; + $view->username = $user->username; + $view->setTemplate('myresearch/newpassword'); + return $view; + } + } + } + $this->flashMessenger()->setNamespace('error') + ->addMessage('recovery_invalid_hash'); + return $this->forwardTo('MyResearch', 'Login'); + } + + /** + * Handling submission of a new password for a user. + * + * @return view + */ + public function newPasswordAction() + { + // Have we submitted the form? + if (!$this->params()->fromPost('submit', false)) { + return $this->redirect()->toRoute('home'); + } + // Pull in from POST + $request = $this->getRequest(); + $post = $request->getPost(); + // Verify hash + $userFromHash = isset($post->hash) + ? $this->getTable('User')->getByVerifyHash($post->hash) + : false; + // Missing or invalid hash + if (false == $userFromHash) { + $this->flashMessenger()->setNamespace('error') + ->addMessage('recovery_user_not_found'); + // Force login or restore hash + return $this->forwardTo('MyResearch', 'ChangePassword'); + } else if ($userFromHash->username !== $post->username) { + $this->flashMessenger()->setNamespace('error') + ->addMessage('authentication_error_invalid'); + $userFromHash->updateHash(); + $post->username = $userFromHash->username; + $post->hash = $userFromHash->verify_hash; + return $this->createViewModel($post); + } + // Verify old password if we're logged in + if ($this->getUser()) { + if (isset($post->oldpwd)) { + try { + // Reassign oldpwd to password in the request so login works + $temp_password = $post->password; + $post->password = $post->oldpwd; + $authClass = $this->getAuthManager()->login($request); + $post->password = $temp_password; + } catch(AuthException $e) { + $this->flashMessenger()->setNamespace('error') + ->addMessage($e->getMessage()); + return $this->createViewModel( + $this->params()->fromPost() + ); + } + } else { + $this->flashMessenger()->setNamespace('error') + ->addMessage('authentication_error_invalid'); + $post['verifyold'] = true; + return $this->createViewModel($post); + } + } + // Update password + try { + $user = $this->getAuthManager()->updatePassword($this->getRequest()); + } catch (AuthException $e) { + $this->flashMessenger()->setNamespace('error') + ->addMessage($e->getMessage()); + return $this->createViewModel( + $this->params()->fromPost() + ); + } + // Update hash to prevent reusing hash + $user->updateHash(); + // Login + $this->getAuthManager()->login($this->request); + // Go to favorites + $this->flashMessenger()->setNamespace('info') + ->addMessage('new_password_success'); + return $this->redirect()->toRoute('myresearch-home'); + } + + /** + * Handling submission of a new password for a user. + * + * @return view + */ + public function changePasswordAction() + { + if (!$this->getAuthManager()->isLoggedIn()) { + return $this->forceLogin(); + } + // If not submitted, are we logged in? + if (!$this->getAuthManager()->supportsPasswordChange()) { + $this->flashMessenger()->setNamespace('error') + ->addMessage('recovery_new_disabled'); + return $this->redirect()->toRoute('home'); + } + $view = $this->createViewModel($this->params()->fromPost()); + // Verify user password + $view->verifyold = true; + // Display username + $user = $this->getUser(); + $view->username = $user->username; + // Identification + $user->updateHash(); + $view->hash = $user->verify_hash; + $view->setTemplate('myresearch/newpassword'); + return $view; + } + + /** + * Helper function for verification hashes + * + * @return int age in seconds + */ + protected function getHashAge($hash) + { + return intval(substr($hash, -10)); + } } diff --git a/module/VuFind/src/VuFind/Db/Row/User.php b/module/VuFind/src/VuFind/Db/Row/User.php index dbc1d5bc4f5f4837ca47788f8103a8fec6dd0369..aac4c06cc44f8e467810e4094bf3b611de86fd68 100644 --- a/module/VuFind/src/VuFind/Db/Row/User.php +++ b/module/VuFind/src/VuFind/Db/Row/User.php @@ -406,4 +406,17 @@ class User extends ServiceLocatorAwareGateway // Remove the user itself: return parent::delete(); } + + /** + * Update the verification hash for this user + * + * @return bool save success + */ + public function updateHash() + { + $this->verify_hash = md5( + $this->username . $this->password . $this->pass_hash . rand() + ) . (time() % pow(10,10)); + return $this->save(); + } } diff --git a/module/VuFind/src/VuFind/Db/Table/User.php b/module/VuFind/src/VuFind/Db/Table/User.php index 6174b4a2c585e9b3ecc0a706d0812067a7aff4c0..0f91d6a906d875ce78a027fb80091ad0a45b3e89 100644 --- a/module/VuFind/src/VuFind/Db/Table/User.php +++ b/module/VuFind/src/VuFind/Db/Table/User.php @@ -93,4 +93,15 @@ class User extends Gateway }; return $this->select($callback); } + + /** + * Return a row by a verification hash + * + * @return mixed + */ + public function getByVerifyHash($hash) + { + $row = $this->select(array('verify_hash' => $hash))->current(); + return $row; + } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Auth.php b/module/VuFind/src/VuFind/View/Helper/Root/Auth.php index 9c852d6632c9b0074c43fcf952e9d56bc3498cdb..f2286308dea3d704338ac88c31f6eeda517a1310 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Auth.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Auth.php @@ -227,4 +227,30 @@ class Auth extends \Zend\View\Helper\AbstractHelper $classParts = explode('\\', $className); return array_pop($classParts); } + + + /** + * Render the new password form template. + * + * @param array $context Context for rendering template + * + * @return string + */ + public function getNewPasswordForm($context = array()) + { + return $this->renderTemplate('newpassword.phtml', $context); + } + + + /** + * Render the password recovery form template. + * + * @param array $context Context for rendering template + * + * @return string + */ + public function getPasswordRecoveryForm($context = array()) + { + return $this->renderTemplate('recovery.phtml', $context); + } } diff --git a/themes/blueprint/css/styles.css b/themes/blueprint/css/styles.css index 49f518ec79a3d75c7b672c00534b1de647ef78f0..af0baeb04dee26874de3168a33ad464a19b12082 100644 --- a/themes/blueprint/css/styles.css +++ b/themes/blueprint/css/styles.css @@ -647,6 +647,18 @@ input.bookbagEmpty { background-position: left; padding:.5em .5em .5em 20px; } +.forgot_password { + background-image:url(../images/silk/key_go.png); + background-repeat:no-repeat; + background-position: left; + padding:.5em .5em .5em 20px; +} +.lock { + background-image:url(../images/silk/lock.png); + background-repeat:no-repeat; + background-position: left; + padding:.5em .5em .5em 20px; +} .cite { background-image:url(../images/silk/report.png); background-repeat:no-repeat; @@ -724,6 +736,13 @@ input.bookbagEmpty { padding:.5em .5em .5em 20px; /*margin-left:1em;*/ } +.gear { + background-image:url(../images/silk/cog.png); + background-repeat:no-repeat; + background-position: left; + padding:.6em .5em .5em 20px; + /*margin-left:1em;*/ +} h3.list { padding-bottom:0; margin-bottom:0; diff --git a/themes/blueprint/images/silk/cog.png b/themes/blueprint/images/silk/cog.png new file mode 100644 index 0000000000000000000000000000000000000000..67de2c6ccbeac17742f56cf7391e72b2bf5033ba Binary files /dev/null and b/themes/blueprint/images/silk/cog.png differ diff --git a/themes/blueprint/images/silk/key_go.png b/themes/blueprint/images/silk/key_go.png new file mode 100644 index 0000000000000000000000000000000000000000..30b0dc316e52dba388d88112d4c1cc32672fffbb Binary files /dev/null and b/themes/blueprint/images/silk/key_go.png differ diff --git a/themes/blueprint/images/silk/lock.png b/themes/blueprint/images/silk/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..2ebc4f6f9663e32cad77d67ef93ab8843dfea3c0 Binary files /dev/null and b/themes/blueprint/images/silk/lock.png differ diff --git a/themes/blueprint/templates/Auth/AbstractBase/login.phtml b/themes/blueprint/templates/Auth/AbstractBase/login.phtml index 30df536ea06d17c0433d8454a161279ce60d6844..ee38214e7d5fb45287a29500cbc40cf63d1d4f87 100644 --- a/themes/blueprint/templates/Auth/AbstractBase/login.phtml +++ b/themes/blueprint/templates/Auth/AbstractBase/login.phtml @@ -15,6 +15,9 @@ <? if ($account->supportsCreation()): ?> <a class="new_account" href="<?=$this->url('myresearch-account')?>?auth_method=<?=$this->auth()->getActiveAuthMethod()?>"><?=$this->transEsc('Create New Account')?></a> <? endif; ?> + <? if ($account->supportsRecovery()): ?> + <a class="forgot_password" href="<?=$this->url('myresearch-recover')?>"><?=$this->transEsc('Forgot Password')?></a> + <? endif; ?> <? else: ?> <a href="<?=$this->escapeHtml($sessionInitiator)?>"><?=$this->transEsc("Institutional Login")?></a> <? endif; ?> diff --git a/themes/blueprint/templates/Auth/Database/newpassword.phtml b/themes/blueprint/templates/Auth/Database/newpassword.phtml new file mode 100644 index 0000000000000000000000000000000000000000..a5c74c252e178fbb69b468a89fe5016f295c4a4e --- /dev/null +++ b/themes/blueprint/templates/Auth/Database/newpassword.phtml @@ -0,0 +1,12 @@ +<? if (isset($this->username)): ?> + <label class="span-4"><?=$this->transEsc('Username') ?>:</label> + <input type="text" disabled value="<?=$this->username ?>"/><br/> +<? endif; ?> +<? if (isset($this->verifyold) && $this->verifyold || isset($this->oldpwd)): ?> + <label class="span-4"><?=$this->transEsc('old_password') ?>:</label> + <input type="password" name="oldpwd"/><br/> +<? endif; ?> +<label class="span-4"><?=$this->transEsc('new_password') ?>:</label> +<input type="password" name="password"/><br/> +<label class="span-4"><?=$this->transEsc('confirm_new_password') ?>:</label> +<input type="password" name="password2"/><br/> \ No newline at end of file diff --git a/themes/blueprint/templates/Auth/Database/recovery.phtml b/themes/blueprint/templates/Auth/Database/recovery.phtml new file mode 100644 index 0000000000000000000000000000000000000000..359cc4f965d8a5d340ba676e87289e66cc1b8047 --- /dev/null +++ b/themes/blueprint/templates/Auth/Database/recovery.phtml @@ -0,0 +1,7 @@ +<label class="span-4"><?=$this->transEsc('recovery_by_username') ?>:</label> +<input type="text" name="username"/> +<input type="submit" name="submit"/> +<br/><br/> +<label class="span-4"><?=$this->transEsc('recovery_by_email') ?>:</label> +<input type="email" name="email"/> +<input type="submit" name="submit"/> \ No newline at end of file diff --git a/themes/blueprint/templates/myresearch/menu.phtml b/themes/blueprint/templates/myresearch/menu.phtml index 2c316aadcd8c8ae9ba4b999c109cedf3f71993cc..a1b6c496e0d8f43b43643d182f32782bc1e242af 100644 --- a/themes/blueprint/templates/myresearch/menu.phtml +++ b/themes/blueprint/templates/myresearch/menu.phtml @@ -18,6 +18,14 @@ <? endif; ?> <li<?=$this->active == 'history' ? ' class="active"' : ''?>><a href="<?=$this->url('search-history')?>?require_login"><?=$this->transEsc('history_saved_searches')?></a></li> </ul> + <? if ($this->auth()->isLoggedIn() && $this->auth()->getManager()->supportsPasswordChange()): ?> + <h4 class="gear"><?=$this->transEsc('Preferences')?></h4> + <ul> + <li> + <a href="<?=$this->url('myresearch-changepassword') ?>"><?=$this->transEsc('Change Password') ?></a> + </li> + </ul> + <? endif; ?> <? if ($this->userlist()->getMode() !== 'disabled' && $user = $this->auth()->isLoggedIn()): ?> <h4 class="list"><?=$this->transEsc('Your Lists')?></h4> <ul> diff --git a/themes/blueprint/templates/myresearch/newpassword.phtml b/themes/blueprint/templates/myresearch/newpassword.phtml new file mode 100644 index 0000000000000000000000000000000000000000..41741c051c3263bc35bb895d649a149f8acea6c4 --- /dev/null +++ b/themes/blueprint/templates/myresearch/newpassword.phtml @@ -0,0 +1,22 @@ +<div class="<?=$this->layoutClass('mainbody')?>"> + <h2><?=$this->transEsc('Create New Password') ?></h2> + <?=$this->flashmessages() ?> + <? if (!$this->auth()->getManager()->supportsPasswordChange()): ?> + <div class="error"><?=$this->transEsc('recovery_new_disabled') ?></div> + <? elseif (!isset($this->hash)): ?> + <div class="error"><?=$this->transEsc('recovery_user_not_found') ?></div> + <? else: ?> + <form action="<?=$this->url('myresearch-newpassword') ?>" method="post"> + <?=$this->auth()->getNewPasswordForm() ?> + <input type="hidden" value="<?=$this->hash ?>" name="hash"/> + <input type="hidden" value="<?=$this->username ?>" name="username"/> + <input name="submit" type="submit"/> + </form> + <? endif; ?> +</div> + +<? if ($this->auth()->isLoggedIn()): ?> + <div class="<?=$this->layoutClass('sidebar')?>"> + <?=$this->context($this)->renderInContext("myresearch/menu.phtml", array('active' => 'newpassword'))?> + </div> +<? endif; ?> \ No newline at end of file diff --git a/themes/blueprint/templates/myresearch/recover.phtml b/themes/blueprint/templates/myresearch/recover.phtml new file mode 100644 index 0000000000000000000000000000000000000000..afffcc651e88535a3eadd9a091932a824b9e444b --- /dev/null +++ b/themes/blueprint/templates/myresearch/recover.phtml @@ -0,0 +1,9 @@ +<h2><?=$this->transEsc('Password Recovery') ?></h2> +<?=$this->flashmessages()?> +<? if (!$this->auth()->getManager()->supportsRecovery()): ?> + <div class="error"><?=$this->transEsc('recovery_disabled') ?></div> +<? else: ?> + <form action="" method="post"> + <?=$this->auth()->getPasswordRecoveryForm() ?> + </form> +<? endif; ?> \ No newline at end of file diff --git a/themes/bootprint/css/icons.css b/themes/bootprint/css/icons.css index 29565471d615f2925f4dd6230fe9790182da5c1c..d5b276b922f45b2e693e8b0f79921c8823b6d7a5 100644 --- a/themes/bootprint/css/icons.css +++ b/themes/bootprint/css/icons.css @@ -23,6 +23,7 @@ i.icon-home, i.icon-inbox, i.icon-leaf, i.icon-list-alt, +i.icon-lock, i.icon-minus-sign, i.icon-ok, i.icon-phone-sign, @@ -102,6 +103,7 @@ i.icon-user { .small i.icon-inbox, .small i.icon-leaf, .small i.icon-list-alt, +.small i.icon-lock, .small i.icon-minus-sign, .small i.icon-ok, .small i.icon-phone-sign, @@ -154,6 +156,7 @@ i.icon-home {background:url('../images/icons/house.png')} i.icon-inbox {background:url('../images/icons/box.png')} i.icon-leaf,.icon-sitemap {background:url('../images/icons/treeCurrent.png')} i.icon-list-alt,i.icon-export {background:url('../images/icons/application_add.png')} +i.icon-lock {background:url('../images/icons/lock.png')} i.icon-minus-sign {background:url('../images/icons/delete.png')} i.icon-ok {background:url('../images/icons/tick.png')} i.icon-phone-sign {background:url('../images/icons/phone.png')} diff --git a/themes/bootprint/images/icons/cog.png b/themes/bootprint/images/icons/cog.png new file mode 100644 index 0000000000000000000000000000000000000000..67de2c6ccbeac17742f56cf7391e72b2bf5033ba Binary files /dev/null and b/themes/bootprint/images/icons/cog.png differ diff --git a/themes/bootprint/images/icons/key_go.png b/themes/bootprint/images/icons/key_go.png new file mode 100644 index 0000000000000000000000000000000000000000..30b0dc316e52dba388d88112d4c1cc32672fffbb Binary files /dev/null and b/themes/bootprint/images/icons/key_go.png differ diff --git a/themes/bootprint/images/icons/lock.png b/themes/bootprint/images/icons/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..2ebc4f6f9663e32cad77d67ef93ab8843dfea3c0 Binary files /dev/null and b/themes/bootprint/images/icons/lock.png differ diff --git a/themes/bootstrap/templates/Auth/AbstractBase/login.phtml b/themes/bootstrap/templates/Auth/AbstractBase/login.phtml index 1bee344cd8375daf555bf8589596ec927d91c124..539c1e484b9ccbae3d9e959e8cd1e3df42858ba7 100644 --- a/themes/bootstrap/templates/Auth/AbstractBase/login.phtml +++ b/themes/bootstrap/templates/Auth/AbstractBase/login.phtml @@ -6,10 +6,14 @@ <input type="hidden" name="auth_method" value="<?=$this->auth()->getActiveAuthMethod()?>"> <div class="control-group"> <div class="controls"> - <input class="btn btn-primary" type="submit" name="processLogin" value="Login"> <? if ($account->supportsCreation()): ?> <a class="btn btn-link createAccountLink" href="<?=$this->url('myresearch-account') ?>"><?=$this->transEsc('Create New Account')?></a> <? endif; ?> + <input class="btn btn-primary" type="submit" name="processLogin" value="Login"> + <? if ($account->supportsRecovery()): ?> + <br/> + <a class="btn btn-link" href="<?=$this->url('myresearch-recover') ?>"><?=$this->transEsc('Forgot Password')?></a> + <? endif; ?> </div> </div> </form> diff --git a/themes/bootstrap/templates/Auth/Database/newpassword.phtml b/themes/bootstrap/templates/Auth/Database/newpassword.phtml new file mode 100644 index 0000000000000000000000000000000000000000..40189de011f383b1c28ec9c6446658b2318878a2 --- /dev/null +++ b/themes/bootstrap/templates/Auth/Database/newpassword.phtml @@ -0,0 +1,28 @@ +<? if (isset($this->username)): ?> + <div class="control-group"> + <label class="control-label"><?=$this->transEsc('Username') ?>:</label> + <div class="controls"> + <span class="input-xlarge uneditable-input"><?=$this->username ?></span> + </div> + </div> +<? endif; ?> +<? if (isset($this->verifyold) && $this->verifyold || isset($this->oldpwd)): ?> + <div class="control-group"> + <label class="control-label"><?=$this->transEsc('old_password') ?>:</label> + <div class="controls"> + <input type="password" name="oldpwd"/> + </div> + </div> +<? endif; ?> +<div class="control-group"> + <label class="control-label"><?=$this->transEsc('new_password') ?>:</label> + <div class="controls"> + <input type="password" name="password"/> + </div> +</div> +<div class="control-group"> + <label class="control-label"><?=$this->transEsc('confirm_new_password') ?>:</label> + <div class="controls"> + <input type="password" name="password2"/> + </div> +</div> \ No newline at end of file diff --git a/themes/bootstrap/templates/Auth/Database/recovery.phtml b/themes/bootstrap/templates/Auth/Database/recovery.phtml new file mode 100644 index 0000000000000000000000000000000000000000..56355213823b4194d598774f275d61217df5b281 --- /dev/null +++ b/themes/bootstrap/templates/Auth/Database/recovery.phtml @@ -0,0 +1,14 @@ +<div class="control-group"> + <label class="control-label"><?=$this->transEsc('recovery_by_username') ?>:</label> + <div class="controls"> + <input type="text" name="username"/> + <input class="btn" name="submit" type="submit"/> + </div> +</div> +<div class="control-group"> + <label class="control-label"><?=$this->transEsc('recovery_by_email') ?>:</label> + <div class="controls"> + <input type="email" name="email"/> + <input class="btn" name="submit" type="submit"/> + </div> +</div> \ No newline at end of file diff --git a/themes/bootstrap/templates/myresearch/menu.phtml b/themes/bootstrap/templates/myresearch/menu.phtml index 157ac8fae80c92507447bf61541a3a4ad7230aa6..da74118efaad5dc95813ceafd7cecc59b1939f94 100644 --- a/themes/bootstrap/templates/myresearch/menu.phtml +++ b/themes/bootstrap/templates/myresearch/menu.phtml @@ -20,14 +20,20 @@ <li><a href="<?=$this->url('myresearch-logout')?>"><?=$this->transEsc("Log Out")?> <i class="icon-signout pull-right"></i></a></li> <? endif; ?> </ul> - <? if ($this->userlist()->getMode() !== 'disabled' && $user = $this->auth()->isLoggedIn()): ?> - <h4 class="list"><?=$this->transEsc('Your Lists')?></h4> - <ul class="nav nav-list"> - <li<?=$this->active == 'favorites' ? ' class="active"' : ''?>><a href="<?=$this->url('myresearch-favorites')?>"><?=$this->transEsc('Your Favorites')?> <i class="icon-star pull-right"></i></a></li> - <? $lists = $user->getLists() ?> - <? foreach ($lists as $list): ?> - <li<?=$this->active == 'list' . $list['id'] ? ' class="active"' : ''?>> <a href="<?=$this->url('userList', array('id' => $list['id']))?>"><?=$this->escapeHtml($list['title'])?> <span class="pull-right"><?=$list->cnt?></span></a></li> - <? endforeach; ?> - <li><a href="<?=$this->url('editList', array('id'=>'NEW'))?>" title="<?=$this->transEsc('Create a List') ?>"><?=$this->transEsc('Create a List') ?> <span class="pull-right"><i class="icon-plus"></i></span></a></li> - </ul> - <? endif ?> +<? if ($this->auth()->isLoggedIn() && $this->auth()->getManager()->supportsPasswordChange()): ?> + <h4><?=$this->transEsc('Preferences')?></h4> + <ul class="nav nav-list"> + <li<?=$this->active == 'newpassword' ? ' class="active"' : ''?>><a href="<?=$this->url('myresearch-changepassword') ?>"><?=$this->transEsc('Change Password') ?> <i class="icon-lock pull-right"></i></a></li> + </ul> +<? endif; ?> +<? if ($this->userlist()->getMode() !== 'disabled' && $user = $this->auth()->isLoggedIn()): ?> + <h4><?=$this->transEsc('Your Lists')?></h4> + <ul class="nav nav-list"> + <li<?=$this->active == 'favorites' ? ' class="active"' : ''?>><a href="<?=$this->url('myresearch-favorites')?>"><?=$this->transEsc('Your Favorites')?> <i class="icon-star pull-right"></i></a></li> + <? $lists = $user->getLists() ?> + <? foreach ($lists as $list): ?> + <li<?=$this->active == 'list' . $list['id'] ? ' class="active"' : ''?>> <a href="<?=$this->url('userList', array('id' => $list['id']))?>"><?=$this->escapeHtml($list['title'])?> <span class="pull-right"><?=$list->cnt?></span></a></li> + <? endforeach; ?> + <li><a href="<?=$this->url('editList', array('id'=>'NEW'))?>" title="<?=$this->transEsc('Create a List') ?>"><?=$this->transEsc('Create a List') ?> <span class="pull-right"><i class="icon-plus"></i></span></a></li> + </ul> +<? endif ?> diff --git a/themes/bootstrap/templates/myresearch/newpassword.phtml b/themes/bootstrap/templates/myresearch/newpassword.phtml new file mode 100644 index 0000000000000000000000000000000000000000..74951da80b7e5c2a687918f7286b3b8be56e2a58 --- /dev/null +++ b/themes/bootstrap/templates/myresearch/newpassword.phtml @@ -0,0 +1,26 @@ +<div class="<?=$this->layoutClass('mainbody')?>"> + <h2><?=$this->transEsc('Create New Password') ?></h2> + <?=$this->flashmessages() ?> + <? if (!$this->auth()->getManager()->supportsPasswordChange()): ?> + <div class="error"><?=$this->transEsc('recovery_new_disabled') ?></div> + <? elseif (!isset($this->hash)): ?> + <div class="error"><?=$this->transEsc('recovery_user_not_found') ?></div> + <? else: ?> + <form class="form-horizontal" action="<?=$this->url('myresearch-newpassword') ?>" method="post"> + <input type="hidden" value="<?=$this->hash ?>" name="hash"/> + <input type="hidden" value="<?=$this->username ?>" name="username"/> + <?=$this->auth()->getNewPasswordForm() ?> + <div class="control-group"> + <div class="controls"> + <input class="btn" name="submit" type="submit"/> + </div> + </div> + </form> + <? endif; ?> +</div> + +<? if ($this->auth()->isLoggedIn()): ?> + <div class="<?=$this->layoutClass('sidebar')?>"> + <?=$this->context($this)->renderInContext("myresearch/menu.phtml", array('active' => 'newpassword'))?> + </div> +<? endif; ?> \ No newline at end of file diff --git a/themes/bootstrap/templates/myresearch/profile.phtml b/themes/bootstrap/templates/myresearch/profile.phtml index 989b5862ff34f39c95ad05adb658d2fe04a46d24..96dcb5cb94fe2683cbf7edc92ec45ac424e94845 100644 --- a/themes/bootstrap/templates/myresearch/profile.phtml +++ b/themes/bootstrap/templates/myresearch/profile.phtml @@ -33,7 +33,7 @@ ? $this->profile['home_library'] : $this->defaultPickupLocation ?> <td> - <form method="post" action="" id="profile_form"> + <form id="profile_form" class="form-inline" action="" method="post"> <select id="home_library" name="home_library"> <? foreach ($this->pickup as $lib): ?> <option value="<?=$this->escapeHtml($lib['locationID'])?>"<?=($selected == $lib['locationID'])?' selected="selected"':''?>><?=$this->escapeHtml($lib['locationDisplay'])?></option> diff --git a/themes/bootstrap/templates/myresearch/recover.phtml b/themes/bootstrap/templates/myresearch/recover.phtml new file mode 100644 index 0000000000000000000000000000000000000000..e2c25e006b1b415bc79441da7e6dd18063c59165 --- /dev/null +++ b/themes/bootstrap/templates/myresearch/recover.phtml @@ -0,0 +1,9 @@ +<h2><?=$this->transEsc('Password Recovery') ?></h2> +<?=$this->flashmessages()?> +<? if (!$this->auth()->getManager()->supportsRecovery()): ?> + <div class="error"><?=$this->transEsc('recovery_disabled') ?></div> +<? else: ?> + <form class="form-horizontal" action="" method="post"> + <?=$this->auth()->getPasswordRecoveryForm() ?> + </form> +<? endif; ?> \ No newline at end of file diff --git a/themes/jquerymobile/templates/Auth/AbstractBase/login.phtml b/themes/jquerymobile/templates/Auth/AbstractBase/login.phtml index e41194cc5283f1008fca6071241475d93c82f683..e7f616f8c31b1298e7c5565400bea73c2151ade2 100644 --- a/themes/jquerymobile/templates/Auth/AbstractBase/login.phtml +++ b/themes/jquerymobile/templates/Auth/AbstractBase/login.phtml @@ -15,6 +15,9 @@ <? if ($account->supportsCreation()): ?> <a rel="external" data-role="button" class="new_account" href="<?=$this->url('myresearch-account')?>?auth_method=<?=$this->auth()->getActiveAuthMethod()?>"><?=$this->transEsc('Create New Account')?></a> <? endif; ?> + <? if ($account->supportsRecovery()): ?> + <a rel="external" data-role="button" class="recover_password" href="<?=$this->url('myresearch-recover')?>"><?=$this->transEsc('Forgot Password')?></a> + <? endif; ?> <? else: ?> <a rel="external" data-role="button" href="<?=$this->escapeHtml($sessionInitiator)?>"><?=$this->transEsc("Institutional Login")?></a> <? endif; ?> diff --git a/themes/jquerymobile/templates/Auth/Database/newpassword.phtml b/themes/jquerymobile/templates/Auth/Database/newpassword.phtml new file mode 100644 index 0000000000000000000000000000000000000000..4c0b2d107fd35a6b3f22b57495d085c510a06728 --- /dev/null +++ b/themes/jquerymobile/templates/Auth/Database/newpassword.phtml @@ -0,0 +1,15 @@ +<div data-role="fieldcontain" class="ui-field-contain ui-body ui-br"> + <? if (isset($this->username)): ?> + <input type="hidden" name="username" value="<?=$this->username ?>"/> + <label class="ui-input-text"><?=$this->transEsc('Username') ?>:</label> + <input type="text" name="username" id="username" value="<?=$this->username ?>" disabled class="ui-input-text ui-body-c ui-corner-all ui-shadow-inset" style="border:1px solid #CCC;box-shadow:rgba(0, 0, 0, 0.1) 0px 1px 4px 0px inset;color:#777"/><br/> + <? endif; ?> + <? if (isset($this->verifyold) && $this->verifyold || isset($this->oldpwd)): ?> + <label for="oldpwd" class="ui-input-text"><?=$this->transEsc('old_password') ?>:</label> + <input type="password" name="oldpwd" id="oldpwd" class="ui-input-text ui-body-c ui-corner-all ui-shadow-inset"/><br/> + <? endif; ?> + <label for="password" class="ui-input-text"><?=$this->transEsc('new_password') ?>:</label> + <input type="password" name="password" id="password" class="ui-input-text ui-body-c ui-corner-all ui-shadow-inset"/><br/> + <label for="password2" class="ui-input-text"><?=$this->transEsc('confirm_new_password') ?>:</label> + <input type="password" name="password2" id="password2" class="ui-input-text ui-body-c ui-corner-all ui-shadow-inset"/><br/> +</div> \ No newline at end of file diff --git a/themes/jquerymobile/templates/Auth/Database/recovery.phtml b/themes/jquerymobile/templates/Auth/Database/recovery.phtml new file mode 100644 index 0000000000000000000000000000000000000000..b1503fb4a54e0a63d35f25a239741d3d0f937c39 --- /dev/null +++ b/themes/jquerymobile/templates/Auth/Database/recovery.phtml @@ -0,0 +1,10 @@ +<div class="ui-grid-b"> + <div class="ui-block-a"><label><?=$this->transEsc('recovery_by_username') ?>:</label></div> + <div class="ui-block-b"><input type="text" name="username" style="margin-top:.5em;height:28px"/></div> + <div class="ui-block-c"><input type="submit" name="submit" value="<?=$this->transEsc('Submit') ?>"/></div> +</div> +<div class="ui-grid-b"> + <div class="ui-block-a"><label><?=$this->transEsc('recovery_by_email') ?>:</label></div> + <div class="ui-block-b"><input type="email" name="email" style="margin-top:.5em;height:28px"/></div> + <div class="ui-block-c"><input type="submit" name="submit" value="<?=$this->transEsc('Submit') ?>"/></div> +</div> \ No newline at end of file diff --git a/themes/jquerymobile/templates/myresearch/footer-navbar.phtml b/themes/jquerymobile/templates/myresearch/footer-navbar.phtml index aec903518b647116eca33792bf415bba9295ba5e..624e3b6788a5a3024eb42751c5c39749b073fe86 100644 --- a/themes/jquerymobile/templates/myresearch/footer-navbar.phtml +++ b/themes/jquerymobile/templates/myresearch/footer-navbar.phtml @@ -5,6 +5,9 @@ <li><a rel="external" <?=$this->layout()->templateName=="mylist" ? ' class="ui-btn-active"' : ''?> href="<?=$this->url('myresearch-favorites')?>"><?=$this->transEsc('Favorites')?></a></li> <? endif; ?> <li><a rel="external" <?=$this->layout()->templateName=="history" ? ' class="ui-btn-active"' : ''?> href="<?=$this->url('search-history')?>?require_login"><?=$this->transEsc('History')?></a></li> + <? if ($this->auth()->getManager()->supportsPasswordChange()): ?> + <li><a rel="external" href="<?=$this->url('myresearch-changepassword')?>"><?=$this->transEsc("Change Password")?></a></li> + <? endif; ?> <li><a rel="external" href="<?=$this->url('myresearch-logout')?>"><?=$this->transEsc("Log Out")?></a></li> </ul> </div> diff --git a/themes/jquerymobile/templates/myresearch/newpassword.phtml b/themes/jquerymobile/templates/myresearch/newpassword.phtml new file mode 100644 index 0000000000000000000000000000000000000000..9a8e1ecbaaaeaf17bd0f62db70fb8765a8112121 --- /dev/null +++ b/themes/jquerymobile/templates/myresearch/newpassword.phtml @@ -0,0 +1,29 @@ +<? + // Set up page title: + $this->headTitle(isset($list) ? $list->title : $this->translate('Create New Password')); + + // Set up extra button for header: + $extraButton = '<a rel="external" href="' + . $this->url('myresearch-home') + . '" data-icon="back" class="ui-btn-left">' + . $this->transEsc('My Profile') + . '</a>'; +?> +<div data-role="page" id="MyResearch-newpassword" class="newpassword"> + <?=$this->mobileMenu()->header(array('extraButtons'=>array($extraButton))) ?> + <div data-role="content"> + <?=$this->flashmessages() ?> + <? if (!$this->auth()->getManager()->supportsPasswordChange()): ?> + <div class="error"><?=$this->transEsc('recovery_new_disabled') ?></div> + <? elseif (!isset($this->hash)): ?> + <div class="error"><?=$this->transEsc('recovery_user_not_found') ?></div> + <? else: ?> + <form data-ajax="false" action="<?=$this->url('myresearch-newpassword') ?>" method="post"> + <?=$this->auth()->getNewPasswordForm() ?> + <input type="hidden" value="<?=$this->hash ?>" name="hash"/> + <input type="submit" name="submit" value="<?=$this->transEsc('Submit') ?>"/> + </form> + <? endif; ?> + </div> + <?=$this->mobileMenu()->footer() ?> +</div> \ No newline at end of file diff --git a/themes/jquerymobile/templates/myresearch/recover.phtml b/themes/jquerymobile/templates/myresearch/recover.phtml new file mode 100644 index 0000000000000000000000000000000000000000..4e73da0009ce325086927d9c778740206479a2be --- /dev/null +++ b/themes/jquerymobile/templates/myresearch/recover.phtml @@ -0,0 +1,27 @@ +<? + // Set up page title: + $this->headTitle(isset($list) ? $list->title : $this->translate('recovery_title')); + + // Set up extra button for header: + $extraButton = '<a rel="external" href="' + . $this->url('myresearch-home') + . '" data-icon="back" class="ui-btn-left">'; + $extraButton .= $this->auth()->isLoggedIn() + ? $this->transEsc('My Profile') + : $this->transEsc('Login'); + $extraButton .= '</a>'; +?> +<div data-role="page" id="MyResearch-recover" class="results-page"> + <?=$this->mobileMenu()->header(array('extraButtons'=>array($extraButton))) ?> + <div data-role="content"> + <?=$this->flashmessages()?> + <? if (!$this->auth()->getManager()->supportsRecovery()): ?> + <div class="error"><?=$this->transEsc('recovery_disabled') ?></div> + <? else: ?> + <form data-ajax="false" action="" method="post"> + <?=$this->auth()->getPasswordRecoveryForm() ?> + </form> + <? endif; ?> + </div> + <?=$this->mobileMenu()->footer() ?> +</div> \ No newline at end of file diff --git a/themes/root/templates/Email/recover-password.phtml b/themes/root/templates/Email/recover-password.phtml new file mode 100644 index 0000000000000000000000000000000000000000..6037d640e280c061414c309da769eb7cddef0989 --- /dev/null +++ b/themes/root/templates/Email/recover-password.phtml @@ -0,0 +1,3 @@ +<?=$this->translate('recovery_email_notification', array('%%library%%' => $this->library)) ?> + +<?=$this->translate('recovery_email_url_pretext', array('%%url%%' => $this->url)) ?> \ No newline at end of file