Newer
Older
* Copyright (C) 2019 Leipzig University Library
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* @category VuFind
* @package Controller
* @author Gregor Gawol <gawol@ub.uni-leipzig.de>
* @author Sebastian Kehr <kehr@ub.uni-leipzig.de>
* @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
* @link https://vufind.org/wiki/development Wiki
*/
namespace fid\Controller;
use fid\FormModel\PasswordChangeModel;
use fid\FormModel\PasswordResetModel;
use fid\FormModel\UsernameChangeModel;
use fid\Service\Client;
use fid\Service\ClientException;
use fid\Service\DataTransferObject\Library;
use fid\Service\DataTransferObject\Order;
use fid\Service\DataTransferObject\User;
use fid\Service\UserNotAuthorizedException;
use VuFind\Auth\Manager as AuthManager;
use VuFind\Controller\AbstractBase;
use VuFind\Exception\Auth as AuthException;
use Zend\Form\Element\Select;
use Zend\Form\Form;
use Zend\Http\PhpEnvironment\Request;
use Zend\Http\PhpEnvironment\Response as HttpResponse;
use Zend\Http\Response;
use Zend\Mvc\Plugin\FlashMessenger\FlashMessenger;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\View\Model\ViewModel;
/**
* User Controller
*
* @category VuFind
* @package Controller
* @author Gregor Gawol <gawol@ub.uni-leipzig.de>
* @author Sebastian Kehr <kehr@ub.uni-leipzig.de>
* @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
* @link https://vufind.org/wiki/development Wiki
*/
class UserController extends AbstractBase
{
/**
* @var AuthManager
protected $authManager;
* @var Client
*/
protected $client;
* @var array
*/
protected $config;
/**
* RegistrationController constructor.
*
* @param ServiceLocatorInterface $serviceLocator Service Locator
* @param AuthManager $authManager Authentication Manager
* @param Client $client fidis client
* @param array $config Configuration
*/
public function __construct(
ServiceLocatorInterface $serviceLocator,
AuthManager $authManager,
) {
parent::__construct($serviceLocator);
$this->authManager = $authManager;
$this->client = $client;
* Registration init (registration part 1) route action
* Starting registration process
*
*/
public function initAction()
{
/* @var Form $form */
/* @var Request $request */
$request = $this->getRequest();
$form = $this->serviceLocator->get('user-init-form');
$forwarded = $this->params()->fromRoute('forwarded', false);
if ($submitted = $this->formWasSubmitted()) {
$form->setData($request->getPost());
if (!$forwarded && $form->isValid()) {
return $this->init($form);
}
}
$action = $this->url()->fromRoute('fid/user/init');
$form->setAttribute('action', $action);
$form->prepare();
$view = $this->createViewModel();
$view->setVariables(compact('form'));
$view->setTemplate('fid/user/init');
return $view;
}
* Registration init (registration part 1) by requesting
* email registration link via fidis after succeded formular validation.
*
* @param Form $form Formular
*
* @return mixed|Response
*/
protected function init(Form $form)
{
$messenger = $this->getMessenger();
$user = $form->getHydrator()->hydrate($form->getData(), new User());
/* @noinspection PhpUndefinedFieldInspection */
$query['lng'] = $this->layout()->userLang;
$username = $query['username'] = $user->getUsername();
$firstname = $query['firstname'] = $user->getFirstname();
$lastname = $query['lastname'] = $user->getLastname();
$baseUrl = $this->url()->fromRoute(
'fid/user/create',

Alexander Purr
committed
[],
['query' => $query, 'force_canonical' => true]
$this->client->requestRegistrationLink(

Alexander Purr
committed
$baseUrl,
$username,
$firstname,
$lastname
} catch (ClientException $exception) {
$message = $exception->getCode() === 400
? 'fid::user_init_error_username'
: 'fid::user_init_error';
$messenger->addErrorMessage($this->translate($message));

Alexander Purr
committed
self::class,
[
'action' => 'init',
'forwarded' => true
}
$message = $this->translate('fid::user_init_success');
$messenger->addSuccessMessage(sprintf($message, $username));
return $this->redirect()->toRoute('myresearch-home');
}
/**
* Get flash messenger
*
* @return FlashMessenger
*/
protected function getMessenger(): FlashMessenger
{
/* @noinspection PhpUndefinedMethodInspection */
/* @var FlashMessenger $messenger */
return $this->flashMessenger();
}
* Create new user (registration part 2) route action.
* Finish registration process.
* Action is triggert by link receved by email
*
*/
public function createAction()
{
/* @var Form $form */
/* @var Request $request */
/* @var Select $homeLibraryElement */
$request = $this->getRequest();
$query = $request->getQuery();
$messenger = $this->getMessenger();
if ($credentials = $query->get('logon')) {
try {
$this->client->logon($credentials);
} catch (ClientException $exception) {
$message = $exception->getCode() === 401
? 'fid::user_create_error_expired'
: 'fid::user_create_error';
$messenger->addErrorMessage($this->translate($message));
return $this->redirect()->toRoute('fid/user/init');
}
$query->offsetUnset('logon');

Alexander Purr
committed
'fid/user/create',
[],
[
'query' => $query->toArray()
$libraries = array_map(
function (Library $libary) {
return $libary->getLabel();

Alexander Purr
committed
},
$this->client->requestLibraryList()
} catch (ClientException $exception) {
$message = 'fid::user_create_error';
$messenger->addErrorMessage($this->translate($message));
return $this->redirect()->toRoute('fid/user/init');
}
$form = $this->serviceLocator->get('user-create-form');
$homeLibraryElement = $form->get('home_library');
$homeLibraryElement->setValueOptions($libraries);
$this->applyJobTitleOptions($form);
if ($this->formWasSubmitted()) {
$form->setData($request->getPost());
if ($form->isValid()) {
return $this->create($form);
}
} else {
$form->setData($query);
}
$action = $this->url()->fromRoute('fid/user/create');
$form->setAttribute('action', $action);
$form->prepare();
$view = $this->createViewModel();
$view->setVariables(compact('form'));
$view->setTemplate('fid/user/create');
return $view;
}
/**
* Create new user (registration part 2)
* via fidis after succeded formular validation.
*
* @param Form $form Formular
*
* @return Response
*/
protected function create(Form $form)
{
$messenger = $this->getMessenger();
$user = $form->getHydrator()->hydrate($form->getData(), new User());
try {
$this->client->requestUserCreation($user);
$message = $this->translate('fid::user_create_success');
$messenger->addSuccessMessage($message);
$this->authManager->create($this->getRequest());
} catch (ClientException $exception) {
$message = $this->translate('fid::user_create_error');
$messenger->addErrorMessage($message);
} catch (AuthException $e) {
$message = $this->translate('fid::user_create_error_autologon');
$messenger->addWarningMessage($message);
}

Alexander Purr
committed
'myresearch-home',
[],
[
'query' => ['redirect' => false]
* User / profile update route action
*
* @throws UserNotAuthorizedException
public function updateAction()
{
/* @var Form $form */
/* @var Request $request */
/* @var Select $homeLibraryElement */
$request = $this->getRequest();
$user = $this->client->requestUserDetails();
$libraries = array_map(
function (Library $libary) {
return $libary->getLabel();

Alexander Purr
committed
},
$this->client->requestLibraryList()
} catch (ClientException $exception) {
$this->setFollowupUrlToReferer();
$message = $exception->getCode() === 401
? 'fid::user_update_error_expired'
: 'fid::user_update_error';
$this->getMessenger()->addErrorMessage($this->translate($message));
return $this->redirect()->toRoute('myresearch-home');
}
$form = $this->serviceLocator->get('user-update-form');
$homeLibraryElement = $form->get('home_library');
$homeLibraryElement->setValueOptions($libraries);
if ($this->formWasSubmitted()) {
$form->setData($request->getPost());
if ($form->isValid()) {
return $this->update($form);
}
} else {
$form->setData($form->getHydrator()->extract($user));
$action = $this->url()->fromRoute('fid/user/update');
$form->setAttribute('action', $action);
$form->prepare();
$viewModel = $this->createViewModel();
// set cancel target if coming from lightbox
if ($cancelUrl = $this->getFollowupUrl()) {
$viewModel->setVariable('cancelUrl', $cancelUrl);
}
$viewModel->setVariables(compact('form'));
$viewModel->setTemplate('fid/user/update');
return $viewModel;
}
* Finish user update via fidis after succeded formular validation
*
* @param Form $form Formular
* @param string $redirect Route name
*
* @return Response
* @throws UserNotAuthorizedException
*/
protected function update(Form $form, string $redirect = 'myresearch-home')
{
$data = $form->getData();
$messenger = $this->getMessenger();
try {
$user = clone $this->client->requestUserDetails($data['id']);
$form->getHydrator()->hydrate($data, $user);
$this->client->requestUserUpdate($user);
$message = $this->translate('fid::user_update_success');
// use InfoMessage instead of SuccessMessage to prevent hiding
// rendered view (formular) in lightbox
$messenger->addInfoMessage($message);
} catch (ClientException $exception) {
if (in_array($exception->getCode(), [403])) {
$message = $this->translate(
'fid::user_update_error_'
. $exception->getCode()
);
} else {
$message = $this->translate('fid::user_update_error');
}
$messenger->addErrorMessage($message);
}
$this->client->flushUserList();
if ($followUp = $this->getFollowupUrl()) {
$this->clearFollowupUrl();
return $this->redirect()->toUrl($followUp);
}

Alexander Purr
committed
$redirect,
[],
[
'query' => ['redirect' => false]
* Request change username email (update username part 1) route action
*
*/
public function changeUsernameAction()
{
$request = $this->getRequest();
$form = $this->serviceLocator->get(UsernameChangeModel::class);
$forwarded = $this->params()->fromRoute('forwarded', false);
if ($submitted = $this->formWasSubmitted()) {
$form->setData($request->getPost());
if (!$forwarded && $form->isValid()) {
return $this->changeUsername($form);
}
}
$view = $this->createViewModel();
$view->setVariables(compact('form'));
$view->setTemplate('fid/user/username-change');
return $view;
}
/**
* Request change username email (update username part 1)
* via fidis after succeded formular validation.
*
* @param Form $form Formular
*
* @return mixed|Response
*/
protected function changeUsername(Form $form)
{
$messenger = $this->getMessenger();
$model = $form->getHydrator()->hydrate(

Alexander Purr
committed
$form->getData(),
new UsernameChangeModel()
/* @noinspection PhpUndefinedFieldInspection */
$query['lng'] = $this->layout()->userLang;
$username = $query['username'] = $model->getUsername();
$baseUrl = $this->url()->fromRoute(
'fid/user/update-username',

Alexander Purr
committed
[],
['query' => $query, 'force_canonical' => true]
try {
$this->client->requestUsernameLink($baseUrl, $username);
} catch (ClientException $exception) {
$message = $exception->getCode() === 400
? 'fid::username_change_error_username'
: 'fid::username_change_error';
$messenger->addErrorMessage($this->translate($message));

Alexander Purr
committed
self::class,
[
'action' => 'changeUsername',
'forwarded' => true
}
$message = $this->translate('fid::username_change_success');
$messenger->addSuccessMessage(sprintf($message, $username));
return $this->redirect()->toRoute('myresearch-home');
}
* Execute update username (update username part 2) route action.
* Action is triggert by link receved by email
*
*/
public function updateUsernameAction()
{
$request = $this->getRequest();
$query = $request->getQuery();
$messenger = $this->getMessenger();
if ($credentials = $query->get('logon')) {
try {
$this->client->logon($credentials);
} catch (ClientException $exception) {
$message = $exception->getCode() === 401
? 'fid::username_update_error_expired'
: 'fid::username_update_error';
$messenger->addErrorMessage($this->translate($message));
return $this->redirect()->toRoute('fid/user/change-username');
}
$query->offsetUnset('logon');

Alexander Purr
committed
'fid/user/update-username',
[],
[
$user = clone $this->client->requestUserDetails();
$user->setUsername($query->get('username'));
$this->client->requestUserUsernameUpdate($user);
$message = $this->translate('fid::username_update_success');
$this->getMessenger()->addSuccessMessage($message);
} catch (ClientException $exception) {
$message = $this->translate('fid::username_update_error');
$this->getMessenger()->addErrorMessage($message);
}
return $this->redirect()->toRoute('myresearch-home');
}
* Confirm delete profile route action
*
* @noinspection PhpUnused
*/
public function deleteAction()
{
if (!$this->getUser()) {
return $this->forceLogin();
}
$user = $this->client->requestUserDetails(null, true);
if ($user->getData()['deleted'] ?? false) {
$view = $this->createViewModel();
$messages['html'] = true;

Alexander Purr
committed
"fid::user_delete_error_account_blocked",
[
'%%fidname%%' => $this->translate("fid::acquisition_fid_name")
$this->flashMessenger()->addMessage($messages, 'error');
$view->setTemplate('default/flash-message');
return $view;
}
$request = $this->getRequest();
$form = $this->serviceLocator->get(UserDeleteModel::class);
$forwarded = $this->params()->fromRoute('forwarded', false);
if ($submitted = $this->formWasSubmitted()) {
$form->setData($request->getPost());
if (!$forwarded && $form->isValid()) {
return $this->deleteUser($form);
}
}
$view = $this->createViewModel();
$view->setVariables(compact('form'));
$view->setTemplate('fid/user/user-delete');
return $view;
}
/**
* Save user as deleted via fidis after succeded password confirmation.
* And log out user.
*
* @param Form $form Formular
*
* @return Response|ViewModel
* @throws ClientException
* @throws UserNotAuthorizedException
*/
protected function deleteUser(Form $form)
{
$messenger = $this->getMessenger();
/* @var UserDeleteModel $model */
$model = $form->getHydrator()
->hydrate($form->getData(), new UserDeleteModel());
$error = [];
try {
$user = $this->client->requestUserDetails();
if ($this->client->checkCredentials(
$user->getUsername(),
$model->getPasswordConfirmation()
)
) {
$user->setDeleted(true);
$user->setDeletedAt(date('Y-m-d H:i:s', time()));
$user = $this->client->requestUserUpdate($user);
if (!$user->isDeleted()) {
$error['msg'] = $this
->translate('fid::user_delete_error_unknown');
$error['msg'] = $this
->translate('fid::user_delete_error_wrong_password');
}
} catch (ClientException $exception) {
$error['msg'] = $this->translate('fid::user_delete_error_unknown');
}
$error['html'] = true;
$messenger->addErrorMessage($error);
return $this->redirect()->toRoute('fid/user/delete');
}
$this->client->logoff();
$this->getAuthManager()->logout("");
$view = $this->createViewModel();
$view->setTemplate('myresearch/delete-success');
return $view;
}
/**
* Display policy route action
*
* @return ViewModel
*/
public function policyAction()
{
$viewModel = $this->createViewModel();
$viewModel->setTemplate('fid/user/policy');
return $viewModel;
}
/**
* Display terms route action
*
* @return ViewModel
*/
public function termsAction()
{
$viewModel = $this->createViewModel();
$viewModel->setTemplate('fid/user/terms');
return $viewModel;
}
* Request reset password mail (reset password part 1) route action
* Allows the reset password form to appear, must be confirmed by user.
*
* @return ViewModel
*/
public function resetPasswordAction()
{
/* @var Form $form */
/* @var Request $request */
$form = $this->serviceLocator->get(PasswordResetModel::class);
$forwarded = $this->params()->fromRoute('forwarded', false);
$form->get('username')->setAttribute('disabled', 'true');
$form->get('username')
->setValue($this->client->requestUserDetails()->getUsername());
$form->setValidationGroup(['submit']);
}
if ($submitted = $this->formWasSubmitted()) {
$form->setData($request->getPost());
if (!$forwarded && $form->isValid()) {
return $this->sendResetPassword($form);
}
}
$view = $this->createViewModel();
$view->setVariables(compact('form'));
$view->setTemplate('fid/user/password-reset');
return $view;
}
/**
* Send reset password email via fidis to user
*
* @param Form $form Formular
*
* @return Response
* @throws ClientException
* @throws UserNotAuthorizedException
*/
protected function sendResetPassword(Form $form)
{
$messenger = $this->getMessenger();
if ($this->getUser()) {
$username = $this->client->requestUserDetails()->getUsername();
} else {
$model = $form->getHydrator()->hydrate(

Alexander Purr
committed
$form->getData(),
new PasswordResetModel()
$username = $model->getUsername();
}
/* @noinspection PhpUndefinedFieldInspection */
$query['lng'] = $this->layout()->userLang;
$baseUrl = $this->url()->fromRoute(
'fid/user/change-password',

Alexander Purr
committed
[],
['query' => $query, 'force_canonical' => true]
$this->client->requestPasswordLink($baseUrl, $username);
$message = $this->translate('fid::password_reset_success');
$messenger->addSuccessMessage(sprintf($message, $username));
} catch (ClientException $exception) {
$message = $exception->getCode() === 400
? $this->translate('fid::password_reset_error_username')
: $this->translate('fid::password_reset_error');
$messenger->addErrorMessage(sprintf($message, $username));
return $this->redirect()->toRoute('fid/user/reset-password');
}
return $this->redirect()->toRoute('myresearch-home');
}
* Reset password route action (reset password part 2).
* Action is triggerd by email link.
* Allows the change password form to appear.
*
* @return Response|ViewModel
*/
public function changePasswordAction()
{
$request = $this->getRequest();
$query = $request->getQuery();
$messenger = $this->getMessenger();
if ($credentials = $query->get('logon')) {
try {
$this->client->logon($credentials);
} catch (ClientException $exception) {
$message = $exception->getCode() === 401
? 'fid::password_change_error_expired'
: 'fid::password_change_error';
$messenger->addErrorMessage($this->translate($message));
return $this->redirect()->toRoute('fid/user/reset-password');
}
$query->offsetUnset('logon');

Alexander Purr
committed
'fid/user/change-password',
[],
[
$form = $this->serviceLocator->get(PasswordChangeModel::class);
if ($this->formWasSubmitted()) {
$form->setData($request->getPost());
if ($form->isValid()) {
return $this->changePassword($form);
}
} else {
$form->setData($query);
}
$view = $this->createViewModel();
$view->setVariables(compact('form'));
$view->setTemplate('fid/user/password-change');
return $view;
}
/**
* Update password by passing it to user model
* and run update password request to fidis
*
* @param Form $form Formular
*
* @return Response
* @throws UserNotAuthorizedException
* @throws \fid\Service\UserNotLoggedinException
*/
protected function changePassword(Form $form)
{
$messenger = $this->getMessenger();
$model = $form->getHydrator()->hydrate(

Alexander Purr
committed
$form->getData(),
new PasswordChangeModel()
$user = clone $this->client->requestUserDetails();
$user->setPassword($password = $model->getPassword());
$this->client->requestUserPasswordUpdate($user);
$message = $this->translate('fid::password_change_success');
$message = sprintf($message, $username = $user->getUsername());
$messenger->addSuccessMessage($message);
$request = clone $this->getRequest();
$params = clone $request->getPost();
$params->set('username', $username);
$params->set('password', $password);
$request->setPost($params);
$this->authManager->create($request);
} catch (ClientException $exception) {
$message = $this->translate('fid::password_change_error');
$messenger->addErrorMessage($message);
} catch (AuthException $e) {
$message = $this->translate('fid::password_change_error_autologon');
$messenger->addErrorMessage($message);
}

Alexander Purr
committed
'myresearch-home',
[],
[
'query' => ['redirect' => false]
/**
* Edit user route action.
* Only for admins.
*
* @return mixed|Response|ViewModel
* @throws UserNotAuthorizedException
*/
if (!$this->getUser()) {
return $this->forceLogin();
$request = $this->getRequest();
$userId = $this->params()->fromRoute('userid');
if (empty($userId)) {
// if no user ID is set the call is not valid
// unless we are in submission state
return $this->redirect()->toRoute('fid/admin/list');
}
try {
try {
$user = $this->client->requestUserDetails($userId);
} catch (UserNotAuthorizedException $ex) {
// either user does not exist, or we are not allowed to edit it
$this->getMessenger()
->addErrorMessage(
$this->translate(
'fid::user_edit_not_allowed',
['%%userid%%' => $userId]
)
);
return $this->redirect()->toRoute('fid/admin/list');
} catch (\Exception $ex) {
$this->getMessenger()
->addErrorMessage(
$this->translate(
'fid::user_read_error',
['%%userid%%' => $userId]
)
);
return $this->redirect()->toRoute('fid/admin/list');
}
$libraries = array_map(
function (Library $libary) {
return $libary->getLabel();

Alexander Purr
committed
},
$this->client->requestLibraryList()
} catch (ClientException $exception) {
$this->setFollowupUrlToReferer();
$message = $exception->getCode() === 401
? 'fid::user_update_error_expired'
: 'fid::user_update_error';
$this->getMessenger()->addErrorMessage($this->translate($message));
return $this->redirect()->toRoute('fid/admin/list');
}
/* @var Form $form */
/* @var Select $homeLibraryElement */
$form = $this->serviceLocator->get('admin-edit-form');
$homeLibraryElement = $form->get('home_library');
$homeLibraryElement->setValueOptions($libraries);
$homeLibraryElement->setUnselectedValue($user->getHomeLibrary());
$this->applyJobTitleOptions($form);
if ($this->formWasSubmitted()) {
$form->setData($request->getPost());
if ($form->isValid()) {
return $this->update($form, 'fid/admin/list');
} else {
$form->setData($form->getHydrator()->extract($user));

Alexander Purr
committed
'fid/admin/edit',
[
'userid' => $userId
$form->setAttribute('action', $action);
$form->prepare();
$config = $this->config;
$viewModel = $this->createViewModel();
$viewModel->setVariables(compact('config', 'form', 'user'));
$viewModel->setTemplate('fid/admin/edit');
return $viewModel;
}
/**
* Display admins user list route action
*
* @return mixed|ViewModel
*/
public function listAction()
{
if (!$this->getUser()) {
return $this->forceLogin();
}
$viewModel = $this->createViewModel();
try {
$list = $this->client->requestUserList();
$viewModel->setVariable('list', $list);
} catch (UserNotAuthorizedException $exception) {
$this->getMessenger()
->addErrorMessage('fid::read_user_list_not_allowed');
} catch (ClientException $exception) {
$this->getMessenger()->addErrorMessage('fid::read_user_list_error');
}
if ($fields = $this->config['Admin']['overview_fields']) {
$viewModel->setVariable('fields', $fields);
$viewModel->setVariable('config', $this->config);
$viewModel->setTemplate('fid/admin/list');
return $viewModel;
}
/**
* Display users order list route action
*
* @return mixed|void|ViewModel
* @throws UserNotAuthorizedException
*/
public function ordersAction()
{
if (!$this->getUser()) {
return $this->forceLogin();
}
try {
$user = $this->client->requestUserDetails(null, true);
$viewModel = $this->createViewModel();
$orders = $user->getOrders();

Alexander Purr
committed
$displayCols = $this->config['OrderListUser']['displayCols'];