Skip to content
Snippets Groups Projects
UserController.php 25.2 KiB
Newer Older
<?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 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,
        Client $client,
        array $config
    ) {
        parent::__construct($serviceLocator);
        $this->authManager = $authManager;
        $this->client = $client;
        $this->config = $config;
     * @noinspection PhpUnused
     * @return ViewModel
     */
    public function initAction()
    {
        /** @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 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 Request $request */
        /** @var Select $homeLibraryElement */
            $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);
    /**
     * Reset password action - Allows the reset password form to appear.
     *
     * @return ViewModel
     */
    public function resetPasswordAction()
    {
        /** @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()
    {
            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;
    }