<?php /** * 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. * * @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 */ 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\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\Checkbox; use Zend\Form\Element\Radio; use Zend\Form\Element\Select; use Zend\Form\Form; use Zend\Http\PhpEnvironment\Request; use Zend\Http\Response; use Zend\Mvc\Plugin\FlashMessenger\FlashMessenger; use Zend\ServiceManager\ServiceLocatorInterface; use Zend\View\Model\ViewModel; class UserController extends AbstractBase { /** * @var AuthManager */ protected $authManager; /** * @var Client */ protected $client; /** * @var array */ protected $config; /** * RegistrationController constructor. * * @param ServiceLocatorInterface $serviceLocator * @param AuthManager $authManager * @param Client $client * @param array $config */ public function __construct( ServiceLocatorInterface $serviceLocator, AuthManager $authManager, Client $client, array $config ) { parent::__construct($serviceLocator); $this->authManager = $authManager; $this->client = $client; $this->config = $config; } /** * @noinspection PhpUnused * @return ViewModel */ 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; } /** * @param Form $form * * @return mixed|Response */ protected function init(Form $form) { /** @var User $user */ $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', [], ['query' => $query, 'force_canonical' => true]); try { $this->client->requestRegistrationLink($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)); return $this->forward()->dispatch(self::class, [ 'action' => 'init', 'forwarded' => true ]); } $message = $this->translate('fid::user_init_success'); $messenger->addSuccessMessage(sprintf($message, $username)); return $this->redirect()->toRoute('myresearch-home'); } protected function getMessenger(): FlashMessenger { /** @noinspection PhpUndefinedMethodInspection */ /** @var FlashMessenger $messenger */ return $this->flashMessenger(); } /** * @noinspection PhpUnused * @return Response|ViewModel */ 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'); return $this->redirect()->toRoute('fid/user/create', [], [ 'query' => $query->toArray() ]); } try { $libraries = array_map(function (Library $libary) { return $libary->getLabel(); }, $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); 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; } protected function create(Form $form) { /** @var User $user */ $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); /** @noinspection PhpParamsInspection */ $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); } return $this->redirect()->toRoute('myresearch-home', [], [ 'query' => ['redirect' => false] ]); } /** * @return Response|ViewModel * @throws UserNotAuthorizedException */ public function updateAction() { /** @var Form $form */ /** @var Request $request */ /** @var Select $homeLibraryElement */ try { $request = $this->getRequest(); $user = $this->client->requestUserDetails(); $libraries = array_map(function (Library $libary) { return $libary->getLabel(); }, $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(); $viewModel->setVariables(compact('form')); $viewModel->setTemplate('fid/user/update'); return $viewModel; } /** * @param Form $form * @param string $redirect * * @return Response * @throws UserNotAuthorizedException */ protected function update(Form $form, string $redirect = 'myresearch-home') { $data = $form->getData(); $messenger = $this->getMessenger(); try { $user = $this->client->requestUserDetails($data['id']); $form->getHydrator()->hydrate($data, $user); $this->client->requestUserUpdate($user); $message = $this->translate('fid::user_update_success'); $messenger->addSuccessMessage($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(); return $this->redirect()->toRoute($redirect, [], [ 'query' => ['redirect' => false] ]); } /** * @noinspection PhpUnused * @return ViewModel */ public function changeUsernameAction() { /** @var Request $request */ $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; } protected function changeUsername(Form $form) { $messenger = $this->getMessenger(); $model = $form->getHydrator()->hydrate( $form->getData(), new UsernameChangeModel()); /** @noinspection PhpUndefinedFieldInspection */ $query['lng'] = $this->layout()->userLang; $username = $query['username'] = $model->getUsername(); $baseUrl = $this->url()->fromRoute('fid/user/update-username', [], ['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)); return $this->forward()->dispatch(self::class, [ 'action' => 'changeUsername', 'forwarded' => true ]); } $message = $this->translate('fid::username_change_success'); $messenger->addSuccessMessage(sprintf($message, $username)); return $this->redirect()->toRoute('myresearch-home'); } /** * @noinspection PhpUnused * @return Response */ public function updateUsernameAction() { /** @var Request $request */ $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'); return $this->redirect()->toRoute('fid/user/update-username', [], [ 'query' => $query->toArray() ]); } try { $user = $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'); } public function policyAction() { $viewModel = $this->createViewModel(); $viewModel->setTemplate('fid/user/policy'); $this->setBackUrl($viewModel); return $viewModel; } public function termsAction() { $viewModel = $this->createViewModel(); $viewModel->setTemplate('fid/user/terms'); $this->setBackUrl($viewModel); return $viewModel; } /** * Reset password action - Allows the reset password form to appear. * * @return ViewModel */ public function resetPasswordAction() { /** @var Form $form */ /** @var Request $request */ $request = $this->getRequest(); $form = $this->serviceLocator->get(PasswordResetModel::class); $forwarded = $this->params()->fromRoute('forwarded', false); 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; } protected function sendResetPassword(Form $form) { /** @var PasswordResetModel $model */ $messenger = $this->getMessenger(); $model = $form->getHydrator()->hydrate( $form->getData(), new PasswordResetModel()); $username = $model->getUsername(); try { /** @noinspection PhpUndefinedFieldInspection */ $query['lng'] = $this->layout()->userLang; $baseUrl = $this->url()->fromRoute('fid/user/change-password', [], ['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 action - Allows the change password form to appear. * * @return Response|ViewModel */ public function changePasswordAction() { /** @var Request $request */ $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'); return $this->redirect()->toRoute('fid/user/change-password', [], [ 'query' => $query->toArray() ]); } $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; } protected function changePassword(Form $form) { /** @var PasswordChangeModel $model */ $messenger = $this->getMessenger(); $model = $form->getHydrator()->hydrate( $form->getData(), new PasswordChangeModel()); try { $user = $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); /** @var Request $request */ $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); } return $this->redirect()->toRoute('myresearch-home', [], [ 'query' => ['redirect' => false] ]); } public function editAction() { if (!$this->getUser()) { return $this->forceLogin(); } /** @var Request $request */ $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(); }, $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()); if ($this->formWasSubmitted()) { $form->setData($request->getPost()); if ($form->isValid()) { return $this->update($form, 'fid/admin/list'); } } else { $form->setData($form->getHydrator()->extract($user)); } $action = $this->url()->fromRoute('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; } 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; } public function ordersAction() { if (!$this->getUser()) { return $this->forceLogin(); } try { $user = $this->client->requestUserDetails(); $viewModel = $this->createViewModel(); $viewModel->setVariable('orders', $user->getOrders()); $viewModel->setTemplate('fid/user/orders'); return $viewModel; } catch (ClientException $exception) { $this->getMessenger()->addErrorMessage('fid::orders_error'); $this->redirect()->toRoute('myresearch-profile'); } } public function adminOrdersAction() { if (!$this->getUser()) { return $this->forceLogin(); } try { $orders = $this->client->requestOrderList(); $viewModel = $this->createViewModel(); $viewModel->setVariables(compact('orders')); $viewModel->setTemplate('fid/user/admin-orders'); return $viewModel; } catch (UserNotAuthorizedException $exception) { $this->getMessenger()->addErrorMessage('fid::read_order_list_not_allowed'); } catch (ClientException $exception) { $this->getMessenger()->addErrorMessage('fid::read_order_list_error'); $this->redirect()->toRoute('myresearch-profile'); } } /** * @param ViewModel $viewModel * @return bool */ protected function setBackUrl(ViewModel $viewModel): bool { if (!empty($query = $this->getRequest()->getQuery()) && !empty($query->get('backUrl'))) { $viewModel->setVariable('backUrl', $this->getRequest()->getQuery()->get('backUrl')); return true; } if (isset($_REQUEST['lbreferer'])) { $viewModel->setVariable('backUrl', $_REQUEST['lbreferer']); return true; } return false; } }