From baaef31fc17d7cb91ae6ae1f12286dd3ac4044f4 Mon Sep 17 00:00:00 2001 From: Zeon Date: 2020年10月26日 16:08:19 +0000 Subject: [PATCH 01/35] Reg db, reg form + template, login form, reg feldolgozas jon --- appinfo/info.xml | 4 +- appinfo/routes.php | 4 + lib/Controller/ApiController.php | 8 + lib/Controller/PageController.php | 12 +- lib/Controller/SureveyUserController.php | 174 ++++++++++++++++++ lib/Db/SurveyUser.php | 86 +++++++++ lib/Db/SurveyUserMapper.php | 84 +++++++++ .../Version020005Date20201019161000.php | 94 ++++++++++ lib/Service/FormsService.php | 42 ++++- lib/Service/SurveyUserService.php | 76 ++++++++ src/views/Sidebar.vue | 13 ++ templates/surveyuserlogin.php | 57 ++++++ templates/surveyuserregister.php | 102 ++++++++++ 13 files changed, 745 insertions(+), 11 deletions(-) create mode 100644 lib/Controller/SureveyUserController.php create mode 100644 lib/Db/SurveyUser.php create mode 100644 lib/Db/SurveyUserMapper.php create mode 100644 lib/Migration/Version020005Date20201019161000.php create mode 100644 lib/Service/SurveyUserService.php create mode 100644 templates/surveyuserlogin.php create mode 100644 templates/surveyuserregister.php diff --git a/appinfo/info.xml b/appinfo/info.xml index 979361b56..65441cf62 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -2,7 +2,7 @@ forms - Forms + FormsEx 📝 Simple surveys and questionnaires, self-hosted - 2.0.4 + 2.0.5 agpl Affan Hussain diff --git a/appinfo/routes.php b/appinfo/routes.php index 03cbdc209..bb7aed578 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -28,6 +28,10 @@ 'routes' => [ ['name' => 'page#index', 'url' => '/', 'verb' => 'GET'], + ['name' => 'sureveyUser#register', 'url' => '/sureveyuser/register/form', 'verb' => 'GET'], + ['name' => 'sureveyUser#commitRegister', 'url' => '/sureveyuser/register/form', 'verb' => 'POST'], + ['name' => 'sureveyUser#login', 'url' => '/sureveyuser/login', 'verb' => 'POST'], + // Before /{hash} to avoid conflict ['name' => 'page#index', 'url' => '/new', 'verb' => 'GET', 'postfix' => 'create'], ['name' => 'page#index', 'url' => '/{hash}/edit', 'verb' => 'GET', 'postfix' => 'edit'], diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index 3bd06d013..e62b3cb66 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -39,6 +39,7 @@ use OCA\Forms\Db\SubmissionMapper; use OCA\Forms\Service\FormsService; +use OCA\Forms\Service\SurveyUserService; use OCP\AppFramework\Controller; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\IMapperException; @@ -86,6 +87,9 @@ class ApiController extends Controller { /** @var FormsService */ private $formsService; + /** @var SurveyUserService */ + private $surveyUserService; + /** @var ISecureRandom */ private $secureRandom; @@ -101,6 +105,7 @@ public function __construct(string $appName, ILogger $logger, IL10N $l10n, FormsService $formsService, + SurveyUserService $surveyUserService, ISecureRandom $secureRandom) { parent::__construct($appName, $request); $this->appName = $appName; @@ -115,6 +120,7 @@ public function __construct(string $appName, $this->logger = $logger; $this->l10n = $l10n; $this->formsService = $formsService; + $this->surveyUserService = $surveyUserService; $this->secureRandom = $secureRandom; $this->currentUser = $userSession->getUser(); @@ -211,6 +217,8 @@ public function newForm(): DataResponse { * @throws OCSForbiddenException */ public function updateForm(int $id, array $keyValuePairs): DataResponse { + // TODO the absolute lack of input validation is worrysome all along Form::fromParams + $this->logger->debug('Updating form: FormId: {id}, values: {keyValuePairs}', [ 'id' => $id, 'keyValuePairs' => $keyValuePairs diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index c6da21222..e13b46994 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -55,16 +55,16 @@ class PageController extends Controller { /** @var FormMapper */ private $formMapper; - + /** @var FormsService */ private $formsService; /** @var IAccountManager */ protected $accountManager; - + /** @var IGroupManager */ private $groupManager; - + /** @var IInitialStateService */ private $initialStateService; @@ -76,7 +76,7 @@ class PageController extends Controller { /** @var IUserManager */ private $userManager; - + /** @var IUserSession */ private $userSession; @@ -151,6 +151,10 @@ public function gotoForm($hash): ?TemplateResponse { // Does the user have access to form if (!$this->formsService->hasUserAccess($form->getId())) { + if ($this->formsService->isSurveyLoginRequired($form->getId())) { + return $this->provideTemplate( + SureveyUserController::TEMPLATE_SURVEY_USER_LOGIN, $form); + } return $this->provideTemplate(self::TEMPLATE_NOTFOUND); } diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php new file mode 100644 index 000000000..0f1348985 --- /dev/null +++ b/lib/Controller/SureveyUserController.php @@ -0,0 +1,174 @@ + + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Controller; + +use OCA\Forms\Service\SurveyUserService; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Http\Template\PublicTemplateResponse; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IL10N; +use OCP\ILogger; +use OCP\IRequest; + +class SureveyUserController extends Controller { + public const TEMPLATE_SURVEY_USER_LOGIN = 'surveyuserlogin'; + public const TEMPLATE_SURVEY_USER_REGISTER = 'surveyuserregister'; + + public const SUREVEY_USER_PASS_MIN_LEN = 8; + + protected $appName; + + /** @var IL10N */ + private $l10n; + + /** @var ILogger */ + private $logger; + + /** @var SurveyUserService */ + private $surveyUserService; + + public function __construct(string $appName, + IL10N $l10n, + ILogger $logger, + SurveyUserService $surveyUserService, + IRequest $request) { + parent::__construct($appName, $request); + $this->l10n = $l10n; + $this->surveyUserService = $surveyUserService; + $this->logger = $logger; + $this->appName = $appName; + } + + public function login(): Response { + //https://docs.nextcloud.com/server/15/developer_manual/app/requests/controllers.html + //$response->addCookie('foo', 'bar'); + } + + /** + * Provide a form where the survey users can register + * + * @PublicPage + * @NoCSRFRequired + * @NoSameSiteCookieRequired + * @CORS + * + * @return TemplateResponse The template with the registration details + */ + public function register(): PublicTemplateResponse { + return $this->setupRegisterPage([ + 'mode' => 'first' + ]); + } + + /** + * Process the form where the survey users can register + * + * @PublicPage + * @NoCSRFRequired + * @NoSameSiteCookieRequired + * @CORS + * + * @return PublicTemplateResponse The template with the registration details + */ + public function commitRegister($su_email, + $su_login, + $su_password, + $su_password2, + $su_address, + $su_born): PublicTemplateResponse { + + $su_email = $_POST['su_email']; + + $success = true; + $message = ''; + $problems = []; + + if (!filter_var($su_email, FILTER_VALIDATE_EMAIL)) { + $success = false; + $problems[] = $this->l10n->t('Invalid e-mail format.'); + } + + if ($su_password !== $su_password2) { + $success = false; + $problems[] = $this->l10n->t( + 'The password and password verification fields don\'t match.'); + } + + if (strlen($su_password) < self::SUREVEY_USER_PASS_MIN_LEN) { + $success = false; + $problems[] = $this->l10n->t( + 'The password is too short. Please use at least %s characters.', + self::SUREVEY_USER_PASS_MIN_LEN); + } + + if (strlen($su_login) < 3) { + $success = false; + $problems[] = $this->l10n->t( + 'The login name is too short. Please use at least three characters.'); + } else if (!$this->surveyUserService->isUserNameAvailable($su_login)) { + $success = false; + $problems[] = $this->l10n->t( + 'The login is in use. Please select a different one.'); + } + + if (!$success) + $message = $this->l10n->n( + 'There is a problem with the registration data, please correct it:', // singular string + 'There are problems with the registration data, please correct them:', // plural string + count($problems) + ); + + $data = [ + 'mode' => 'return', + 'su_email' => $su_email, + 'su_login' => $su_login, + 'su_password' => $su_password, + 'su_password2' => $su_password2, + 'su_address' => $su_address, + 'su_born' => $su_born, + 'success' => $success, + 'message' => $message, + 'problems' => $problems, + ]; + + return $this->setupRegisterPage($data, + $this->l10n->t('Please check your details')); + } + + private function setupRegisterPage($data, + $subtitle = null) : PublicTemplateResponse { + // TODO + $template = new PublicTemplateResponse( + $this->appName, + self::TEMPLATE_SURVEY_USER_REGISTER, + $data); + $template->setHeaderTitle($this->l10n->t('Survey user registration')); + $template->setHeaderDetails($subtitle === null + ? $this->l10n->t('Please enter your details') + : $subtitle + ); + return $template; + } +} diff --git a/lib/Db/SurveyUser.php b/lib/Db/SurveyUser.php new file mode 100644 index 000000000..c2ee4375f --- /dev/null +++ b/lib/Db/SurveyUser.php @@ -0,0 +1,86 @@ + + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Db; + +use OCP\AppFramework\Db\Entity; + +/** + * @method integer getFormId() + * @method void setFormId(integer $value) + * @method string getLogin() + * @method void setLogin(string $value) + * @method string getRealname() + * @method void setRealname(string $value) + * @method bool getIsdeleted() + * @method void setIsdeleted(bool $value) + * @method string getAddress() + * @method void setAddress(string $value) + * @method string getEmail() + * @method void setEmail(string $value) + * @method string getConfirmcode() + * @method void setConfirmcode(string $value) + * @method integer getBornyear() + * @method void setBornyear(integer $value) + */ +class SurveyUser extends Entity +{ + protected $login; + protected $realname; + protected $isdeleted; + protected $address; + protected $email; + protected $confirmcode; + protected $bornyear; + + /** + * SurveyUser constructor. + */ + public function __construct() + { + $this->addType('login', 'string'); + $this->addType('realname', 'string'); + $this->addType('isdeleted', 'bool'); + $this->addType('address', 'string'); + $this->addType('email', 'string'); + $this->addType('confirmcode', 'string'); + $this->addType('bornyear', 'integer'); + } + + public function read(): array + { + return [ + 'id' => $this->getId(), + 'login' => $this->getLogin(), + 'realname' => $this->getRealname(), + 'isdeleted' => $this->getIsdeleted(), + 'address' => $this->getAddress(), + 'email' => $this->getEmail(), + 'confirmcode' => $this->getConfirmcode(), + 'bornyear' => $this->getBornyear(), + ]; + } +} diff --git a/lib/Db/SurveyUserMapper.php b/lib/Db/SurveyUserMapper.php new file mode 100644 index 000000000..1ca8c9f20 --- /dev/null +++ b/lib/Db/SurveyUserMapper.php @@ -0,0 +1,84 @@ + + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Db; + +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\QBMapper; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; + +class SurveyUserMapper extends QBMapper { + + /** + * SubmissionMapper constructor. + * @param IDBConnection $db + */ + public function __construct(IDBConnection $db) { + parent::__construct($db, 'forms_surveyusers', SurveyUser::class); + } + + /** + * Find a user by e-mail address. (Either deleted or active) + * + * @param string $email + * @return SurveyUser|\OCP\AppFramework\Db\Entity + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result + * @throws \OCP\AppFramework\Db\DoesNotExistException if not found + */ + public function findByEmail(string $email) + { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from($this->getTableName()) + ->where( + $qb->expr()->eq('email', $qb->createNamedParameter($email, IQueryBuilder::PARAM_STR)) + ); + + return $this->findEntity($qb); + } + + /** + * Find a user by login name. (Either deleted or active) + * + * @param string $login + * @return SurveyUser|\OCP\AppFramework\Db\Entity + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result + * @throws \OCP\AppFramework\Db\DoesNotExistException if not found + */ + public function findByLogin(string $login) + { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from($this->getTableName()) + ->where( + $qb->expr()->eq('login', $qb->createNamedParameter($login, IQueryBuilder::PARAM_STR)) + ); + + return $this->findEntity($qb); + } + +} diff --git a/lib/Migration/Version020005Date20201019161000.php b/lib/Migration/Version020005Date20201019161000.php new file mode 100644 index 000000000..e6055c344 --- /dev/null +++ b/lib/Migration/Version020005Date20201019161000.php @@ -0,0 +1,94 @@ + + * + * @author Jonas Rittershofer + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Migration; + +use Closure; +use Doctrine\DBAL\Types\Type; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +/** + * Class Version020005Date20201019161000 Add user table for the survey users + * @package OCA\Forms\Migration + */ +class Version020005Date20201019161000 extends SimpleMigrationStep { + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + if (!$schema->hasTable('forms_surveyusers')) { + $table = $schema->createTable('forms_surveyusers'); + $table->addColumn('id', TYPE::BIGINT, [ + 'autoincrement' => true, + 'notnull' => true, + 'length' => 11, + 'unsigned' => true + ]); + $table->addColumn('login', TYPE::STRING, [ + 'notnull' => true, + 'length' => 100 + ]); + $table->addColumn('realname', TYPE::STRING, [ + 'notnull' => false, + 'length' => 255 + ]); + $table->addColumn('isdeleted', TYPE::BOOLEAN, [ + 'notnull' => true, + 'length' => 1, + 'default' => 0 + ]); + $table->addColumn('address', TYPE::TEXT, [ + 'notnull' => false + ]); + $table->addColumn('email', TYPE::STRING, [ + 'notnull' => true, + 'length' => 200 + ]); + $table->addColumn('confirmcode', TYPE::STRING, [ + 'notnull' => false, + 'length' => 200 + ]); + $table->addColumn('bornyear', TYPE::INTEGER, [ + 'notnull' => false + ]); + + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['login'], 'forms_su_login'); + $table->addUniqueIndex(['email', 'isdeleted'], 'forms_su_email'); + $table->addIndex(['confirmcode'], 'forms_su_confirmcode'); + } + + return $schema; + } +} diff --git a/lib/Service/FormsService.php b/lib/Service/FormsService.php index 5e9c43a02..6a2eab655 100644 --- a/lib/Service/FormsService.php +++ b/lib/Service/FormsService.php @@ -41,7 +41,7 @@ * Trait for getting forms information in a service */ class FormsService { - + /** @var FormMapper */ private $formMapper; @@ -53,13 +53,16 @@ class FormsService { /** @var SubmissionMapper */ private $submissionMapper; - + /** @var IGroupManager */ private $groupManager; - + /** @var IUserManager */ private $userManager; + /** @var SurveyUserService */ + private $surveyUserService; + /** @var IUser */ private $currentUser; @@ -73,8 +76,10 @@ public function __construct(FormMapper $formMapper, IGroupManager $groupManager, IUserManager $userManager, IUserSession $userSession, + SurveyUserService $surveyUserService, ILogger $logger) { $this->formMapper = $formMapper; + $this->surveyUserService = $surveyUserService; $this->questionMapper = $questionMapper; $this->optionMapper = $optionMapper; $this->submissionMapper = $submissionMapper; @@ -140,7 +145,7 @@ public function getForm(int $id): array { $result['questions'] = $this->getQuestions($id); // Set proper user/groups properties - + // Make sure we have the bare minimum $result['access'] = array_merge(['users' => [], 'groups' => []], $result['access']); @@ -193,6 +198,21 @@ public function canSubmit($formId) { return true; } + /** + * Checks if the form is a "registered survey users only" form that + * requires survey user login + * + * @param int $formId + * @return bool + */ + public function isSurveyLoginRequired(int $formId): bool { + $form = $this->formMapper->findById($formId); + $access = $form->getAccess(); + + return $access['type'] === 'surveyusers' + && !$this->hasUserAccess($formId); + } + /** * Check if user has access to this form * @@ -207,7 +227,15 @@ public function hasUserAccess(int $formId): bool { if ($access['type'] === 'public') { return true; } - + + $surveyUserForm = $access['type'] === 'surveyusers'; + $surveyUserLoggedIn = $this->surveyUserService->isSurveyUserLoggedIn(); + + // TODO TODOFORMS check access + if ($surveyUserForm && $surveyUserLoggedIn) { + return true; + } + // Refuse access, if not public and no user logged in. if (!$this->currentUser) { return false; @@ -218,6 +246,10 @@ public function hasUserAccess(int $formId): bool { return true; } + // Survey user forms can be filled by NC users to enter physical copies + if ($surveyUserForm) + return true; + // Now all remaining users are allowed, if access-type 'registered'. if ($access['type'] === 'registered') { return true; diff --git a/lib/Service/SurveyUserService.php b/lib/Service/SurveyUserService.php new file mode 100644 index 000000000..216a74dba --- /dev/null +++ b/lib/Service/SurveyUserService.php @@ -0,0 +1,76 @@ + + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Service; + +use OCA\Forms\Db\FormMapper; +use OCA\Forms\Db\SurveyUserMapper; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\ILogger; + +/** + * Class SurveyUserService Handles the survey user registration and login + * @package OCA\Forms\Service + */ +class SurveyUserService +{ + /** @var FormMapper */ + private $formMapper; + + /** @var SurveyUserMapper */ + private $surveyUserMapper; + + /** @var ILogger */ + private $logger; + + public function __construct(FormMapper $formMapper, + SurveyUserMapper $surveyUserMapper, + ILogger $logger) { + $this->formMapper = $formMapper; + $this->surveyUserMapper = $surveyUserMapper; + $this->logger = $logger; + } + + public function isSurveyUserLoggedIn() { + return false; + } + + public function getCurrentSurveyUser() { + return false; + } + + /** + * @param $loginToCheck + * @return bool + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + */ + public function isUserNameAvailable($loginToCheck) { + try { + $this->surveyUserMapper->findByLogin($loginToCheck); + } catch (DoesNotExistException $e) { + return true; + } + + return false; + } +} diff --git a/src/views/Sidebar.vue b/src/views/Sidebar.vue index 89298a920..b41bae8ef 100644 --- a/src/views/Sidebar.vue +++ b/src/views/Sidebar.vue @@ -57,6 +57,19 @@ +
  • + + +
  • + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +$routeRegister = \OC::$server->getURLGenerator()->linkToRoute('forms.sureveyUser.register'); +$routeLogin = \OC::$server->getURLGenerator()->linkToRoute('forms.sureveyUser.login'); + +?> + +
    + +

    t('This form is for registered users only. If you already have an account, please log.')); ?>

    + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    + +

    t('If you don\'t have an account yet, you can register by clicking here.', $routeRegister)); ?>

    + +
    diff --git a/templates/surveyuserregister.php b/templates/surveyuserregister.php new file mode 100644 index 000000000..e82a6d603 --- /dev/null +++ b/templates/surveyuserregister.php @@ -0,0 +1,102 @@ + + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +$routeRegister = \OC::$server->getURLGenerator()->linkToRoute('forms.sureveyUser.commitRegister'); + +?> + +
    + + + 0): ?> +
    + 0) { + print_unescaped( + '
    • '. + join('
    • ', $_['problems']). + '
    '); + } + ?> +
    + + +

    t('Please provide your details below.')); ?>

    + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    + +
    From 9fbba900a66fb9071bfa49bfafecf6b4ad513e4c Mon Sep 17 00:00:00 2001 From: Zeon Date: 2020年10月27日 14:33:38 +0000 Subject: [PATCH 02/35] Survey user reg, login megy --- appinfo/routes.php | 7 +- lib/Controller/PageController.php | 8 +- lib/Controller/SureveyUserController.php | 186 ++++++++++++++++-- lib/Db/SurveyUser.php | 10 +- lib/Db/SurveyUserMapper.php | 22 +-- lib/Helper/RandomHelper.php | 48 +++++ lib/Helper/ValidationHelper.php | 41 ++++ .../Version020005Date20201019161000.php | 5 +- lib/Service/SurveyUserService.php | 53 ++++- templates/surveyuserlogin.php | 67 ++++--- templates/surveyuserregister.php | 150 +++++++------- 11 files changed, 447 insertions(+), 150 deletions(-) create mode 100644 lib/Helper/RandomHelper.php create mode 100644 lib/Helper/ValidationHelper.php diff --git a/appinfo/routes.php b/appinfo/routes.php index bb7aed578..9bfa7627b 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -28,9 +28,10 @@ 'routes' => [ ['name' => 'page#index', 'url' => '/', 'verb' => 'GET'], - ['name' => 'sureveyUser#register', 'url' => '/sureveyuser/register/form', 'verb' => 'GET'], - ['name' => 'sureveyUser#commitRegister', 'url' => '/sureveyuser/register/form', 'verb' => 'POST'], - ['name' => 'sureveyUser#login', 'url' => '/sureveyuser/login', 'verb' => 'POST'], + ['name' => 'sureveyUser#register', 'url' => '/sureveyuser/register/form/{id}', 'verb' => 'GET', 'defaults' => ['id' => '']], + ['name' => 'sureveyUser#commitRegister', 'url' => '/sureveyuser/register/form/{id}', 'verb' => 'POST', 'defaults' => ['id' => '']], + ['name' => 'sureveyUser#loginForm', 'url' => '/sureveyuser/login/form/{id}', 'verb' => 'GET', 'defaults' => ['id' => '']], + ['name' => 'sureveyUser#login', 'url' => '/sureveyuser/login/{id}', 'verb' => 'POST', 'defaults' => ['id' => '']], // Before /{hash} to avoid conflict ['name' => 'page#index', 'url' => '/new', 'verb' => 'GET', 'postfix' => 'create'], diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index e13b46994..142deef18 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -59,6 +59,9 @@ class PageController extends Controller { /** @var FormsService */ private $formsService; + /** @var SureveyUserController */ + private $sureveyUserController; + /** @var IAccountManager */ protected $accountManager; @@ -99,6 +102,7 @@ public function __construct(string $appName, IAccountManager $accountManager, IGroupManager $groupManager, IInitialStateService $initialStateService, + SureveyUserController $sureveyUserController, IL10N $l10n, ILogger $logger, IUserManager $userManager, @@ -109,6 +113,7 @@ public function __construct(string $appName, $this->formMapper = $formMapper; $this->formsService = $formsService; + $this->sureveyUserController = $sureveyUserController; $this->accountManager = $accountManager; $this->groupManager = $groupManager; @@ -152,8 +157,7 @@ public function gotoForm($hash): ?TemplateResponse { // Does the user have access to form if (!$this->formsService->hasUserAccess($form->getId())) { if ($this->formsService->isSurveyLoginRequired($form->getId())) { - return $this->provideTemplate( - SureveyUserController::TEMPLATE_SURVEY_USER_LOGIN, $form); + return $this->sureveyUserController->surveyUserLoginPage($hash); } return $this->provideTemplate(self::TEMPLATE_NOTFOUND); } diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php index 0f1348985..54fdf7e05 100644 --- a/lib/Controller/SureveyUserController.php +++ b/lib/Controller/SureveyUserController.php @@ -23,8 +23,19 @@ namespace OCA\Forms\Controller; +use OC\OCS\Exception; +use OCA\Forms\Db\Form; +use OCA\Forms\Db\FormMapper; +use OCA\Forms\Db\SurveyUser; +use OCA\Forms\Db\SurveyUserMapper; +use OCA\Forms\Helper\RandomHelper; +use OCA\Forms\Helper\ValidationHelper; use OCA\Forms\Service\SurveyUserService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\IMapperException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\Template\PublicTemplateResponse; use OCP\AppFramework\Http\TemplateResponse; @@ -49,26 +60,106 @@ class SureveyUserController extends Controller { /** @var SurveyUserService */ private $surveyUserService; + /** @var FormMapper */ + private $formMapper; + + /** @var SurveyUserMapper */ + private $surveyUserMapper; + public function __construct(string $appName, IL10N $l10n, ILogger $logger, + FormMapper $formMapper, + SurveyUserMapper $surveyUserMapper, SurveyUserService $surveyUserService, IRequest $request) { parent::__construct($appName, $request); $this->l10n = $l10n; + $this->formMapper = $formMapper; + $this->surveyUserMapper = $surveyUserMapper; $this->surveyUserService = $surveyUserService; $this->logger = $logger; $this->appName = $appName; } - public function login(): Response { + /** + * Process the login details and/or show the login page for the survey users + * accordingly + * + * @PublicPage + * @NoCSRFRequired + * @NoSameSiteCookieRequired + * @CORS + * + * @param string $id Form id to redirect to after the login + * @return Response Login page + */ + public function login($id, $su_email, $su_password): Response { //https://docs.nextcloud.com/server/15/developer_manual/app/requests/controllers.html //$response->addCookie('foo', 'bar'); + + if (!$su_email || !$su_password) { + return $this->surveyUserLoginPage( + $id, + false, + $this->l10n->t('Please fill in both the e-mail and password fields.') + ); + } + + $success = false; + + try { + $user = $this->surveyUserMapper->findByEmail($su_email); + if ($user && password_verify($su_password, $user->getPasswordhash())) { + $this->surveyUserService->setCurrentSurveyUser($user->getId()); + $success = true; + } + } catch (IMapperException $e) { + // TODO TODOFORMS log + // We are already at success = false + } + + if ($success) { + if ($id) + $response = new RedirectResponse(\OC::$server->getURLGenerator() + ->linkToRoute('forms.page.goto_form', ['hash' => $id])); + else + $response = $this->surveyUserLoginPage( + null, + true, + $this->l10n->t('Successful login') + ); + } else { + $response = $this->surveyUserLoginPage( + $id, + false, + $this->l10n->t('Invalid e-mail or login.') + ); + } + + return $response; + } + + /** + * Show the login page for the survey users + * + * @PublicPage + * @NoCSRFRequired + * @NoSameSiteCookieRequired + * @CORS + * + * @param string $id Form id to redirect to after the login + * @return Response Login page + */ + public function loginForm($id): Response { + return $this->login($id, null, null); } /** * Provide a form where the survey users can register * + * @param string $id Form id to pass to the login after the registration + * * @PublicPage * @NoCSRFRequired * @NoSameSiteCookieRequired @@ -76,8 +167,9 @@ public function login(): Response { * * @return TemplateResponse The template with the registration details */ - public function register(): PublicTemplateResponse { + public function register($id): PublicTemplateResponse { return $this->setupRegisterPage([ + 'formid' => $id, 'mode' => 'first' ]); } @@ -85,6 +177,8 @@ public function register(): PublicTemplateResponse { /** * Process the form where the survey users can register * + * @param string $id Form id to pass to the login after the registration + * * @PublicPage * @NoCSRFRequired * @NoSameSiteCookieRequired @@ -92,10 +186,11 @@ public function register(): PublicTemplateResponse { * * @return PublicTemplateResponse The template with the registration details */ - public function commitRegister($su_email, - $su_login, + public function commitRegister($id, + $su_email, $su_password, $su_password2, + $su_realname, $su_address, $su_born): PublicTemplateResponse { @@ -108,6 +203,13 @@ public function commitRegister($su_email, if (!filter_var($su_email, FILTER_VALIDATE_EMAIL)) { $success = false; $problems[] = $this->l10n->t('Invalid e-mail format.'); + } else { + $su_email = filter_var($su_email, FILTER_VALIDATE_EMAIL); + if (!$this->surveyUserService->isEmailAvailable($su_email)) { + $success = false; + $problems[] = $this->l10n->t( + 'The e-mail address is in use. Please try to reset your password.'); + } } if ($su_password !== $su_password2) { @@ -123,31 +225,46 @@ public function commitRegister($su_email, self::SUREVEY_USER_PASS_MIN_LEN); } - if (strlen($su_login) < 3) { - $success = false; - $problems[] = $this->l10n->t( - 'The login name is too short. Please use at least three characters.'); - } else if (!$this->surveyUserService->isUserNameAvailable($su_login)) { - $success = false; - $problems[] = $this->l10n->t( - 'The login is in use. Please select a different one.'); - } - - if (!$success) + if (!$success) { $message = $this->l10n->n( 'There is a problem with the registration data, please correct it:', // singular string 'There are problems with the registration data, please correct them:', // plural string count($problems) ); + } else { + $newUser = new SurveyUser(); + $newUser->setRealname(ValidationHelper::filterAlphaNumericUnicde($su_realname, 255)); + $newUser->setAddress(ValidationHelper::filterAlphaNumericUnicde($su_address, 1000)); + $newUser->setEmail($su_email); // It is already filtered above + $newUser->setPasswordhash(password_hash($su_password, PASSWORD_ARGON2I)); + $newUser->setConfirmcode(RandomHelper::randomStr(100)); + + // TODO TODOFORMS send mail + + $born = (int)$su_born; + if ($born> 0) + $newUser->setBornyear(ValidationHelper::filterAlphaNumericUnicde($born)); + + try { + $this->surveyUserMapper->insert($newUser); + $message = $this->l10n->t('Successful registration.'); + } catch (Exception $e) { + // TODO TODOFORMS Log + $message = $this->l10n->t( + 'Error occurred during the registration, please try again later.'); + $success = false; + } + } $data = [ + 'formid' => $id, 'mode' => 'return', 'su_email' => $su_email, - 'su_login' => $su_login, 'su_password' => $su_password, 'su_password2' => $su_password2, 'su_address' => $su_address, 'su_born' => $su_born, + 'su_realname' => $su_realname, 'success' => $success, 'message' => $message, 'problems' => $problems, @@ -157,14 +274,47 @@ public function commitRegister($su_email, $this->l10n->t('Please check your details')); } + public function surveyUserLoginPage($formId = null, + $success = null, + $message = null) : PublicTemplateResponse { + return $this->setupLoginPage([ + 'formid' => $formId, + 'message' => $message, + 'success' => $success + ], + $formId + ? $this->l10n->t('To access the survey, please log in') + : ''); + } + + private function setupLoginPage($data, + $subtitle = null) : PublicTemplateResponse { + return $this->setupPage( + $data, + $this->l10n->t('Survey user log in'), + $subtitle, + self::TEMPLATE_SURVEY_USER_LOGIN); + } + private function setupRegisterPage($data, $subtitle = null) : PublicTemplateResponse { + return $this->setupPage( + $data, + $this->l10n->t('Survey user registration'), + $subtitle, + self::TEMPLATE_SURVEY_USER_REGISTER); + } + + private function setupPage($data, + $title, + $subtitle, + $template) : PublicTemplateResponse { // TODO $template = new PublicTemplateResponse( $this->appName, - self::TEMPLATE_SURVEY_USER_REGISTER, + $template, $data); - $template->setHeaderTitle($this->l10n->t('Survey user registration')); + $template->setHeaderTitle($title); $template->setHeaderDetails($subtitle === null ? $this->l10n->t('Please enter your details') : $subtitle diff --git a/lib/Db/SurveyUser.php b/lib/Db/SurveyUser.php index c2ee4375f..46ed70217 100644 --- a/lib/Db/SurveyUser.php +++ b/lib/Db/SurveyUser.php @@ -31,8 +31,8 @@ /** * @method integer getFormId() * @method void setFormId(integer $value) - * @method string getLogin() - * @method void setLogin(string $value) + * @method string getPasswordhash() + * @method void setPasswordhash(string $value) * @method string getRealname() * @method void setRealname(string $value) * @method bool getIsdeleted() @@ -48,7 +48,7 @@ */ class SurveyUser extends Entity { - protected $login; + protected $passwordhash; protected $realname; protected $isdeleted; protected $address; @@ -61,20 +61,20 @@ class SurveyUser extends Entity */ public function __construct() { - $this->addType('login', 'string'); $this->addType('realname', 'string'); $this->addType('isdeleted', 'bool'); $this->addType('address', 'string'); $this->addType('email', 'string'); $this->addType('confirmcode', 'string'); $this->addType('bornyear', 'integer'); + $this->addType('passwordhash', 'string'); } public function read(): array { return [ 'id' => $this->getId(), - 'login' => $this->getLogin(), + 'passwordhash' => $this->getPasswordhash(), 'realname' => $this->getRealname(), 'isdeleted' => $this->getIsdeleted(), 'address' => $this->getAddress(), diff --git a/lib/Db/SurveyUserMapper.php b/lib/Db/SurveyUserMapper.php index 1ca8c9f20..2543d7d0b 100644 --- a/lib/Db/SurveyUserMapper.php +++ b/lib/Db/SurveyUserMapper.php @@ -51,6 +51,7 @@ public function findByEmail(string $email) { $qb = $this->db->getQueryBuilder(); + // TODO TODOFORMS check for deleted flag and modify comment $qb->select('*') ->from($this->getTableName()) ->where( @@ -60,25 +61,4 @@ public function findByEmail(string $email) return $this->findEntity($qb); } - /** - * Find a user by login name. (Either deleted or active) - * - * @param string $login - * @return SurveyUser|\OCP\AppFramework\Db\Entity - * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result - * @throws \OCP\AppFramework\Db\DoesNotExistException if not found - */ - public function findByLogin(string $login) - { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from($this->getTableName()) - ->where( - $qb->expr()->eq('login', $qb->createNamedParameter($login, IQueryBuilder::PARAM_STR)) - ); - - return $this->findEntity($qb); - } - } diff --git a/lib/Helper/RandomHelper.php b/lib/Helper/RandomHelper.php new file mode 100644 index 000000000..f9d3b3682 --- /dev/null +++ b/lib/Helper/RandomHelper.php @@ -0,0 +1,48 @@ + + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Helper; + +use Exception; + +class RandomHelper +{ + /** + * Create (crypto) random alphanumeric lower and uppercase string with the + * given byte len with (PHP 7) random_int — Generates cryptographically + * secure pseudo-random integers + * + * @param int $length Length to generate the string for + * @return string Generated random string + * @throws Exception + */ + public static function randomStr($length) + { + $keyspace = + '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $str = ''; + $max = mb_strlen($keyspace, '8bit') - 1; + for ($i = 0; $i < $length; ++$i) $str .= $keyspace[random_int(0, $max)]; + return $str; + } +} diff --git a/lib/Helper/ValidationHelper.php b/lib/Helper/ValidationHelper.php new file mode 100644 index 000000000..0b42caf00 --- /dev/null +++ b/lib/Helper/ValidationHelper.php @@ -0,0 +1,41 @@ + + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Helper; + +class ValidationHelper +{ + /** + * Filter input for unicode alphanumeric characters + * + * @param string $input String to filter + * @param int $len Maximum length + * @return false|string Filtered string or false if there was no result + */ + public static function filterAlphaNumericUnicde($input, + $len = 200) + { + return substr(preg_replace("/[^[:alnum:][:space:]]/u", '', + $input), 0, $len); + } +} diff --git a/lib/Migration/Version020005Date20201019161000.php b/lib/Migration/Version020005Date20201019161000.php index e6055c344..b817d060b 100644 --- a/lib/Migration/Version020005Date20201019161000.php +++ b/lib/Migration/Version020005Date20201019161000.php @@ -55,9 +55,9 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt 'length' => 11, 'unsigned' => true ]); - $table->addColumn('login', TYPE::STRING, [ + $table->addColumn('passwordhash', TYPE::STRING, [ 'notnull' => true, - 'length' => 100 + 'length' => 200 ]); $table->addColumn('realname', TYPE::STRING, [ 'notnull' => false, @@ -84,7 +84,6 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt ]); $table->setPrimaryKey(['id']); - $table->addUniqueIndex(['login'], 'forms_su_login'); $table->addUniqueIndex(['email', 'isdeleted'], 'forms_su_email'); $table->addIndex(['confirmcode'], 'forms_su_confirmcode'); } diff --git a/lib/Service/SurveyUserService.php b/lib/Service/SurveyUserService.php index 216a74dba..413598734 100644 --- a/lib/Service/SurveyUserService.php +++ b/lib/Service/SurveyUserService.php @@ -24,8 +24,11 @@ namespace OCA\Forms\Service; use OCA\Forms\Db\FormMapper; +use OCA\Forms\Db\SurveyUser; use OCA\Forms\Db\SurveyUserMapper; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\IMapperException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\ILogger; /** @@ -43,6 +46,8 @@ class SurveyUserService /** @var ILogger */ private $logger; + public const SURVEY_USER_SESSION_ID = 'FORMS_SURVEY_USER'; + public function __construct(FormMapper $formMapper, SurveyUserMapper $surveyUserMapper, ILogger $logger) { @@ -52,23 +57,59 @@ public function __construct(FormMapper $formMapper, } public function isSurveyUserLoggedIn() { - return false; + $user = \OC::$server->getSession()->get(self::SURVEY_USER_SESSION_ID); + return $user === null || ((int)$user) === 0; } - public function getCurrentSurveyUser() { - return false; + public function setCurrentSurveyUser($userId) { + \OC::$server->getSession()->set(self::SURVEY_USER_SESSION_ID, $userId); + } + + public function getCurrentSurveyUser() : ?SurveyUser { + if (!$this->isSurveyUserLoggedIn()) + return null; + + try { + $user = $this->surveyUserMapper->get( + $_SESSION[self::SURVEY_USER_SESSION_ID]); + return $user; + } catch (IMapperException $e) { + // TODO FORMSTODO log + return null; + } } /** - * @param $loginToCheck - * @return bool - * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + * Check if the user name is available to register + * + * @param string $loginToCheck Username to check + * @return bool True if the username is not taken */ public function isUserNameAvailable($loginToCheck) { try { $this->surveyUserMapper->findByLogin($loginToCheck); } catch (DoesNotExistException $e) { return true; + } catch (MultipleObjectsReturnedException $e) { + return false; + } + + return false; + } + + /** + * Check if the email is available to register + * + * @param string $emailToCheck Email to check + * @return bool True if the email is not taken + */ + public function isEmailAvailable($emailToCheck) { + try { + $this->surveyUserMapper->findByEmail($emailToCheck); + } catch (DoesNotExistException $e) { + return true; + } catch (MultipleObjectsReturnedException $e) { + return false; } return false; diff --git a/templates/surveyuserlogin.php b/templates/surveyuserlogin.php index 5db1e7be8..811059041 100644 --- a/templates/surveyuserlogin.php +++ b/templates/surveyuserlogin.php @@ -21,37 +21,54 @@ * */ -$routeRegister = \OC::$server->getURLGenerator()->linkToRoute('forms.sureveyUser.register'); -$routeLogin = \OC::$server->getURLGenerator()->linkToRoute('forms.sureveyUser.login'); +$routeRegister = \OC::$server->getURLGenerator() + ->linkToRoute('forms.sureveyUser.register', ['id' => $_['formid']]); +$routeLogin = \OC::$server->getURLGenerator() + ->linkToRoute('forms.sureveyUser.login', ['id' => $_['formid']]); + +var_dump(\OC::$server->getSession()->get(self::SURVEY_USER_SESSION_ID)); ?>
    - -

    t('This form is for registered users only. If you already have an account, please log.')); ?>

    - -
    -
    - - -
    -
    - - -
    + +
    - +
    -
    + + + +

    t('This form is for registered users only. If you already have an account, please log in.')); ?>

    + + +
    + +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    -

    t('If you don\'t have an account yet, you can register by clicking here.', $routeRegister)); ?>

    +

    t('If you don\'t have an account yet, you can register by clicking here.', $routeRegister)); ?>

    +
    diff --git a/templates/surveyuserregister.php b/templates/surveyuserregister.php index e82a6d603..a61af36f4 100644 --- a/templates/surveyuserregister.php +++ b/templates/surveyuserregister.php @@ -21,82 +21,98 @@ * */ -$routeRegister = \OC::$server->getURLGenerator()->linkToRoute('forms.sureveyUser.commitRegister'); +$routeRegister = \OC::$server->getURLGenerator() + ->linkToRoute('forms.sureveyUser.commitRegister', ['id' => $_['formid']]); +$routeLogin = \OC::$server->getURLGenerator() + ->linkToRoute('forms.sureveyUser.loginForm', ['id' => $_['formid']]); ?>
    - - 0): ?> -
    - 0) { - print_unescaped( - '
    • '. - join('
    • ', $_['problems']). - '
    '); - } - ?> -
    - - -

    t('Please provide your details below.')); ?>

    + -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    - - +

    + +

    +

    + t('Please click here to log in.', $routeLogin)); ?> +

    + + + + 0): ?>
    - + 0) { + print_unescaped( + '
    • '. + join('
    • ', $_['problems']). + '
    '); + } + ?>
    -
    + + +

    t('Please provide your details below.')); ?>

    + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    + +
    From d03d042757e351c9f1fb8fb38e8b118a627b66ec Mon Sep 17 00:00:00 2001 From: Zeon Date: 2020年10月27日 20:37:19 +0000 Subject: [PATCH 03/35] NC session fix --- lib/Controller/SureveyUserController.php | 14 ++++---------- lib/Db/SurveyUserMapper.php | 19 +++++++++++++++++++ lib/Service/SurveyUserService.php | 24 +++++++++++++++++------- templates/surveyuserlogin.php | 14 +++++++++++++- 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php index 54fdf7e05..a98308f7f 100644 --- a/lib/Controller/SureveyUserController.php +++ b/lib/Controller/SureveyUserController.php @@ -88,16 +88,11 @@ public function __construct(string $appName, * * @PublicPage * @NoCSRFRequired - * @NoSameSiteCookieRequired - * @CORS * * @param string $id Form id to redirect to after the login * @return Response Login page */ public function login($id, $su_email, $su_password): Response { - //https://docs.nextcloud.com/server/15/developer_manual/app/requests/controllers.html - //$response->addCookie('foo', 'bar'); - if (!$su_email || !$su_password) { return $this->surveyUserLoginPage( $id, @@ -111,7 +106,7 @@ public function login($id, $su_email, $su_password): Response { try { $user = $this->surveyUserMapper->findByEmail($su_email); if ($user && password_verify($su_password, $user->getPasswordhash())) { - $this->surveyUserService->setCurrentSurveyUser($user->getId()); + $this->surveyUserService->setCurrentSurveyUserId($user->getId()); $success = true; } } catch (IMapperException $e) { @@ -145,8 +140,6 @@ public function login($id, $su_email, $su_password): Response { * * @PublicPage * @NoCSRFRequired - * @NoSameSiteCookieRequired - * @CORS * * @param string $id Form id to redirect to after the login * @return Response Login page @@ -162,8 +155,6 @@ public function loginForm($id): Response { * * @PublicPage * @NoCSRFRequired - * @NoSameSiteCookieRequired - * @CORS * * @return TemplateResponse The template with the registration details */ @@ -181,6 +172,9 @@ public function register($id): PublicTemplateResponse { * * @PublicPage * @NoCSRFRequired + * + * TODO TODOFORM delete these + * * @NoSameSiteCookieRequired * @CORS * diff --git a/lib/Db/SurveyUserMapper.php b/lib/Db/SurveyUserMapper.php index 2543d7d0b..ce32f8fbf 100644 --- a/lib/Db/SurveyUserMapper.php +++ b/lib/Db/SurveyUserMapper.php @@ -39,6 +39,25 @@ public function __construct(IDBConnection $db) { parent::__construct($db, 'forms_surveyusers', SurveyUser::class); } + /** + * @param int $id Load a user with a specific ID + * @return \OCP\AppFramework\Db\Entity + * @throws \OCP\AppFramework\Db\DoesNotExistException + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + */ + public function load(int $id) { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from($this->getTableName()) + ->where( + $qb->expr()->eq('id', + $qb->createNamedParameter($id)) + ); + + return $this->findEntity($qb); + } + /** * Find a user by e-mail address. (Either deleted or active) * diff --git a/lib/Service/SurveyUserService.php b/lib/Service/SurveyUserService.php index 413598734..8b5540f26 100644 --- a/lib/Service/SurveyUserService.php +++ b/lib/Service/SurveyUserService.php @@ -46,23 +46,33 @@ class SurveyUserService /** @var ILogger */ private $logger; - public const SURVEY_USER_SESSION_ID = 'FORMS_SURVEY_USER'; + public const SURVEY_USER_SESSION_ID = 'FormsSurveyUserId'; public function __construct(FormMapper $formMapper, SurveyUserMapper $surveyUserMapper, ILogger $logger) { + // We need the session if we want to use survey user login sessions + session_start(); $this->formMapper = $formMapper; $this->surveyUserMapper = $surveyUserMapper; $this->logger = $logger; } public function isSurveyUserLoggedIn() { - $user = \OC::$server->getSession()->get(self::SURVEY_USER_SESSION_ID); - return $user === null || ((int)$user) === 0; + $user = $this->getCurrentSurveyUserId(); + return $user !== null && ((int)$user)> 0; } - public function setCurrentSurveyUser($userId) { - \OC::$server->getSession()->set(self::SURVEY_USER_SESSION_ID, $userId); + public function setCurrentSurveyUserId($userId) { + session_start(); + // \OC::$server->getSession()->set(self::SURVEY_USER_SESSION_ID, $userId); + $_SESSION[self::SURVEY_USER_SESSION_ID] = $userId; + } + + public function getCurrentSurveyUserId() { + session_start(); + // return \OC::$server->getSession()->get(self::SURVEY_USER_SESSION_ID); + return $_SESSION[self::SURVEY_USER_SESSION_ID]; } public function getCurrentSurveyUser() : ?SurveyUser { @@ -70,8 +80,8 @@ public function getCurrentSurveyUser() : ?SurveyUser { return null; try { - $user = $this->surveyUserMapper->get( - $_SESSION[self::SURVEY_USER_SESSION_ID]); + $user = $this->surveyUserMapper->load( + $this->getCurrentSurveyUserId()); return $user; } catch (IMapperException $e) { // TODO FORMSTODO log diff --git a/templates/surveyuserlogin.php b/templates/surveyuserlogin.php index 811059041..39804ae8f 100644 --- a/templates/surveyuserlogin.php +++ b/templates/surveyuserlogin.php @@ -26,7 +26,19 @@ $routeLogin = \OC::$server->getURLGenerator() ->linkToRoute('forms.sureveyUser.login', ['id' => $_['formid']]); -var_dump(\OC::$server->getSession()->get(self::SURVEY_USER_SESSION_ID)); +//\OC::$server->getSession()->set('fake_ss', 3); +echo('FAKE value ['. (\OC::$server->getSession()->get('fake_ss')).'] hasval: '. + (\OC::$server->getSession()->exists('fake_ss')) + ); +var_dump(\OC::$server->getSession()->get(\OCA\Forms\Service\SurveyUserService::SURVEY_USER_SESSION_ID)); +var_dump(\OC::$server->getSession()->getId()); + +//$_SESSION['fake_s2'] = 'ok'; + +echo('FAKE3: '.$_SESSION['fake_s2']); +echo(' FAKE4: '.$_SESSION[\OCA\Forms\Service\SurveyUserService::SURVEY_USER_SESSION_ID]); + +//var_dump($_SESSION); ?> From b32a2422e36ffb5cbe8c1c186581990971b47089 Mon Sep 17 00:00:00 2001 From: Zeon Date: Mon, 2 Nov 2020 09:30:27 +0000 Subject: [PATCH 04/35] Settings form alapok megvan, csrf kell es mentes --- appinfo/info.xml | 7 +- appinfo/routes.php | 3 + css/survey.scss | 81 +++++++++ img/forms-dark.svg | 56 ++++++ lib/Controller/ApiController.php | 22 ++- lib/Controller/PageController.php | 13 +- lib/Controller/SettingsController.php | 212 +++++++++++++++++++++++ lib/Controller/SureveyUserController.php | 65 +++++-- lib/Service/FormsService.php | 28 ++- lib/Service/SurveyUserService.php | 1 + lib/Settings/Admin.php | 83 +++++++++ lib/Settings/Section.php | 78 +++++++++ src/Forms.vue | 6 + src/components/AppNavigationSettings.vue | 17 ++ src/views/Create.vue | 2 + src/views/Submit.vue | 2 + templates/settings.php | 38 ++++ templates/surveyuserlogin.php | 85 +++++---- templates/surveyuserregister.php | 158 +++++++++-------- 19 files changed, 815 insertions(+), 142 deletions(-) create mode 100644 css/survey.scss create mode 100644 img/forms-dark.svg create mode 100644 lib/Controller/SettingsController.php create mode 100644 lib/Settings/Admin.php create mode 100644 lib/Settings/Section.php create mode 100644 src/components/AppNavigationSettings.vue create mode 100644 templates/settings.php diff --git a/appinfo/info.xml b/appinfo/info.xml index 65441cf62..f5056cedb 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -11,7 +11,7 @@ - **🔒 Data under your control!** Unlike in Google Forms, Typeform, Doodle and others, the survey info and responses are kept private on your instance. - **🙋 Get involved!** We have lots of stuff planned like more question types, collaboration on forms, [and much more](https://github.com/nextcloud/forms/milestones)! ]]> - 2.0.5 + 2.0.8 agpl Affan Hussain @@ -56,4 +56,9 @@ 77 + + + OCA\Forms\Settings\Admin + OCA\Forms\Settings\Section + diff --git a/appinfo/routes.php b/appinfo/routes.php index 9bfa7627b..b0c2feda1 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -33,6 +33,9 @@ ['name' => 'sureveyUser#loginForm', 'url' => '/sureveyuser/login/form/{id}', 'verb' => 'GET', 'defaults' => ['id' => '']], ['name' => 'sureveyUser#login', 'url' => '/sureveyuser/login/{id}', 'verb' => 'POST', 'defaults' => ['id' => '']], + // Settings + ['name' => 'settings#postSettings', 'url' => '/settings', 'verb' => 'POST'], + // Before /{hash} to avoid conflict ['name' => 'page#index', 'url' => '/new', 'verb' => 'GET', 'postfix' => 'create'], ['name' => 'page#index', 'url' => '/{hash}/edit', 'verb' => 'GET', 'postfix' => 'edit'], diff --git a/css/survey.scss b/css/survey.scss new file mode 100644 index 000000000..e48a83657 --- /dev/null +++ b/css/survey.scss @@ -0,0 +1,81 @@ +/** + * @copyright Copyright (c) 2020 John Molakvoæ + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +.survey-user-center { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + + .survey-user-message { + text-align: left; + padding: 25px; + color: $color-error; + border: 1px $color-error solid; + border-radius: $border-radius; + + li { + padding: 5px; + list-style: circle; + margin-left: 45px; + } + } + + .survey-user-success { + text-align: left; + padding: 25px; + color: $color-success; + + a, a.visited, a.hover, a.active { + color: $color-success; + text-decoration: underline; + } + } + + .survey_user_form, .survey_register_form { + text-align: left; + display: block; + padding: 20px 0px; + + div { + display: flex; + + label { + text-align: right; + flex: 1; + padding-right: 10px; + margin-top: 11px; + } + input { + flex: 2; + } + input.primary { + margin-top: 20px; + } + } + } + + .survey_user_login { + min-width: 70%; + max-width: 600px; + } +} diff --git a/img/forms-dark.svg b/img/forms-dark.svg new file mode 100644 index 000000000..306c0a5e5 --- /dev/null +++ b/img/forms-dark.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index e62b3cb66..58cc607bc 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -715,6 +715,10 @@ public function getSubmissions(string $hash): DataResponse { if (substr($submission['userId'], 0, 10) === 'anon-user-') { // Anonymous User $submission['userDisplayName'] = $this->l10n->t('Anonymous response'); + } else if (substr($submission['userId'], 0, 12) === 'survey-user-') { + // Survey User. They are anonymous unless the user has extra + // rights to expose the name + $submission['userDisplayName'] = $this->l10n->t('Registered survey user response'); } else { $userEntity = $this->userManager->get($submission['userId']); @@ -761,6 +765,7 @@ public function insertSubmission(int $formId, array $answers): DataResponse { try { $form = $this->formMapper->findById($formId); $questions = $this->formsService->getQuestions($formId); + $access = $form->getAccess(); } catch (IMapperException $e) { $this->logger->debug('Could not find form'); throw new OCSBadRequestException(); @@ -786,12 +791,19 @@ public function insertSubmission(int $formId, array $answers): DataResponse { $submission->setFormId($formId); $submission->setTimestamp(time()); - // If not logged in or anonymous use anonID - if (!$this->currentUser || $form->getIsAnonymous()) { - $anonID = "anon-user-". hash('md5', (time() + rand())); - $submission->setUserId($anonID); + if ($access['type'] === 'surveyusers') { + $surveyUserID = + SurveyUserService::SURVEY_USER_DB_PREFIX. + $this->surveyUserService->getCurrentSurveyUserId(); + $submission->setUserId($surveyUserID); } else { - $submission->setUserId($this->currentUser->getUID()); + // If not logged in or anonymous use anonID + if (!$this->currentUser || $form->getIsAnonymous()) { + $anonID = "anon-user-". hash('md5', (time() + rand())); + $submission->setUserId($anonID); + } else { + $submission->setUserId($this->currentUser->getUID()); + } } // Insert new submission diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 142deef18..f04da9ff2 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -164,7 +164,11 @@ public function gotoForm($hash): ?TemplateResponse { // Does the user have permissions to submit (resp. submitOnce) if (!$this->formsService->canSubmit($form->getId())) { - return $this->provideTemplate(self::TEMPLATE_NOSUBMIT, $form); + $template = $this->provideTemplate(self::TEMPLATE_NOSUBMIT, $form); + // Add survey user profile/logout menu if necessary + if ($this->formsService->isSurveyUserForm($form)) + $this->sureveyUserController->addSurveyUserMenu($template); + return $template; } // Has form expired @@ -176,7 +180,12 @@ public function gotoForm($hash): ?TemplateResponse { Util::addScript($this->appName, 'forms-submit'); $this->initialStateService->provideInitialState($this->appName, 'form', $this->formsService->getPublicForm($form->getId())); $this->initialStateService->provideInitialState($this->appName, 'maxStringLengths', $this->maxStringLengths); - return $this->provideTemplate(self::TEMPLATE_MAIN, $form); + + $template = $this->provideTemplate(self::TEMPLATE_MAIN, $form); + if ($this->formsService->isSurveyUserForm($form)) + $this->sureveyUserController->addSurveyUserMenu($template); + + return $template; } /** diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php new file mode 100644 index 000000000..2b5690d96 --- /dev/null +++ b/lib/Controller/SettingsController.php @@ -0,0 +1,212 @@ + + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Controller; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\ILogger; +use OCP\IRequest; +use OCP\IUserManager; + +class SettingsController extends Controller +{ + /** @var IUserManager */ + private $userManager; + + /** @var IGroupManager */ + protected $groupManager; + + /** @var IConfig */ + protected $config; + + /** @var ILogger */ + private $logger; + + public const FORMS_ARRAY_SEPARATOR = + "\n"; + public const CONFIG_BOOL_TRUE = + 'true'; + public const CONFIG_BOOL_FALSE = + 'false'; + + public const CONFIG_CREATE_FORMS_GROUPS = + 'FormsCreateAllowed'; + public const CONFIG_VIEW_RESULTS_FORMS_GROUPS = + 'FormsViewResults'; + + public function __construct($AppName, + IRequest $request, + ILogger $logger, + IUserManager $userManager, + IGroupManager $groupManager, + IConfig $config, + $UserId) { + parent::__construct($AppName, $request); + $this->userManager = $userManager; + $this->groupManager = $groupManager; + $this->logger = $logger; + $this->userId = $UserId; + $this->config = $config; + } + + /** + * Admin only, this method handles the settings post route + */ + public function postSettings() { + // TODO SAVE + return $this->getForm(); + } + + /** + * Display the settings page + */ + public function getForm() + { + $createGroupList = []; + $viewGroupList = []; + + $configViewGroups = + $this->getFormsViewResultsGroups(); + $configCreateGroups = + $this->getFormsCreatorGroups(); + + foreach ($this->groupManager->search('') as $group) { + $id = $group->getGID(); + $viewGroupList[] = [ + 'name' => $group->getDisplayName(), + 'id' => $group->getGID(), + 'selected' => in_array($id, $configViewGroups) + ]; + $createGroupList[] = [ + 'name' => $group->getDisplayName(), + 'id' => $group->getGID(), + 'selected' => in_array($id, $configCreateGroups) + ]; + } + + $data = [ + 'createGroups' => $createGroupList, + 'viewGroups' => $viewGroupList + ]; + + return new TemplateResponse( + 'forms', + 'settings', + $data); + } + + /** + * Retrieve a value from the NC app settings + * + * @param string $valName Name of a setting entry + * @return mixed Setting value retrieved + */ + private function getVal($valName) { + return ($this->config->getAppValue($this->appName, $valName)); + } + + /** + * Retrieve a bool value from the NC app settings + * + * @param string $valName Name of a setting entry + * @return bool Setting value retrieved + */ + private function getBoolVal($valName) { + return ($this->config->getAppValue($this->appName, $valName)) + === self::CONFIG_BOOL_TRUE; + } + + /** + * Store a value in the NC app settings + * + * @param string $valName Name of a setting entry + * @param mixed $value Item to store + */ + private function setVal($valName, $value) { + $this->config->setAppValue($this->appName, $valName, $value); + } + + /** + * Store a bool value in the NC app settings + * + * @param string $valName Name of a setting entry + * @param bool $value Item to store + */ + private function setBoolVal($valName, $value) { + $this->config->setAppValue($this->appName, $valName, $value ? + self::CONFIG_BOOL_TRUE : self::CONFIG_BOOL_FALSE); + } + + /** + * General setting to array converter, retrieves an array form the NC app + * settings + * + * @param string $valName Name of a setting entry + * @return array Setting value retrieved + */ + private function getArray($valName) { + $val = $this->getVal($valName); + // We don't want to go rounds with the explode and filter if it will be + // a completely empty array + if ($val === null || $val === '') return []; + + $arr = (explode(self::FORMS_ARRAY_SEPARATOR, + $val)); + + if ($arr === null || !is_array($arr)) + return []; + else + return $arr; + } + + /** + * General array to setting converter, this one stores an array in the NC + * app settings + * + * @param string $valName Name of a setting entry + * @param array $array array to save in the settings + */ + private function setArray($valName, $array) { + $this->setVal($valName, implode(self::FORMS_ARRAY_SEPARATOR, + $array)); + } + + /** + * @return array List of the groups allowed to create forms + */ + public function getFormsCreatorGroups() + { + return $this->getArray(self::CONFIG_CREATE_FORMS_GROUPS); + } + + /** + * @return array List of the groups allowed to view forms results + */ + public function getFormsViewResultsGroups() + { + return $this->getArray(self::CONFIG_VIEW_RESULTS_FORMS_GROUPS); + } +} diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php index a98308f7f..e01d748c7 100644 --- a/lib/Controller/SureveyUserController.php +++ b/lib/Controller/SureveyUserController.php @@ -38,10 +38,12 @@ use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\Template\PublicTemplateResponse; +use OCP\AppFramework\Http\Template\SimpleMenuAction; use OCP\AppFramework\Http\TemplateResponse; use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; +use OCP\Util; class SureveyUserController extends Controller { public const TEMPLATE_SURVEY_USER_LOGIN = 'surveyuserlogin'; @@ -158,7 +160,7 @@ public function loginForm($id): Response { * * @return TemplateResponse The template with the registration details */ - public function register($id): PublicTemplateResponse { + public function register($id): Response { return $this->setupRegisterPage([ 'formid' => $id, 'mode' => 'first' @@ -186,10 +188,7 @@ public function commitRegister($id, $su_password2, $su_realname, $su_address, - $su_born): PublicTemplateResponse { - - $su_email = $_POST['su_email']; - + $su_born): TemplateResponse { $success = true; $message = ''; $problems = []; @@ -270,7 +269,7 @@ public function commitRegister($id, public function surveyUserLoginPage($formId = null, $success = null, - $message = null) : PublicTemplateResponse { + $message = null) : TemplateResponse { return $this->setupLoginPage([ 'formid' => $formId, 'message' => $message, @@ -282,7 +281,7 @@ public function surveyUserLoginPage($formId = null, } private function setupLoginPage($data, - $subtitle = null) : PublicTemplateResponse { + $subtitle = null) : TemplateResponse { return $this->setupPage( $data, $this->l10n->t('Survey user log in'), @@ -290,8 +289,17 @@ private function setupLoginPage($data, self::TEMPLATE_SURVEY_USER_LOGIN); } + /** + * Prepare a registration page for the survey users + * + * @param array $data Data to be passed to the PHP template + * @param null|string $subtitle Left bottom NC title + * @return TemplateResponse Regsitration page + */ private function setupRegisterPage($data, - $subtitle = null) : PublicTemplateResponse { + $subtitle = null) : TemplateResponse { + Util::addStyle($this->appName, 'survey'); + return $this->setupPage( $data, $this->l10n->t('Survey user registration'), @@ -299,20 +307,57 @@ private function setupRegisterPage($data, self::TEMPLATE_SURVEY_USER_REGISTER); } + /** + * Add header actions for the logged in survey users + * + * @param TemplateResponse $response Add the menu actions to this response + * @return TemplateResponse The response we got easier caller syntax + */ + public function addSurveyUserMenu(TemplateResponse $response) : TemplateResponse { + // Only provide menu for the users when they are logged in + if (!$this->surveyUserService->isSurveyUserLoggedIn()) + return $response; + + $profileUrl = 'http://'; + $logoutUrl = 'http://2'; + $docsUrlPrivacyPolicy = 'http://2'; + $docsUrlTermsOfUse = 'http://2'; + $profile = new SimpleMenuAction('profile', $this->l10n->t('View profile'), 'icon-user', $profileUrl, 0); + $docs1 = new SimpleMenuAction('tos', $this->l10n->t('Terms of use'), 'icon-info', $docsUrlTermsOfUse, 1); + $docs2 = new SimpleMenuAction('ppolicy', $this->l10n->t('Privacy Policy'), 'icon-info', $docsUrlPrivacyPolicy, 2); + $logout = new SimpleMenuAction('logout', $this->l10n->t('Log out'), 'icon-close', $logoutUrl, 3); + $response->setHeaderActions([$profile, $docs1, $docs2, $logout]); + return $response; + } + + /** + * Create a general public page for the survey users + * + * @param array $data Data passed to the template + * @param string $title Left top title in NC + * @param string $subtitle Left bottom title in NC + * @param string $template PHP template name + * @return TemplateResponse The created response + */ private function setupPage($data, $title, $subtitle, - $template) : PublicTemplateResponse { + $template) : TemplateResponse + { + Util::addStyle($this->appName, 'survey'); + // TODO $template = new PublicTemplateResponse( $this->appName, $template, $data); + $template->setHeaderTitle($title); $template->setHeaderDetails($subtitle === null ? $this->l10n->t('Please enter your details') : $subtitle ); - return $template; + + return $this->addSurveyUserMenu($template); } } diff --git a/lib/Service/FormsService.php b/lib/Service/FormsService.php index 6a2eab655..a8c05b1de 100644 --- a/lib/Service/FormsService.php +++ b/lib/Service/FormsService.php @@ -23,6 +23,7 @@ namespace OCA\Forms\Service; +use OCA\Forms\Db\Form; use OCA\Forms\Db\FormMapper; use OCA\Forms\Db\OptionMapper; use OCA\Forms\Db\QuestionMapper; @@ -166,6 +167,9 @@ public function getForm(int $id): array { public function getPublicForm(int $id): array { $form = $this->getForm($id); + // Keep the form type after we cut down the access info + $form['isSurveyUserForm'] = ($form['access']['type'] === 'surveyusers'); + // Remove sensitive data unset($form['access']); unset($form['ownerId']); @@ -187,9 +191,20 @@ public function canSubmit($formId) { // Refuse access, if SubmitOnce is set and user already has taken part. if ($form->getSubmitOnce()) { + if ($access['type'] === 'surveyusers') { + // Check for forms with survey users + $lookupId = + SurveyUserService::SURVEY_USER_DB_PREFIX. + $this->surveyUserService->getCurrentSurveyUserId(); + } else { + // Check for forms with internal users + $lookupId = $this->currentUser->getUID(); + } + + // TODO It should be more efficient if the ID went with the query $participants = $this->submissionMapper->findParticipantsByForm($form->getId()); foreach ($participants as $participant) { - if ($participant === $this->currentUser->getUID()) { + if ($participant === $lookupId) { return false; } } @@ -213,6 +228,17 @@ public function isSurveyLoginRequired(int $formId): bool { && !$this->hasUserAccess($formId); } + /** + * Checks if the form is a survey user type form (requires separate login) + * + * @param Form $form + * @return bool True, if it's a survey user form + */ + public function isSurveyUserForm(Form $form) : bool { + $access = $form->getAccess(); + return $access['type'] === 'surveyusers'; + } + /** * Check if user has access to this form * diff --git a/lib/Service/SurveyUserService.php b/lib/Service/SurveyUserService.php index 8b5540f26..6e94b045b 100644 --- a/lib/Service/SurveyUserService.php +++ b/lib/Service/SurveyUserService.php @@ -47,6 +47,7 @@ class SurveyUserService private $logger; public const SURVEY_USER_SESSION_ID = 'FormsSurveyUserId'; + public const SURVEY_USER_DB_PREFIX = 'survey-user-'; public function __construct(FormMapper $formMapper, SurveyUserMapper $surveyUserMapper, diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php new file mode 100644 index 000000000..1a57bac1a --- /dev/null +++ b/lib/Settings/Admin.php @@ -0,0 +1,83 @@ + + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Settings; + +use OC\Group\Group; +use OCA\Forms\Controller\SettingsController; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IL10N; +use OCP\ILogger; + +class Admin implements \OCP\Settings\ISettings +{ + /** @var SettingsController */ + private $settingsController; + + /** @var IL10N */ + private $l; + + /** @var ILogger */ + private $logger; + + /** @var IGroupManager */ + private $groupManager; + + public function __construct(SettingsController $settingsController, + IL10N $l, + IGroupManager $groupManager, + ILogger $logger) { + $this->settingsController = $settingsController; + $this->l = $l; + $this->groupManager = $groupManager; + $this->logger = $logger; + } + + /** + * @inheritDoc + */ + public function getForm() + { + return $this->settingsController->getForm(); + } + + /** + * @inheritDoc + */ + public function getSection() + { + // TODO: Implement getSection() method. + return 'forms'; + } + + /** + * @inheritDoc + */ + public function getPriority() + { + // TODO: Implement getPriority() method. + return 90; + } +} diff --git a/lib/Settings/Section.php b/lib/Settings/Section.php new file mode 100644 index 000000000..773b62a5f --- /dev/null +++ b/lib/Settings/Section.php @@ -0,0 +1,78 @@ + + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Settings; + +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\Settings\IIconSection; + +class Section implements IIconSection { + + /** @var IL10N */ + private $l; + + /** @var IURLGenerator */ + private $urlGenerator; + + public function __construct(IL10N $l, IURLGenerator $urlGenerator) { + $this->l = $l; + $this->urlGenerator = $urlGenerator; + } + + /** + * returns the ID of the section. It is supposed to be a lower case string + * + * @returns string + */ + public function getID() { + return 'forms'; //or a generic id if feasible + } + + /** + * returns the translated name as it should be displayed, e.g. 'LDAP / AD + * integration'. Use the L10N service to translate it. + * + * @return string + */ + public function getName() { + return $this->l->t('Forms'); + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the settings navigation. The sections are arranged in ascending order of + * the priority values. It is required to return a value between 0 and 99. + */ + public function getPriority() { + return 80; + } + + /** + * @return string The relative path to a an icon describing the section + */ + public function getIcon() { + return $this->urlGenerator->imagePath('forms', 'forms-dark.svg'); + } + +} diff --git a/src/Forms.vue b/src/Forms.vue index fed8751b5..bd18fa3ef 100644 --- a/src/Forms.vue +++ b/src/Forms.vue @@ -32,6 +32,10 @@ @mobile-close-navigation="mobileCloseNavigation" @delete="onDeleteForm" /> + + + + @@ -80,6 +84,7 @@ import AppNavigationNew from '@nextcloud/vue/dist/Components/AppNavigationNew' import Content from '@nextcloud/vue/dist/Components/Content' import isMobile from '@nextcloud/vue/src/mixins/isMobile' +import AppNavigationSettings from './components/AppNavigationSettings.vue' import AppNavigationForm from './components/AppNavigationForm' import EmptyContent from './components/EmptyContent' @@ -88,6 +93,7 @@ export default { components: { AppNavigationForm, + AppNavigationSettings, AppContent, AppNavigation, AppNavigationNew, diff --git a/src/components/AppNavigationSettings.vue b/src/components/AppNavigationSettings.vue new file mode 100644 index 000000000..8e7390f03 --- /dev/null +++ b/src/components/AppNavigationSettings.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/views/Create.vue b/src/views/Create.vue index ea8d7ef00..fb48a159a 100644 --- a/src/views/Create.vue +++ b/src/views/Create.vue @@ -203,6 +203,8 @@ export default { let message = '' if (this.form.isAnonymous) { message += t('forms', 'Responses are anonymous.') + } else if (this.form.isSurveyUserForm) { + message += t('forms', 'Responses are connected to your account.') } else { message += t('forms', 'Responses are connected to your Nextcloud account.') } diff --git a/src/views/Submit.vue b/src/views/Submit.vue index c3db3bc1a..baba1662f 100644 --- a/src/views/Submit.vue +++ b/src/views/Submit.vue @@ -149,6 +149,8 @@ export default { let message = '' if (this.form.isAnonymous) { message += t('forms', 'Responses are anonymous.') + } else if (this.form.isSurveyUserForm) { + message += t('forms', 'Responses are connected to your account.') } else { message += t('forms', 'Responses are connected to your Nextcloud account.') } diff --git a/templates/settings.php b/templates/settings.php new file mode 100644 index 000000000..f00bed1c8 --- /dev/null +++ b/templates/settings.php @@ -0,0 +1,38 @@ +getURLGenerator() + ->linkToRoute('forms.settings.postSettings'); + +?> +
    + +
    +

    Create forms

    +

    + Please select the user groups allowed to create new forms: +

    + +

    type="checkbox" class="checkbox"> +

    + +
    +
    + +
    +

    View results

    +

    + Please select the user groups allowed to view new results: +

    + +

    type="checkbox" class="checkbox"> +

    + +
    +
    + +
    + +
    diff --git a/templates/surveyuserlogin.php b/templates/surveyuserlogin.php index 39804ae8f..0b36d1d1b 100644 --- a/templates/surveyuserlogin.php +++ b/templates/surveyuserlogin.php @@ -26,61 +26,54 @@ $routeLogin = \OC::$server->getURLGenerator() ->linkToRoute('forms.sureveyUser.login', ['id' => $_['formid']]); -//\OC::$server->getSession()->set('fake_ss', 3); -echo('FAKE value ['. (\OC::$server->getSession()->get('fake_ss')).'] hasval: '. - (\OC::$server->getSession()->exists('fake_ss')) - ); -var_dump(\OC::$server->getSession()->get(\OCA\Forms\Service\SurveyUserService::SURVEY_USER_SESSION_ID)); -var_dump(\OC::$server->getSession()->getId()); - -//$_SESSION['fake_s2'] = 'ok'; - -echo('FAKE3: '.$_SESSION['fake_s2']); -echo(' FAKE4: '.$_SESSION[\OCA\Forms\Service\SurveyUserService::SURVEY_USER_SESSION_ID]); - -//var_dump($_SESSION); - ?>
    - -
    - -
    - - - -

    t('This form is for registered users only. If you already have an account, please log in.')); ?>

    - - +
    +
    - + + -
    -
    - - -
    -
    - - -
    -
    - +
    +

    t('This form is for registered users only. If you already have an account, please log in.')); ?>

    + + +
    + +
    + + +
    + +
    + + +
    +
    + + +
    +
    + + +
    + +
    - -

    t('If you don\'t have an account yet, you can register by clicking here.', $routeRegister)); ?>

    - +

    t('If you don\'t have an account yet, you can register by clicking here.', $routeRegister)); ?>

    + +
    diff --git a/templates/surveyuserregister.php b/templates/surveyuserregister.php index a61af36f4..7980936ea 100644 --- a/templates/surveyuserregister.php +++ b/templates/surveyuserregister.php @@ -30,89 +30,93 @@
    - +
    + -
    -

    - -

    -

    - t('Please click here to log in.', $routeLogin)); ?> -

    -
    +
    +

    + +

    +

    + t('Please click here to log in.', $routeLogin)); ?> +

    +
    - + - 0): ?> -
    - 0) { - print_unescaped( - '
    • '. - join('
    • ', $_['problems']). - '
    '); - } - ?> -
    - + 0): ?> +
    + 0) { + print_unescaped( + '
    • '. + join('
    • ', $_['problems']). + '
    '); + } + ?> +
    + -

    t('Please provide your details below.')); ?>

    +
    +

    t('Please provide your details below.')); ?>

    -
    -
    - - -
    -
    - - -
    -
    - - + +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    - - + +
    From c52618aab40a43caa9f1516683fd57e6a9727aa8 Mon Sep 17 00:00:00 2001 From: Zeon Date: Mon, 2 Nov 2020 15:04:38 +0000 Subject: [PATCH 05/35] Settings megy --- lib/Controller/SettingsController.php | 64 ++++++++++++++++++++++++--- templates/settings.php | 25 ++++++++--- 2 files changed, 76 insertions(+), 13 deletions(-) diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 2b5690d96..50b1593b0 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -24,9 +24,12 @@ namespace OCA\Forms\Controller; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\IConfig; use OCP\IGroupManager; +use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; use OCP\IUserManager; @@ -45,6 +48,9 @@ class SettingsController extends Controller /** @var ILogger */ private $logger; + /** @var IL10N */ + private $l10n; + public const FORMS_ARRAY_SEPARATOR = "\n"; public const CONFIG_BOOL_TRUE = @@ -63,11 +69,13 @@ public function __construct($AppName, IUserManager $userManager, IGroupManager $groupManager, IConfig $config, + IL10N $l10n, $UserId) { parent::__construct($AppName, $request); $this->userManager = $userManager; $this->groupManager = $groupManager; $this->logger = $logger; + $this->l10n = $l10n; $this->userId = $UserId; $this->config = $config; } @@ -76,8 +84,35 @@ public function __construct($AppName, * Admin only, this method handles the settings post route */ public function postSettings() { - // TODO SAVE - return $this->getForm(); + $configViewGroups = []; + $configCreateGroups = []; + $error = []; + + foreach ($_POST as $key => $item) { + $this->checkFieldForGroup($configViewGroups, 'view-', $key, $error); + $this->checkFieldForGroup($configCreateGroups, 'create-', $key, $error); + } + + $this->setFormsCreatorGroups($configCreateGroups); + $this->setFormsViewResultsGroups($configViewGroups); + + if (count($error) === 0) + return new DataResponse('Ok', Http::STATUS_OK); + else + return new DataResponse(implode("\n", $error), Http::STATUS_BAD_REQUEST); + } + + private function checkFieldForGroup(array &$groupArray, + string $prefix, + string $field, + array &$errorArray) { + if (substr($field, 0, strlen($prefix)) === $prefix) { + $group = str_replace('_', ' ', substr($field, strlen($prefix))); + if ($this->groupManager->groupExists($group)) + $groupArray[] = $group; + else + $errorArray[] = $this->l10n->t('Invalid group name: %s', [$group]); + } } /** @@ -85,13 +120,12 @@ public function postSettings() { */ public function getForm() { + \OC_Util::addScript('forms', 'admin'); + $createGroupList = []; $viewGroupList = []; - - $configViewGroups = - $this->getFormsViewResultsGroups(); - $configCreateGroups = - $this->getFormsCreatorGroups(); + $configViewGroups = $this->getFormsViewResultsGroups(); + $configCreateGroups = $this->getFormsCreatorGroups(); foreach ($this->groupManager->search('') as $group) { $id = $group->getGID(); @@ -209,4 +243,20 @@ public function getFormsViewResultsGroups() { return $this->getArray(self::CONFIG_VIEW_RESULTS_FORMS_GROUPS); } + + /** + * @param array $groupList List of the groups allowed to create forms + */ + public function setFormsCreatorGroups($groupList) + { + return $this->setArray(self::CONFIG_CREATE_FORMS_GROUPS, $groupList); + } + + /** + * @param array $groupList List of the groups allowed to view forms results + */ + public function setFormsViewResultsGroups($groupList) + { + return $this->setArray(self::CONFIG_VIEW_RESULTS_FORMS_GROUPS, $groupList); + } } diff --git a/templates/settings.php b/templates/settings.php index f00bed1c8..07be461ca 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -4,18 +4,25 @@ ->linkToRoute('forms.settings.postSettings'); ?> -
    + name="forms_settings">
    +

    +

    Create forms

    Please select the user groups allowed to create new forms:

    -

    type="checkbox" class="checkbox"> +

    + type="checkbox" + value="yes" + class="checkbox">

    @@ -27,12 +34,18 @@ Please select the user groups allowed to view new results:

    -

    type="checkbox" class="checkbox"> -

    +

    + type="checkbox" + value="yes" + class="checkbox"> +

    -
    + +
    t('Save changes')); ?>">
    From d26c02a2be4bbc079cffd8f9e2926926728d0edd Mon Sep 17 00:00:00 2001 From: Zeon Date: Tue, 3 Nov 2020 14:59:16 +0000 Subject: [PATCH 06/35] Jogok kezelese, view GUI oldalon es create oldalon kesz, create api-nal meg kell --- appinfo/routes.php | 4 +- css/admin.scss | 34 ++++++++++ lib/Controller/ApiController.php | 14 +++- lib/Controller/SettingsController.php | 83 +++++++++++++++++++++++- lib/Service/FormsService.php | 10 +++ src/Forms.vue | 28 ++++++-- src/components/AppNavigationForm.vue | 3 +- src/components/AppNavigationSettings.vue | 17 ----- src/views/Create.vue | 3 +- templates/settings.php | 29 +++++++-- 10 files changed, 190 insertions(+), 35 deletions(-) create mode 100644 css/admin.scss delete mode 100644 src/components/AppNavigationSettings.vue diff --git a/appinfo/routes.php b/appinfo/routes.php index b0c2feda1..a36b5e9e1 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -66,8 +66,10 @@ // Submissions ['name' => 'api#getSubmissions', 'url' => '/api/v1/submissions/{hash}', 'verb' => 'GET'], ['name' => 'api#deleteAllSubmissions', 'url' => '/api/v1/submissions/{formId}', 'verb' => 'DELETE'], - ['name' => 'api#insertSubmission', 'url' => '/api/v1/submission/insert', 'verb' => 'POST'], ['name' => 'api#deleteSubmission', 'url' => '/api/v1/submission/{id}', 'verb' => 'DELETE'], + + // Access + ['name' => 'settings#getAccess', 'url' => '/api/v1/access', 'verb' => 'GET'], ] ]; diff --git a/css/admin.scss b/css/admin.scss new file mode 100644 index 000000000..ce54e8d2a --- /dev/null +++ b/css/admin.scss @@ -0,0 +1,34 @@ +/** + * @copyright Copyright (c) 2020 John Molakvoæ + * + * @author ACPM IT LTD + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +div.section-content { + position: relative; +} + +div.forms-access-curtain { + background: var(--color-main-background); + height: 100%; + width: 100%; + position: absolute; + z-index: 1700; + opacity: 0.8; +} diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index 58cc607bc..aa22aeb97 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -90,6 +90,9 @@ class ApiController extends Controller { /** @var SurveyUserService */ private $surveyUserService; + /** @var SettingsController */ + private $settingsController; + /** @var ISecureRandom */ private $secureRandom; @@ -105,6 +108,7 @@ public function __construct(string $appName, ILogger $logger, IL10N $l10n, FormsService $formsService, + SettingsController $settingsController, SurveyUserService $surveyUserService, ISecureRandom $secureRandom) { parent::__construct($appName, $request); @@ -117,6 +121,7 @@ public function __construct(string $appName, $this->answerMapper = $answerMapper; $this->questionMapper = $questionMapper; $this->optionMapper = $optionMapper; + $this->settingsController = $settingsController; $this->logger = $logger; $this->l10n = $l10n; $this->formsService = $formsService; @@ -141,7 +146,10 @@ public function getForms(): DataResponse { 'hash' => $form->getHash(), 'title' => $form->getTitle(), 'expires' => $form->getExpires(), - 'partial' => true + 'partial' => true, + // The current user can view survey results. This could be + // refined to a per form basis later if required + 'canViewResults' => $this->settingsController->canViewResults() ]; } @@ -687,6 +695,10 @@ private function getAnswers(int $submissionId): array { * @throws OCSForbiddenException */ public function getSubmissions(string $hash): DataResponse { + if ($this->settingsController->isAccessControlEnabled() && + !$this->settingsController->canViewResults()) + throw new OCSForbiddenException(); + try { $form = $this->formMapper->findByHash($hash); } catch (IMapperException $e) { diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 50b1593b0..e090c2412 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -33,6 +33,7 @@ use OCP\ILogger; use OCP\IRequest; use OCP\IUserManager; +use OCP\Util; class SettingsController extends Controller { @@ -51,6 +52,9 @@ class SettingsController extends Controller /** @var IL10N */ private $l10n; + /** @var string */ + private $userId; + public const FORMS_ARRAY_SEPARATOR = "\n"; public const CONFIG_BOOL_TRUE = @@ -58,6 +62,8 @@ class SettingsController extends Controller public const CONFIG_BOOL_FALSE = 'false'; + public const CONFIG_ADVANCED_ACCESS_CONTROL = + 'FormsEnableAdvancedAccessControl'; public const CONFIG_CREATE_FORMS_GROUPS = 'FormsCreateAllowed'; public const CONFIG_VIEW_RESULTS_FORMS_GROUPS = @@ -80,6 +86,56 @@ public function __construct($AppName, $this->config = $config; } + /** + * @NoAdminRequired + * + * Read the access for the current user as set in the settings + */ + public function getAccess(): DataResponse { + if ($this->isAccessControlEnabled()) { + $result = [ + 'canCreate' => $this->canCreateForms(), + 'canViewSurveyResults' => $this->canViewResults(), + ]; + } else { + $result = [ + 'canCreate' => true, + 'canViewSurveyResults' => true, + ]; + } + + return new DataResponse($result); + } + + /** + * @return bool True, if the current user can view the results + */ + public function canViewResults() : bool { + return $this->checkListForAccess($this->getFormsViewResultsGroups()); + } + + /** + * @return bool True, if the current user can create a form + */ + public function canCreateForms() : bool { + return $this->checkListForAccess($this->getFormsCreatorGroups()); + } + + /** + * @param array $accessList List of group IDS allowed + * @return bool True if the user has a group that is allowed + */ + private function checkListForAccess($accessList) : bool { + $userGroups = $this->groupManager->getUserGroups( + $this->userManager->get($this->userId)); + + foreach ($userGroups as $userGroup) + if (in_array($userGroup->getGID(), $accessList)) + return true; + + return false; + } + /** * Admin only, this method handles the settings post route */ @@ -95,6 +151,9 @@ public function postSettings() { $this->setFormsCreatorGroups($configCreateGroups); $this->setFormsViewResultsGroups($configViewGroups); + $this->setIsAccessControlEnabled( + isset($_POST['enable-access']) && $_POST['enable-access'] === 'yes' + ); if (count($error) === 0) return new DataResponse('Ok', Http::STATUS_OK); @@ -120,7 +179,8 @@ private function checkFieldForGroup(array &$groupArray, */ public function getForm() { - \OC_Util::addScript('forms', 'admin'); + \OC_Util::addScript($this->appName, 'admin'); + \OC_Util::addStyle($this->appName, 'admin'); $createGroupList = []; $viewGroupList = []; @@ -143,7 +203,8 @@ public function getForm() $data = [ 'createGroups' => $createGroupList, - 'viewGroups' => $viewGroupList + 'viewGroups' => $viewGroupList, + 'enableAccess' => $this->isAccessControlEnabled(), ]; return new TemplateResponse( @@ -259,4 +320,22 @@ public function setFormsViewResultsGroups($groupList) { return $this->setArray(self::CONFIG_VIEW_RESULTS_FORMS_GROUPS, $groupList); } + + /** + * @return bool True if the group-based access control should be considered + */ + public function isAccessControlEnabled() + { + return $this->getBoolVal(self::CONFIG_ADVANCED_ACCESS_CONTROL); + } + + /** + * @param bool $enabled True if the group-based access control should be + * considered + */ + public function setIsAccessControlEnabled($enabled) + { + return $this->setBoolVal(self::CONFIG_ADVANCED_ACCESS_CONTROL, + $enabled); + } } diff --git a/lib/Service/FormsService.php b/lib/Service/FormsService.php index a8c05b1de..bf671eb99 100644 --- a/lib/Service/FormsService.php +++ b/lib/Service/FormsService.php @@ -23,6 +23,7 @@ namespace OCA\Forms\Service; +use OCA\Forms\Controller\SettingsController; use OCA\Forms\Db\Form; use OCA\Forms\Db\FormMapper; use OCA\Forms\Db\OptionMapper; @@ -70,6 +71,9 @@ class FormsService { /** @var ILogger */ private $logger; + /** @var SettingsController */ + private $settingsController; + public function __construct(FormMapper $formMapper, QuestionMapper $questionMapper, OptionMapper $optionMapper, @@ -77,6 +81,7 @@ public function __construct(FormMapper $formMapper, IGroupManager $groupManager, IUserManager $userManager, IUserSession $userSession, + SettingsController $settingsController, SurveyUserService $surveyUserService, ILogger $logger) { $this->formMapper = $formMapper; @@ -87,6 +92,7 @@ public function __construct(FormMapper $formMapper, $this->groupManager = $groupManager; $this->userManager = $userManager; $this->logger = $logger; + $this->settingsController = $settingsController; $this->currentUser = $userSession->getUser(); } @@ -154,6 +160,10 @@ public function getForm(int $id): array { $result['access']['users'] = array_map([$this, 'formatUsers'], $result['access']['users']); $result['access']['groups'] = array_map([$this, 'formatGroups'], $result['access']['groups']); + // The current user can view survey results. This could be refined to + // a per form basis later if required + $result['access']['canViewResults'] = $this->settingsController->canViewResults(); + return $result; } diff --git a/src/Forms.vue b/src/Forms.vue index bd18fa3ef..e4d474282 100644 --- a/src/Forms.vue +++ b/src/Forms.vue @@ -24,7 +24,10 @@ - - diff --git a/src/views/Create.vue b/src/views/Create.vue index fb48a159a..b322a9d94 100644 --- a/src/views/Create.vue +++ b/src/views/Create.vue @@ -32,7 +32,8 @@ - diff --git a/templates/settings.php b/templates/settings.php index 07be461ca..2f70de9dd 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -9,12 +9,29 @@ method="POST" name="forms_settings"> -
    +

    -

    Create forms

    +

    General settings

    - Please select the user groups allowed to create new forms: + t('These are the controls for the general functions:')); ?> +

    +

    + type="checkbox" + value="yes" + class="checkbox"> +

    +
    +
    + +
    +

    Create forms

    +
    + +

    + t('Please select the user groups allowed to create new forms:')); ?>

    View results

    -

    - Please select the user groups allowed to view new results: +

    + +

    + t('Please select the user groups allowed to view the survey results:')); ?>

    Date: Wed, 4 Nov 2020 06:41:18 +0000 Subject: [PATCH 07/35] Beta --- lib/Controller/ApiController.php | 13 ++++++++++--- lib/Service/FormsService.php | 4 +++- src/Forms.vue | 1 + src/components/AppNavigationForm.vue | 2 +- src/views/Create.vue | 2 +- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index aa22aeb97..0bc95fa7c 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -139,6 +139,11 @@ public function __construct(string $appName, public function getForms(): DataResponse { $forms = $this->formMapper->findAllByOwnerId($this->currentUser->getUID()); + // The current user can view survey results. This could be + // refined to a per form basis later if required + $canView = !$this->settingsController->isAccessControlEnabled() || + $this->settingsController->canViewResults(); + $result = []; foreach ($forms as $form) { $result[] = [ @@ -147,9 +152,7 @@ public function getForms(): DataResponse { 'title' => $form->getTitle(), 'expires' => $form->getExpires(), 'partial' => true, - // The current user can view survey results. This could be - // refined to a per form basis later if required - 'canViewResults' => $this->settingsController->canViewResults() + 'canViewResults' => $canView ]; } @@ -189,6 +192,10 @@ public function getForm(int $id): DataResponse { * @throws OCSForbiddenException */ public function newForm(): DataResponse { + if ($this->settingsController->isAccessControlEnabled() && + !$this->settingsController->canCreateForms()) + throw new OCSForbiddenException(); + $form = new Form(); $form->setOwnerId($this->currentUser->getUID()); diff --git a/lib/Service/FormsService.php b/lib/Service/FormsService.php index bf671eb99..bcab40e9c 100644 --- a/lib/Service/FormsService.php +++ b/lib/Service/FormsService.php @@ -162,7 +162,9 @@ public function getForm(int $id): array { // The current user can view survey results. This could be refined to // a per form basis later if required - $result['access']['canViewResults'] = $this->settingsController->canViewResults(); + $result['canViewResults'] = + !$this->settingsController->isAccessControlEnabled() || + $this->settingsController->canViewResults(); return $result; } diff --git a/src/Forms.vue b/src/Forms.vue index e4d474282..e1826d3c7 100644 --- a/src/Forms.vue +++ b/src/Forms.vue @@ -155,6 +155,7 @@ export default { try { const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'forms') this.forms = response.data + console.info(response.data) } catch (error) { showError(t('forms', 'An error occurred while loading the forms list')) console.error(error) diff --git a/src/components/AppNavigationForm.vue b/src/components/AppNavigationForm.vue index f6ef92c8c..2f935c95e 100644 --- a/src/components/AppNavigationForm.vue +++ b/src/components/AppNavigationForm.vue @@ -35,7 +35,7 @@ @click.stop.prevent="copyLink"> {{ clipboardTooltip }} - -

    +
    +

    t('View personal data')); ?>

    +
    + +

    + t('Please select the user groups allowed to view the personal data connected to the survey results:')); ?> +

    + +

    + type="checkbox" + value="yes" + class="checkbox"> +

    + +
    +
    + +
    +

    t('Background for the survey user interface')); ?>

    +
    + + + +
    +
    +
    t('Save changes')); ?>">
    diff --git a/templates/surveyuserlogin.php b/templates/surveyuserlogin.php index 975f7a582..22c1bfcad 100644 --- a/templates/surveyuserlogin.php +++ b/templates/surveyuserlogin.php @@ -32,7 +32,15 @@
    - + + + + +
    ">
    @@ -41,17 +49,17 @@
    - +
    -

    t('This form is for registered users only. If you already have an account, please log in.')); ?>

    - +
    diff --git a/templates/surveyuserreset.php b/templates/surveyuserreset.php index adecfb58b..0c2642eb3 100644 --- a/templates/surveyuserreset.php +++ b/templates/surveyuserreset.php @@ -41,7 +41,7 @@
    - +
    From 9b43da9d46403e686efc6f76171d51192b8f830c Mon Sep 17 00:00:00 2001 From: Zeon Date: 2020年11月12日 08:26:05 +0000 Subject: [PATCH 21/35] Allithato TOS, PP --- l10n/hu.json | 3 +- lib/Controller/SureveyUserController.php | 20 +++++--- templates/surveyuserregister.php | 61 +++++++++++++----------- templates/surveyuserreset.php | 2 +- 4 files changed, 51 insertions(+), 35 deletions(-) diff --git a/l10n/hu.json b/l10n/hu.json index c97ab4fd7..ea0f0914c 100644 --- a/l10n/hu.json +++ b/l10n/hu.json @@ -1,5 +1,6 @@ { "translations": { + "Reset password": "Jelszó visszaállítása", "Background": "Háttér", "Background for the survey user interface": "Háttérkép a kérdőív felülethez", "There was an error while loading the list of users": "Hiba a felhasználók listájának betöltése közben", @@ -115,7 +116,7 @@ "Expired on {date}": "Lejárt: {date}", "Form expired": "Az űrlap lejárt", "Please provide your details below.": "Kérjük, adja meg az alábbi adatokat.", - "Privacy Policy": "Adatvédelmi tájékoztató", + "Privacy Policy": "Adatvédelmi irányelvek", "Submitting form ...": "Űrlap beküldése ...", "Registered survey user response": "Regisztrált külső felhasználó válasza", "Please enter your name": "Kérjük, adja meg a nevét", diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php index c96d4391c..40cb423c0 100644 --- a/lib/Controller/SureveyUserController.php +++ b/lib/Controller/SureveyUserController.php @@ -476,13 +476,18 @@ public function commitRegister($id, 'The password and password verification fields don\'t match.'); } - if ($su_pp !== 'yes') { + $docsUrlPrivacyPolicy = $this->settingsController->getPrivacyPolicyUrl(); + $docsUrlTermsOfUse = $this->settingsController->getTermsOfServiceUrl(); + $tosReq = ($docsUrlTermsOfUse !== null && strlen($docsUrlTermsOfUse)> 0); + $ppReq = ($docsUrlPrivacyPolicy !== null && strlen($docsUrlPrivacyPolicy)> 0); + + if ($ppReq && $su_pp !== 'yes') { $success = false; $problems[] = $this->l10n->t( 'You have to accept our privacy policy to register.'); } - if ($su_tos !== 'yes') { + if ($tosReq && $su_tos !== 'yes') { $success = false; $problems[] = $this->l10n->t( 'You have to accept our terms of use to register.'); @@ -642,10 +647,13 @@ public function addSurveyUserMenu(TemplateResponse $response) : TemplateResponse $docsUrlTermsOfUse = $this->settingsController->getTermsOfServiceUrl(); // $profile = new SimpleMenuAction('profile', $this->l10n->t('View profile'), 'icon-user', $profileUrl, 0); - $logout = new SimpleMenuAction('logout', $this->l10n->t('Log out'), 'icon-close', $logoutUrl, 0); - $docs1 = new SimpleMenuAction('tos', $this->l10n->t('Terms of use'), 'icon-info', $docsUrlTermsOfUse, 1); - $docs2 = new SimpleMenuAction('ppolicy', $this->l10n->t('Privacy Policy'), 'icon-info', $docsUrlPrivacyPolicy, 2); - $response->setHeaderActions([$logout, $docs1, $docs2]); + $menu = [new SimpleMenuAction('logout', $this->l10n->t('Log out'), 'icon-close', $logoutUrl, 0)]; + if ($docsUrlTermsOfUse !== null && strlen($docsUrlTermsOfUse)> 0) + $menu[] = new SimpleMenuAction('tos', $this->l10n->t('Terms of use'), 'icon-info', $docsUrlTermsOfUse, 1); + if ($docsUrlPrivacyPolicy !== null && strlen($docsUrlPrivacyPolicy)> 0) + $menu[] = new SimpleMenuAction('ppolicy', $this->l10n->t('Privacy Policy'), 'icon-info', $docsUrlPrivacyPolicy, 2); + + $response->setHeaderActions($menu); return $response; } diff --git a/templates/surveyuserregister.php b/templates/surveyuserregister.php index b8ebdb2c9..d98338a09 100644 --- a/templates/surveyuserregister.php +++ b/templates/surveyuserregister.php @@ -26,6 +26,9 @@ $routeLogin = \OC::$server->getURLGenerator() ->linkToRoute('forms.sureveyUser.loginForm', ['id' => $_['formid']]); +$showTos = isset($_['toslink']) && strlen($_['toslink'])> 0; +$showPP = isset($_['pplink']) && strlen($_['pplink'])> 0; + ?>
    @@ -119,33 +122,37 @@ placeholder="t('Please enter the year you were born in')); ?>" maxlength="4" minlength="1" type="number" class="question__input">
    -
    - - id="su_tos" - type="checkbox" - required - value="yes" - class="checkbox question__checkbox"> - -
    - -
    - - id="su_pp" - required - type="checkbox" - value="yes" - class="checkbox question__checkbox"> - - -
    - + +
    + + id="su_tos" + type="checkbox" + required + value="yes" + class="checkbox question__checkbox"> + +
    + + + +
    + + id="su_pp" + required + type="checkbox" + value="yes" + class="checkbox question__checkbox"> + + +
    + +
    diff --git a/templates/surveyuserreset.php b/templates/surveyuserreset.php index 0c2642eb3..b70f073a6 100644 --- a/templates/surveyuserreset.php +++ b/templates/surveyuserreset.php @@ -95,7 +95,7 @@
    - +
    From 05999145b5462fbf0f4d0530339f938a0ecc2e42 Mon Sep 17 00:00:00 2001 From: Zeon Date: 2020年11月12日 09:24:34 +0000 Subject: [PATCH 22/35] Redirect utan megorizze a form id-t --- appinfo/routes.php | 2 +- lib/Controller/SureveyUserController.php | 6 ++++-- templates/surveyuserregister.php | 5 ++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index fdfda65fa..8139f78d7 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -33,7 +33,7 @@ ['name' => 'sureveyUser#loginForm', 'url' => '/sureveyuser/login/form/{id}', 'verb' => 'GET', 'defaults' => ['id' => '']], ['name' => 'sureveyUser#login', 'url' => '/sureveyuser/login/{id}', 'verb' => 'POST', 'defaults' => ['id' => '']], ['name' => 'sureveyUser#logout', 'url' => '/sureveyuser/logout', 'verb' => 'GET'], - ['name' => 'sureveyUser#verifyEmail', 'url' => '/sureveyuser/verifyemail/{code}', 'verb' => 'GET'], + ['name' => 'sureveyUser#verifyEmail', 'url' => '/sureveyuser/verifyemail/{code}/{formid}', 'verb' => 'GET', 'defaults' => ['formid' => '']], ['name' => 'sureveyUser#requireResetPassword', 'url' => '/sureveyuser/resetpassword/require', 'verb' => 'GET'], ['name' => 'sureveyUser#requireResetPasswordPost', 'url' => '/sureveyuser/resetpassword/require', 'verb' => 'POST'], ['name' => 'sureveyUser#resetPassword', 'url' => '/sureveyuser/resetpassword/{code}', 'verb' => 'GET'], diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php index 40cb423c0..f535489f6 100644 --- a/lib/Controller/SureveyUserController.php +++ b/lib/Controller/SureveyUserController.php @@ -238,7 +238,7 @@ private static function getAccessCode(): string { * * @param $code */ - public function verifyEmail($code) { + public function verifyEmail($code, $formid = '') { $code = self::DB_CODE_PREFIX_VALIDATE. ValidationHelper::filterAlphaNumericUnicde($code, self::ACCESS_CODE_LEN); $failed = true; @@ -257,12 +257,14 @@ public function verifyEmail($code) { if ($failed) return $this->setupLoginPage([ + 'formid' => $formid, 'activationMessage' => $this->l10n->t( 'Invalid activation code. Please try again or contact the site administration.') ], $this->l10n->t( 'Activation done.')); else return $this->setupLoginPage([ + 'formid' => $formid, 'activationMessage' => $this->l10n->t( 'Thank you for activating your account. Please log in.') ], $this->l10n->t( @@ -525,7 +527,7 @@ public function commitRegister($id, // Send mail with the activation code $link = \OC::$server->getURLGenerator() - ->linkToRoute('forms.sureveyUser.verifyEmail', ['code' => $code]); + ->linkToRoute('forms.sureveyUser.verifyEmail', ['code' => $code, 'formid' => $id]); $this->sendMail(self::EMAIL_TEMPLATE_VERIFY, $link, $su_email); } catch (Exception $e) { // TODO TODOFORMS Log diff --git a/templates/surveyuserregister.php b/templates/surveyuserregister.php index d98338a09..e6a7094a2 100644 --- a/templates/surveyuserregister.php +++ b/templates/surveyuserregister.php @@ -134,7 +134,7 @@ class="checkbox question__checkbox">
    @@ -147,10 +147,9 @@ class="checkbox question__checkbox"> value="yes" class="checkbox question__checkbox"> -
    From 6b16402d60fa9e30b439d014f690528d9022f324 Mon Sep 17 00:00:00 2001 From: Zeon Date: 2020年11月12日 12:54:10 +0000 Subject: [PATCH 23/35] Telefonszam --- appinfo/info.xml | 2 +- l10n/hu.json | 3 + lib/Controller/SureveyUserController.php | 13 +++- lib/Db/SurveyUser.php | 5 ++ lib/Helper/ValidationHelper.php | 14 +++++ .../Version020010Date20201112161000.php | 60 +++++++++++++++++++ lib/Service/SurveyUserService.php | 4 +- templates/surveyuserregister.php | 22 +++++-- 8 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 lib/Migration/Version020010Date20201112161000.php diff --git a/appinfo/info.xml b/appinfo/info.xml index 545395e02..ca614b76e 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -11,7 +11,7 @@ - **🔒 Data under your control!** Unlike in Google Forms, Typeform, Doodle and others, the survey info and responses are kept private on your instance. - **🙋 Get involved!** We have lots of stuff planned like more question types, collaboration on forms, [and much more](https://github.com/nextcloud/forms/milestones)! ]]> - 2.0.9 + 2.0.10 agpl Affan Hussain diff --git a/l10n/hu.json b/l10n/hu.json index ea0f0914c..f08800cda 100644 --- a/l10n/hu.json +++ b/l10n/hu.json @@ -1,5 +1,8 @@ { "translations": { + "Please provide your phone number.": "Kérjük, adja meg a telefonszámát.", + "Phone number": "Telefonszám", + "Please enter your phone number": "Kérjük, adja meg a telefonszámát", "Reset password": "Jelszó visszaállítása", "Background": "Háttér", "Background for the survey user interface": "Háttérkép a kérdőív felülethez", diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php index f535489f6..e45e0bd9f 100644 --- a/lib/Controller/SureveyUserController.php +++ b/lib/Controller/SureveyUserController.php @@ -431,6 +431,7 @@ public function requireResetPasswordPost($su_email = null) { */ public function commitRegister($id, $su_email, + $su_phone, $su_password, $su_password2, $su_realname, @@ -454,12 +455,18 @@ public function commitRegister($id, 'Please provide your address.'); } - if (!$su_born || $su_born < 1800) { + if (!$su_phone || strlen($su_phone) <= 0) { $success = false; $problems[] = $this->l10n->t( - 'Please provide your date of birth.'); + 'Please provide your phone number.'); } + //if (!$su_born || $su_born < 1800) { + // $success = false; + // $problems[] = $this->l10n->t( + // 'Please provide your date of birth.'); + //} + if (!filter_var($su_email, FILTER_VALIDATE_EMAIL)) { $success = false; $problems[] = $this->l10n->t('Invalid e-mail format.'); @@ -512,6 +519,7 @@ public function commitRegister($id, $newUser = new SurveyUser(); $newUser->setRealname(ValidationHelper::filterAlphaNumericUnicde($su_realname, 255)); $newUser->setAddress(ValidationHelper::filterAlphaNumericUnicde($su_address, 1000)); + $newUser->setPhone(ValidationHelper::filterPhoneNumber($su_phone, 50)); $newUser->setEmail($su_email); // It is already filtered above $newUser->setPasswordhash(password_hash($su_password, PASSWORD_ARGON2I)); $code = self::getAccessCode(); @@ -542,6 +550,7 @@ public function commitRegister($id, 'mode' => 'return', 'su_email' => $su_email, 'su_pp' => $su_pp, + 'su_phone' => $su_phone, 'su_tos' => $su_tos, 'su_password' => $su_password, 'su_password2' => $su_password2, diff --git a/lib/Db/SurveyUser.php b/lib/Db/SurveyUser.php index 1639d9741..4f3eaf335 100644 --- a/lib/Db/SurveyUser.php +++ b/lib/Db/SurveyUser.php @@ -43,6 +43,8 @@ * @method void setEmail(string $value) * @method string getConfirmcode() * @method void setConfirmcode(string $value) + * @method string getPhone() + * @method void setPhone(string $value) * @method integer getBornyear() * @method void setBornyear(integer $value) * @method integer getStatus() @@ -58,6 +60,7 @@ class SurveyUser extends Entity protected $confirmcode; protected $bornyear; protected $status; + protected $phone; /** * SurveyUser constructor. @@ -72,6 +75,7 @@ public function __construct() $this->addType('bornyear', 'integer'); $this->addType('passwordhash', 'string'); $this->addType('status', 'integer'); + $this->addType('phone', 'string'); } public function read(): array @@ -86,6 +90,7 @@ public function read(): array 'confirmcode' => $this->getConfirmcode(), 'bornyear' => $this->getBornyear(), 'status' => $this->getStatus(), + 'phone' => $this->getPhone(), ]; } } diff --git a/lib/Helper/ValidationHelper.php b/lib/Helper/ValidationHelper.php index 0b42caf00..cc64f6111 100644 --- a/lib/Helper/ValidationHelper.php +++ b/lib/Helper/ValidationHelper.php @@ -38,4 +38,18 @@ public static function filterAlphaNumericUnicde($input, return substr(preg_replace("/[^[:alnum:][:space:]]/u", '', $input), 0, $len); } + + /** + * Filter input for phone numbers + * + * @param string $input String to filter + * @param int $len Maximum length + * @return false|string Filtered string or false if there was no result + */ + public static function filterPhoneNumber($input, + $len = 200) + { + return trim(substr(preg_replace('/[^\d^\s^+^\-]/', '', + $input), 0, $len)); + } } diff --git a/lib/Migration/Version020010Date20201112161000.php b/lib/Migration/Version020010Date20201112161000.php new file mode 100644 index 000000000..6a18c18eb --- /dev/null +++ b/lib/Migration/Version020010Date20201112161000.php @@ -0,0 +1,60 @@ + + * + * @author Jonas Rittershofer + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Migration; + +use Closure; +use Doctrine\DBAL\Types\Type; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +/** + * Class Version020005Date20201019161000 Add user table for the survey users + * @package OCA\Forms\Migration + */ +class Version020010Date20201112161000 extends SimpleMigrationStep { + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $table = $schema->getTable('forms_surveyusers'); + if(!$table->hasColumn('phone')) + $table->addColumn('phone', TYPE::STRING, [ + 'notnull' => false, + 'length' => 50, + 'default' => null, + ]); + + return $schema; + } +} diff --git a/lib/Service/SurveyUserService.php b/lib/Service/SurveyUserService.php index e4f8ac309..9b9b440f8 100644 --- a/lib/Service/SurveyUserService.php +++ b/lib/Service/SurveyUserService.php @@ -161,7 +161,9 @@ public function addAnswersForPersonalData(&$answersList, $submission) { // personal data answers return $answersList; - $userId = substr($userId, strlen(self::SURVEY_USER_DB_PREFIX)); + $userId = (int)substr($userId, strlen(self::SURVEY_USER_DB_PREFIX)); + if ($userId === 0) return $answersList; + try { $user = $this->surveyUserMapper->load($userId); $answers = [ diff --git a/templates/surveyuserregister.php b/templates/surveyuserregister.php index e6a7094a2..d06ba4f2c 100644 --- a/templates/surveyuserregister.php +++ b/templates/surveyuserregister.php @@ -78,7 +78,7 @@
    @@ -86,7 +86,7 @@
    @@ -94,7 +94,7 @@
    @@ -102,26 +102,36 @@
    +
    + + +
    +
    +
    Date: 2020年11月12日 15:58:01 +0000 Subject: [PATCH 24/35] Login keresek valtoztatas, survey user mngmnt tovabb --- appinfo/routes.php | 3 ++- css/survey.scss | 4 ++++ l10n/hu.js | 2 ++ l10n/hu.json | 7 +++++-- lib/Controller/PageController.php | 19 +++++++++++++++++++ lib/Controller/SureveyUserController.php | 2 +- lib/Service/SurveyUserService.php | 24 ++++++++++++++++++++---- src/components/Users/UserRow.vue | 11 ++++++----- src/router.js | 2 +- src/views/Users.vue | 23 +++++++++++++++++++++-- templates/surveyuserlogin.php | 18 +++++++++++------- 11 files changed, 92 insertions(+), 23 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 8139f78d7..c0197ac2e 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -38,6 +38,8 @@ ['name' => 'sureveyUser#requireResetPasswordPost', 'url' => '/sureveyuser/resetpassword/require', 'verb' => 'POST'], ['name' => 'sureveyUser#resetPassword', 'url' => '/sureveyuser/resetpassword/{code}', 'verb' => 'GET'], ['name' => 'sureveyUser#resetPasswordPost', 'url' => '/sureveyuser/resetpassword/{code}', 'verb' => 'POST'], + // Survey user management + ['name' => 'page#userAdminIndex', 'url' => '/surveyuseradmin/listusers/{page}/{filter}', 'verb' => 'GET', 'defaults' => ['filter' => '', 'page' => 1]], // Settings ['name' => 'settings#postSettings', 'url' => '/settings', 'verb' => 'POST'], @@ -46,7 +48,6 @@ // Before /{hash} to avoid conflict ['name' => 'page#index', 'url' => '/new', 'verb' => 'GET', 'postfix' => 'create'], - // ['name' => 'page#index', 'url' => '/surveyusers/{filter}', 'verb' => 'GET', 'defaults' => ['filter' => '']], ['name' => 'page#index', 'url' => '/{hash}/edit', 'verb' => 'GET', 'postfix' => 'edit'], ['name' => 'page#index', 'url' => '/{hash}/clone', 'verb' => 'GET', 'postfix' => 'clone'], ['name' => 'page#index', 'url' => '/{hash}/results', 'verb' => 'GET', 'postfix' => 'results'], diff --git a/css/survey.scss b/css/survey.scss index 4b7b7f0a8..68d2199a7 100644 --- a/css/survey.scss +++ b/css/survey.scss @@ -39,6 +39,10 @@ min-width: 400px; transform: translate(-50%, -50%); + p.spaced { + margin-top: 25px; + } + .survey-policy { text-align: left; } diff --git a/l10n/hu.js b/l10n/hu.js index 50c63ca0a..b5dae6b86 100644 --- a/l10n/hu.js +++ b/l10n/hu.js @@ -1,6 +1,8 @@ OC.L10N.register( "forms", { + "Filter results": "Találatok szűrése", + "Phone number": "Telefonszám", "Loading users ...": "Felhasználói lista töltése ...", "If you don't have an account yet, you can register by clicking here.": "Ha még nincs felhasználói fiókja, itt regisztrálhat.", "Allow all instance users to access all forms (no ownership is checked)": "Engedélyezze az összes példány felhasználójának az összes űrlaphoz való hozzáférést (nincs ellenőrizve a tulajdonjog)", diff --git a/l10n/hu.json b/l10n/hu.json index f08800cda..f686b55e6 100644 --- a/l10n/hu.json +++ b/l10n/hu.json @@ -1,5 +1,7 @@ { "translations": { + "When you have finished the registration and confirmed your e-mail address, you can log in to answer the survey.": "Amikor befejezte a regisztrációt és visszaigazolta az e-mail címét, ezen az oldalon beléphet és kitöltheti a kérdőívet.", + "If you don't have an account for this page yet, you can register by clicking on the button below.": "Ha még nincs felhasználói fiókja ezen az oldalon, kérjük regisztráljon a lenti gombbal.", "Please provide your phone number.": "Kérjük, adja meg a telefonszámát.", "Phone number": "Telefonszám", "Please enter your phone number": "Kérjük, adja meg a telefonszámát", @@ -23,7 +25,7 @@ "Please check this if you accept our terms of use": "Jelölje be, ha elfogadja a felhasználási feltételeinket", "You have to accept our terms of use to register.": "El kell fogadnia a felhasználási feltételeket a regisztrációhoz.", "You have to accept our privacy policy to register.": "El kell fogadnia az adatvédelmi irányelveinket a regisztrációhoz.", - "Good bye!": "Viszont látásra!", + "Good bye!": "Viszontlátásra!", "Error resetting your password.": "Hiba a jelszó visszaállítása során.", "Please enter your new password.": "Kérjük, adja meg az új jelszavát.", "You password have been reset. Please log in.": "A jelszavát visszaállítottuk. Kérjük, lépjen be.", @@ -38,7 +40,8 @@ "Send reset link": "Link kérése", "Please enter your e-mail address, so we can send you a password reset link.": "Kérjük, adja meg az e-mail címét, amire a jelszóváltáshoz szükséges linket küldhetjük.", "Did you forgot your password? Click here to start a password reset!": "Elfelejtette a jelszavát? Kattintson ide, ha újat szeretne kérni!", - "This form is for registered users only. If you already have an account, please log in.": "Ez az űrlap csak regisztrált felhasználók számára érhető el. Amennyiben rendelkezik felhasználói fiókkal, kérjük lépjen be.", + "This form is for registered users only.": "Ez az űrlap csak regisztrált felhasználók számára érhető el.", + "If you already have an account, please log in.": "Amennyiben rendelkezik felhasználói fiókkal, kérjük lépjen be.", "If you don't have an account yet, you can register by clicking here.": "Ha még nincs felhasználói fiókja, itt regisztrálhat.", "Password verification for %1$s": "Jelszóellenőrzés %1$s számára", "Please confirm that this is your e-mail address": "Kérjük, erősítse meg, hogy ez az Ön e-mail címe", diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index f04da9ff2..3fc3ca4ea 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -35,6 +35,7 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http\Template\PublicTemplateResponse; use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\OCS\OCSForbiddenException; use OCP\IGroupManager; use OCP\IInitialStateService; use OCP\IL10N; @@ -83,6 +84,9 @@ class PageController extends Controller { /** @var IUserSession */ private $userSession; + /** @var SettingsController */ + private $settingsController; + /** @var Array * * Maximum String lengths, the database is set to store. @@ -105,6 +109,7 @@ public function __construct(string $appName, SureveyUserController $sureveyUserController, IL10N $l10n, ILogger $logger, + SettingsController $settingsController, IUserManager $userManager, IUserSession $userSession) { parent::__construct($appName, $request); @@ -122,6 +127,7 @@ public function __construct(string $appName, $this->logger = $logger; $this->userManager = $userManager; $this->userSession = $userSession; + $this->settingsController = $settingsController; } /** @@ -137,6 +143,19 @@ public function index(): TemplateResponse { return new TemplateResponse($this->appName, self::TEMPLATE_MAIN); } + /** + * @NoAdminRequired + * @NoCSRFRequired + * + * @return TemplateResponse + */ + public function userAdminIndex(): TemplateResponse { + if (!$this->settingsController->canViewPersonalData()) + throw new OCSForbiddenException(); + + return $this->index(); + } + /** * @NoAdminRequired * @NoCSRFRequired diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php index e45e0bd9f..dd32ee061 100644 --- a/lib/Controller/SureveyUserController.php +++ b/lib/Controller/SureveyUserController.php @@ -779,7 +779,7 @@ public function apiList(string $filter): DataResponse { 'realname' => $user->getRealname(), 'address' => $user->getAddress(), 'email' => $user->getEmail(), - 'bornyear' => $user->getBornyear(), + 'phone' => $user->getPhone(), 'status' => $user->getStatus(), ]; } diff --git a/lib/Service/SurveyUserService.php b/lib/Service/SurveyUserService.php index 9b9b440f8..1013c83f4 100644 --- a/lib/Service/SurveyUserService.php +++ b/lib/Service/SurveyUserService.php @@ -58,7 +58,7 @@ class SurveyUserService public const SURVEY_USER_STATUS_DATA_RETRACTED = 2; private const QUESTION_ID_ADDRESS = 2147483647; - private const QUESTION_ID_BIRTHYEAR = 2147483646; + private const QUESTION_ID_PHONE = 2147483646; private const QUESTION_ID_REALNAME = 2147483645; public function __construct(FormMapper $formMapper, @@ -73,28 +73,43 @@ public function __construct(FormMapper $formMapper, $this->l10n = $l10n; } + /** + * @return bool True if there is a logged in survey user session + */ public function isSurveyUserLoggedIn() { $user = $this->getCurrentSurveyUserId(); return $user !== null && ((int)$user)> 0; } + /** + * Log out survey user session + */ public function logoutSurveyUser() { if (session_status() == PHP_SESSION_NONE) session_start(); $_SESSION[self::SURVEY_USER_SESSION_ID] = null; } + /** + * @param $userId int Set the current logged in survey user id + */ public function setCurrentSurveyUserId($userId) { if (session_status() == PHP_SESSION_NONE) session_start(); // \OC::$server->getSession()->set(self::SURVEY_USER_SESSION_ID, $userId); $_SESSION[self::SURVEY_USER_SESSION_ID] = $userId; } + /** + * @return mixed Returns the current logged in surevy user id + */ public function getCurrentSurveyUserId() { if (session_status() == PHP_SESSION_NONE) session_start(); // return \OC::$server->getSession()->get(self::SURVEY_USER_SESSION_ID); return $_SESSION[self::SURVEY_USER_SESSION_ID]; } + /** + * @return SurveyUser|null Returns the current logged in survey user + */ public function getCurrentSurveyUser() : ?SurveyUser { if (!$this->isSurveyUserLoggedIn()) return null; @@ -165,17 +180,18 @@ public function addAnswersForPersonalData(&$answersList, $submission) { if ($userId === 0) return $answersList; try { + /** @var SurveyUser $user */ $user = $this->surveyUserMapper->load($userId); $answers = [ self::QUESTION_ID_ADDRESS => $user->getAddress(), - self::QUESTION_ID_BIRTHYEAR => $user->getBornyear(), + self::QUESTION_ID_PHONE => $user->getPhone(), self::QUESTION_ID_REALNAME => $user->getRealname(), ];; } catch (IMapperException $e) { $retracted = $this->l10n->t('Data not found or retracted'); $answers = [ self::QUESTION_ID_ADDRESS => $retracted, - self::QUESTION_ID_BIRTHYEAR => $retracted, + self::QUESTION_ID_PHONE => $retracted, self::QUESTION_ID_REALNAME => $retracted, ]; } @@ -203,7 +219,7 @@ public function addAnswersForPersonalData(&$answersList, $submission) { public function addQuestionsForPersonalData(&$questions, $formId) { $newFields = [ self::QUESTION_ID_ADDRESS => $this->l10n->t('Address'), - self::QUESTION_ID_BIRTHYEAR => $this->l10n->t('Your birth year'), + self::QUESTION_ID_PHONE => $this->l10n->t('Phone number'), self::QUESTION_ID_REALNAME => $this->l10n->t('Real name'), ]; diff --git a/src/components/Users/UserRow.vue b/src/components/Users/UserRow.vue index 2f65914eb..ccf53c09c 100644 --- a/src/components/Users/UserRow.vue +++ b/src/components/Users/UserRow.vue @@ -9,8 +9,8 @@ {{ t('forms', 'E-mail address') }} - - {{ t('forms', 'Birth year') }} + + {{ t('forms', 'Phone number') }} {{ user.email }} - - {{ user.bornyear }} + + {{ user.phone }} @@ -80,6 +80,7 @@ export default { td, th { padding: 15px; + border-bottom: 1px solid var(--color-border); } tr.users-selected { @@ -103,7 +104,7 @@ tr.users-clickable:hover { background: var(--color-background-hover); } -td.users-birth { +td.users-phone { width: 10%; } diff --git a/src/router.js b/src/router.js index 364fe1ebf..d56488d84 100644 --- a/src/router.js +++ b/src/router.js @@ -47,7 +47,7 @@ export default new Router({ name: 'root', }, { - path: '/surveyusers/:filter?', + path: '/surveyuseradmin/listusers/:page?/:filter?', components: { default: Users, sidebar: Sidebar, diff --git a/src/views/Users.vue b/src/views/Users.vue index f1d425b4e..b1ca69fc3 100644 --- a/src/views/Users.vue +++ b/src/views/Users.vue @@ -28,7 +28,12 @@ - Topbar +
    + + +
    @@ -37,7 +42,7 @@ @@ -74,18 +79,32 @@ export default { type: String, default: '', }, + page: { + type: Number, + default: 1, + }, }, data() { return { + dataFilter: '', loadingUsers: true, surveyUsers: [], } }, + computed: { + filteredResults() { + return this.surveyUsers.filter(item => { + return item.realname.toLowerCase().indexOf(this.dataFilter.toLowerCase())> -1 + }) + }, + }, + beforeMount() { this.loadSurveyUsers(this.filter) SetWindowTitle(this.formTitle) + this.dataFilter = this.$route.params.filter }, methods: { diff --git a/templates/surveyuserlogin.php b/templates/surveyuserlogin.php index 22c1bfcad..ce829b94d 100644 --- a/templates/surveyuserlogin.php +++ b/templates/surveyuserlogin.php @@ -55,10 +55,15 @@
    -

    t('This form is for registered users only. If you already have an account, please log in.')); ?>

    + +

    + +

    t('This form is for registered users only.')); ?>

    +

    t('If you don\'t have an account for this page yet, you can register by clicking on the button below.')); ?>

    +

    t('Register')); ?>

    +

    t('When you have finished the registration and confirmed your e-mail address, you can log in to answer the survey.')); ?>

    +

    t('If you already have an account, please log in.')); ?>

    +
    @@ -88,10 +93,9 @@
    - -

    t('Did you forgot your password? Click here to start a password reset!', $routeReset)); ?>

    -

    t('If you don\'t have an account yet, you can register by clicking here.', $routeRegister)); ?>

    +

    t('Did you forgot your password? Click here to start a password reset!', $routeReset)); ?>

    + From 46e333f635a30c894649b876b749d37ecdaa5746 Mon Sep 17 00:00:00 2001 From: Zeon Date: 2020年11月12日 20:49:13 +0000 Subject: [PATCH 25/35] User mng tovabb --- appinfo/routes.php | 2 +- lib/Controller/SureveyUserController.php | 23 ++- lib/Db/SurveyUserMapper.php | 9 +- package-lock.json | 179 +++-------------------- src/Forms.vue | 1 - src/components/Users/UserRow.vue | 63 +++++--- src/views/Users.vue | 65 ++++++-- 7 files changed, 145 insertions(+), 197 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index c0197ac2e..47019916f 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -83,6 +83,6 @@ ['name' => 'settings#getAccess', 'url' => '/api/v1/access', 'verb' => 'GET'], // Survey user management - ['name' => 'sureveyUser#apiList', 'url' => '/api/v1/surveyusers/{filter}', 'verb' => 'GET', 'defaults' => ['filter' => '']], + ['name' => 'sureveyUser#apiList', 'url' => '/api/v1/surveyusers/{page}/{filter}', 'verb' => 'GET', 'defaults' => ['page' => 0, 'filter' => '']], ] ]; diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php index dd32ee061..1f40cfa5c 100644 --- a/lib/Controller/SureveyUserController.php +++ b/lib/Controller/SureveyUserController.php @@ -762,7 +762,10 @@ private function sendMail(int $template, string $link, string $to) { * @param string $filter Filter to use listing the users * @return DataResponse List of survey users */ - public function apiList(string $filter): DataResponse { + public function apiList(string $filter, int $page): DataResponse { + + // TODO ACCESS CHECK + // if ($this->settingsController->isAccessToAllEnabled()) // $forms = $this->formMapper->findAll(); // else @@ -773,15 +776,27 @@ public function apiList(string $filter): DataResponse { // $canView = !$this->settingsController->isAccessControlEnabled() || // $this->settingsController->canViewResults(); - $data = []; - foreach ($this->surveyUserMapper->findAll() as $user) { - $data[] = [ + $limit = 100; + + $data = [ + 'results' => [], + 'limit' => $limit, + 'page' => $page, + 'more' => false, + ]; + + foreach ($this->surveyUserMapper->findAll($filter, $limit+1, $limit * $page) as $user) { + $data['results'][] = [ 'realname' => $user->getRealname(), 'address' => $user->getAddress(), 'email' => $user->getEmail(), 'phone' => $user->getPhone(), 'status' => $user->getStatus(), ]; + if (count($data['results']) === $limit) { + $data['more'] = true; + break; + } } return new DataResponse($data); diff --git a/lib/Db/SurveyUserMapper.php b/lib/Db/SurveyUserMapper.php index 0634505eb..002b20c56 100644 --- a/lib/Db/SurveyUserMapper.php +++ b/lib/Db/SurveyUserMapper.php @@ -103,12 +103,19 @@ public function findByCode(string $code) /** * @return SurveyUser[] */ - public function findAll(): array { + public function findAll($filter = '', $limit = 100, $offset = 0): array { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->getTableName()); + if ($filter !== '' && $filter !== null) + $qb->where($qb->expr()->iLike('realname', $qb->createNamedParameter("%$filter%"))); + + $qb->orderBy('realname', 'ASC') + ->setFirstResult($offset) + ->setMaxResults($limit); + return $this->findEntities($qb); } } diff --git a/package-lock.json b/package-lock.json index b700e3ff9..757028b05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -604,103 +604,6 @@ } } }, - "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", - "requires": { - "@babel/types": "^7.10.4" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" - }, - "@babel/types": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", - "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-module-transforms": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", - "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/template": "^7.10.4", - "@babel/types": "^7.11.0", - "lodash": "^4.17.19" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } - } - }, "@babel/helper-optimise-call-expression": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", @@ -909,65 +812,6 @@ } } }, - "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", - "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.4.tgz", - "integrity": "sha512-8jHII4hf+YVDsskTF6WuMB3X4Eh+PsUkC2ljq22so5rHvH+T8BzyL94VOdyFLNR8tBSVXOTbNHOKpR4TfRxVtA==" - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/types": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", - "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } - } - }, "@babel/helper-skip-transparent-expression-wrappers": { "version": "7.12.1", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", @@ -6060,6 +5904,7 @@ "has": "^1.0.3", "has-symbols": "^1.0.1", "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", "is-regex": "^1.1.1", "object-inspect": "^1.8.0", "object-keys": "^1.1.1", @@ -6079,8 +5924,30 @@ "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", "requires": { "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", "has-symbols": "^1.0.1", "object-keys": "^1.1.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } } } } diff --git a/src/Forms.vue b/src/Forms.vue index 27fd08e9c..49915cc1d 100644 --- a/src/Forms.vue +++ b/src/Forms.vue @@ -159,7 +159,6 @@ export default { try { const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'forms') this.forms = response.data - console.info(response.data) } catch (error) { showError(t('forms', 'An error occurred while loading the forms list')) console.error(error) diff --git a/src/components/Users/UserRow.vue b/src/components/Users/UserRow.vue index ccf53c09c..65a1b136f 100644 --- a/src/components/Users/UserRow.vue +++ b/src/components/Users/UserRow.vue @@ -16,11 +16,16 @@
    + @click="userOpen = !userOpen">
    {{ user.realname }} +
    + Hello +
    {{ user.address }} @@ -49,6 +54,12 @@ export default { }, }, + data() { + return { + userOpen: false, + } + }, + computed: { itemIcon() { let common = ' user-icon users-clickable user-icon-user' @@ -61,18 +72,6 @@ export default { }, }, - methods: { - itemClicked(event) { - if (this.iamAHeader || this.node === undefined) return - - if (!this.isFolder) { - this.$store.commit('TOGGLE_FILE', this.node) - } else { - this.loading = true - this.$emit('change-folder', this.node.id) - } - }, - }, } @@ -81,6 +80,12 @@ export default { td, th { padding: 15px; border-bottom: 1px solid var(--color-border); + height: 60px; +} + +tr.open { + height: 120px; + padding-bottom: 75px; } tr.users-selected { @@ -104,16 +109,25 @@ tr.users-clickable:hover { background: var(--color-background-hover); } +.users-text, td.users-phone, td.users-addr, td.users-email { + overflow: hidden; + margin-top: 3px; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: top; + padding-top: 18px; +} + td.users-phone { - width: 10%; + min-width: 10%; } td.users-addr { - width: 30%; + min-width: 20%; } td.users-email { - width: 30%; + min-width: 20%; } div.sf-load-pos { @@ -124,11 +138,7 @@ div.sf-load-pos { .users-text { padding-left: 36px; - margin-top: 3px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - width: 30%; + min-width: 20%; } .sf-node-date { @@ -167,4 +177,15 @@ div.user-icon-user { mask-image: url('../../../img/user.svg'); } +div.user-drawer { + position: absolute; + width: 100%; + top: 60px; + left: 0px; + height: 60px; + z-index: 300; + padding: 15px; + background-color: var(--color-background-darker); +} + diff --git a/src/views/Users.vue b/src/views/Users.vue index b1ca69fc3..7764bac00 100644 --- a/src/views/Users.vue +++ b/src/views/Users.vue @@ -27,14 +27,18 @@ - -
    +
    +
    +
    +
    +
    - +
    @@ -42,7 +46,7 @@ @@ -59,9 +63,9 @@ import { showError } from '@nextcloud/dialogs' import axios from '@nextcloud/axios' import { generateOcsUrl } from '@nextcloud/router' import EmptyContent from '../components/EmptyContent' -import ViewsMixin from '../mixins/ViewsMixin' import SetWindowTitle from '../utils/SetWindowTitle' import UserRow from '../components/Users/UserRow' +import debounce from 'debounce' export default { name: 'Users', @@ -72,8 +76,6 @@ export default { EmptyContent, }, - mixins: [ViewsMixin], - props: { filter: { type: String, @@ -88,16 +90,17 @@ export default { data() { return { dataFilter: '', + lastSentFilter: '', loadingUsers: true, + filterLoading: false, surveyUsers: [], + dataPage: 0, } }, - computed: { - filteredResults() { - return this.surveyUsers.filter(item => { - return item.realname.toLowerCase().indexOf(this.dataFilter.toLowerCase())> -1 - }) + watch: { + dataFilter(val) { + this.debounceLoadSurveyUsers(val) }, }, @@ -108,13 +111,30 @@ export default { }, methods: { + debounceLoadSurveyUsers: debounce(function(filter = null) { + if (filter !== null && filter !== '') filter = '/' + filter + else filter = '' + + this.filterLoading = true + axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'surveyusers/' + this.dataPage + filter) + .then(response => { + this.surveyUsers = response.data + }) + .catch(error => { + console.error(error) + showError(t('forms', 'There was an error while loading the list of users')) + }).finally(() => { + this.filterLoading = false + }) + }, 500), + async loadSurveyUsers(filter = null) { this.loadingUsers = true if (filter !== null && filter !== '') filter = '/' + filter else filter = '' try { - const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'surveyusers' + filter) + const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'surveyusers/' + this.dataPage + filter) // Append questions & submissions this.surveyUsers = response.data @@ -138,6 +158,25 @@ table.users-table { div.users-container { width: 100%; + margin-top: 10px; +} + +div.loading-pos { + position: absolute; + right: 12px; + top: 12px; +} + +div.topbar-relative { + position: relative; + margin-left: 40px; + padding: 2px; +} + +div.topbar-container { + position: relative; + width: 100%; + background: var(--color-background-darker); } From f9437fc875fe593972af42e9a10d0310003c9c90 Mon Sep 17 00:00:00 2001 From: Zeon Date: 2020年11月13日 08:40:41 +0000 Subject: [PATCH 26/35] Load rework elott --- appinfo/routes.php | 3 +- css/icons.scss | 2 + img/userban.svg | 57 ++++++++++++++++++ lib/Controller/SureveyUserController.php | 12 ++++ src/components/Users/UserRow.vue | 77 +++++++++++++++++++++--- src/views/Users.vue | 24 +++++--- 6 files changed, 158 insertions(+), 17 deletions(-) create mode 100644 img/userban.svg diff --git a/appinfo/routes.php b/appinfo/routes.php index 47019916f..416f88600 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -83,6 +83,7 @@ ['name' => 'settings#getAccess', 'url' => '/api/v1/access', 'verb' => 'GET'], // Survey user management - ['name' => 'sureveyUser#apiList', 'url' => '/api/v1/surveyusers/{page}/{filter}', 'verb' => 'GET', 'defaults' => ['page' => 0, 'filter' => '']], + ['name' => 'sureveyUser#apiList', 'url' => '/api/v1/surveyusers/list/{page}/{filter}', 'verb' => 'GET', 'defaults' => ['page' => 0, 'filter' => '']], + ['name' => 'sureveyUser#apiSetStatus', 'url' => '/api/v1/surveyusers/status/{user}/{status}', 'verb' => 'PUT', 'defaults' => ['user' => 0, 'status' => 0]], ] ]; diff --git a/css/icons.scss b/css/icons.scss index 4aa373b0d..6966d9a65 100644 --- a/css/icons.scss +++ b/css/icons.scss @@ -30,6 +30,8 @@ @include icon-black-white('answer-short', 'forms', 1); @include icon-black-white('answer-long', 'forms', 1); @include icon-black-white('drag-handle', 'forms', 1); +@include icon-black-white('user', 'forms', 1); +@include icon-black-white('userban', 'forms', 1); .icon-yes { @include icon-color('checkmark', 'actions', $color-success, 1, true); diff --git a/img/userban.svg b/img/userban.svg new file mode 100644 index 000000000..7887bfb05 --- /dev/null +++ b/img/userban.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php index 1f40cfa5c..19b96b8b6 100644 --- a/lib/Controller/SureveyUserController.php +++ b/lib/Controller/SureveyUserController.php @@ -754,6 +754,17 @@ private function sendMail(int $template, string $link, string $to) { $this->mailer->send($message); } + /** + * Set the status on a survey user + * + * @param int $user Survey user id + * @param int $status New status + * @return DataResponse + */ + public function apiSetStatus(int $user, int $status): DataResponse { + return new DataResponse(['OK']); + } + /** * @NoAdminRequired * @@ -787,6 +798,7 @@ public function apiList(string $filter, int $page): DataResponse { foreach ($this->surveyUserMapper->findAll($filter, $limit+1, $limit * $page) as $user) { $data['results'][] = [ + 'id' => $user->getId(), 'realname' => $user->getRealname(), 'address' => $user->getAddress(), 'email' => $user->getEmail(), diff --git a/src/components/Users/UserRow.vue b/src/components/Users/UserRow.vue index 65a1b136f..0d0389704 100644 --- a/src/components/Users/UserRow.vue +++ b/src/components/Users/UserRow.vue @@ -21,10 +21,28 @@ @click="userOpen = !userOpen">
    -
    {{ user.realname }} +
    {{ user.realname }}
    - Hello +
    + {{ t('forms', 'Real name') }}:
    + {{ t('forms', 'Address') }}:
    + {{ t('forms', 'E-mail address') }}:
    + {{ t('forms', 'Phone number') }}: +
    +
    + {{ user.realname }}
    + {{ user.address }}
    + {{ user.email }}
    + {{ user.phone }} +
    +
    @@ -48,6 +66,10 @@ export default { type: Boolean, default: false, }, + banDisabled: { + type: Boolean, + default: false, + }, user: { type: Object, default: null, @@ -72,9 +94,22 @@ export default { }, }, + methods: { + banUserClick() { + this.$emit('user-ban-clicked', this.user) + }, + }, } + + diff --git a/src/views/Users.vue b/src/views/Users.vue index 7764bac00..ebd793b03 100644 --- a/src/views/Users.vue +++ b/src/views/Users.vue @@ -49,7 +49,9 @@ v-for="surveyUser in surveyUsers.results" :key="surveyUser.id" :iam-a-header="false" - :user="surveyUser" /> + :ban-disabled="banDisabled" + :user="surveyUser" + @user-ban-clicked="banUser" />
    @@ -90,6 +92,8 @@ export default { data() { return { dataFilter: '', + loadedFilter: null, + banDisabled: false, lastSentFilter: '', loadingUsers: true, filterLoading: false, @@ -105,20 +109,27 @@ export default { }, beforeMount() { - this.loadSurveyUsers(this.filter) - SetWindowTitle(this.formTitle) this.dataFilter = this.$route.params.filter + this.loadSurveyUsers(this.dataFilter) + SetWindowTitle(this.formTitle) }, methods: { + banUser(sender) { + console.info(sender) + }, + debounceLoadSurveyUsers: debounce(function(filter = null) { if (filter !== null && filter !== '') filter = '/' + filter else filter = '' + if (this.loadedFilter === filter) return + this.filterLoading = true - axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'surveyusers/' + this.dataPage + filter) + axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'surveyusers/list/' + this.dataPage + filter) .then(response => { this.surveyUsers = response.data + this.loadedFilter = filter }) .catch(error => { console.error(error) @@ -134,9 +145,8 @@ export default { else filter = '' try { - const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'surveyusers/' + this.dataPage + filter) - - // Append questions & submissions + const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'surveyusers/list/' + this.dataPage + filter) + this.loadedFilter = filter this.surveyUsers = response.data } catch (error) { console.error(error) From d793d145621c7d14d4b99465468d519394345a66 Mon Sep 17 00:00:00 2001 From: Zeon Date: 2020年11月13日 10:08:29 +0000 Subject: [PATCH 27/35] Ban/unban alapok kesz --- img/userban.svg | 8 +-- l10n/hu.js | 4 ++ l10n/hu.json | 1 + lib/Controller/SureveyUserController.php | 26 +++++++++ lib/Service/SurveyUserService.php | 13 ++++- src/components/Users/UserRow.vue | 29 ++++++---- src/views/Users.vue | 69 ++++++++++++++++-------- 7 files changed, 113 insertions(+), 37 deletions(-) diff --git a/img/userban.svg b/img/userban.svg index 7887bfb05..6771e152b 100644 --- a/img/userban.svg +++ b/img/userban.svg @@ -48,10 +48,10 @@ bordercolor="#666666" pagecolor="#ffffff" /> + id="path835" + d="M 1.5740676,14.425932 14.425932,1.5740676" + style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> diff --git a/l10n/hu.js b/l10n/hu.js index b5dae6b86..49b64f0f0 100644 --- a/l10n/hu.js +++ b/l10n/hu.js @@ -1,6 +1,10 @@ OC.L10N.register( "forms", { + "Unban user and include survey results": "Felhasználói fiók tiltásának visszavonása és a válaszok figyelembevétele", + "Ban user and exclude survey results": "Felhasználói fiók tiltása és a válaszok figyelmen kívül hagyása", + "Hide banned users": "Kitiltott felhasználók elrejtése", + "There was an error while modifying the user account": "Hiba történt a felhasználói fiók módosítása közben", "Filter results": "Találatok szűrése", "Phone number": "Telefonszám", "Loading users ...": "Felhasználói lista töltése ...", diff --git a/l10n/hu.json b/l10n/hu.json index f686b55e6..efb42a134 100644 --- a/l10n/hu.json +++ b/l10n/hu.json @@ -1,5 +1,6 @@ { "translations": { + "Invalid request": "Hibás kérés", "When you have finished the registration and confirmed your e-mail address, you can log in to answer the survey.": "Amikor befejezte a regisztrációt és visszaigazolta az e-mail címét, ezen az oldalon beléphet és kitöltheti a kérdőívet.", "If you don't have an account for this page yet, you can register by clicking on the button below.": "Ha még nincs felhasználói fiókja ezen az oldalon, kérjük regisztráljon a lenti gombbal.", "Please provide your phone number.": "Kérjük, adja meg a telefonszámát.", diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php index 19b96b8b6..b01265f6c 100644 --- a/lib/Controller/SureveyUserController.php +++ b/lib/Controller/SureveyUserController.php @@ -24,6 +24,7 @@ namespace OCA\Forms\Controller; use OC\OCS\Exception; +use OCA\Activity\Data; use OCA\Forms\Db\Form; use OCA\Forms\Db\FormMapper; use OCA\Forms\Db\SurveyUser; @@ -35,6 +36,7 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\IMapperException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\Response; @@ -56,6 +58,8 @@ class SureveyUserController extends Controller { public const DB_CODE_PREFIX_VALIDATE = 'validate:'; public const DB_CODE_PREFIX_RESET = 'reset:'; + public const DB_USER_STATUS_ACTIVE = 0; + public const DB_USER_STATUS_BANNED = 1; public const ACCESS_CODE_LEN = 100; @@ -762,6 +766,28 @@ private function sendMail(int $template, string $link, string $to) { * @return DataResponse */ public function apiSetStatus(int $user, int $status): DataResponse { + // TODO ACCESS CHECK + + $user = (int)$user; + $status = (int)$status; + if ($user <= 0 || $status < 0 || $status>= 100) + return new DataResponse($this->l10n->t('Invalid request'), + Http::STATUS_BAD_REQUEST); + + $userObj = $this->surveyUserService->getSurveyUser($user); + if ($userObj === null) + return new DataResponse($this->l10n->t('Invalid request'), + Http::STATUS_BAD_REQUEST); + + try { + $userObj->setStatus((int)$status); + $this->surveyUserMapper->update($userObj); + } catch (IMapperException $e) { + // TODO FORMSTODO log + return new DataResponse($this->l10n->t('Internal error'), + Http::STATUS_INTERNAL_SERVER_ERROR); + } + return new DataResponse(['OK']); } diff --git a/lib/Service/SurveyUserService.php b/lib/Service/SurveyUserService.php index 1013c83f4..73af1c3b6 100644 --- a/lib/Service/SurveyUserService.php +++ b/lib/Service/SurveyUserService.php @@ -114,9 +114,18 @@ public function getCurrentSurveyUser() : ?SurveyUser { if (!$this->isSurveyUserLoggedIn()) return null; + return $this->getSurveyUser($this->getCurrentSurveyUserId()); + } + + /** + * Retrieves a survey user with error handling + * + * @param $userId int Get this survey user + * @return SurveyUser|null The user or null if there was an error + */ + public function getSurveyUser(int $userId) : ?SurveyUser { try { - $user = $this->surveyUserMapper->load( - $this->getCurrentSurveyUserId()); + $user = $this->surveyUserMapper->load($userId); return $user; } catch (IMapperException $e) { // TODO FORMSTODO log diff --git a/src/components/Users/UserRow.vue b/src/components/Users/UserRow.vue index 0d0389704..fd7bc4ecb 100644 --- a/src/components/Users/UserRow.vue +++ b/src/components/Users/UserRow.vue @@ -37,9 +37,14 @@ {{ user.phone }}
    - +
    @@ -84,18 +89,20 @@ export default { computed: { itemIcon() { - let common = ' user-icon users-clickable user-icon-user' - common += ' ' - return common - }, - - isFolder() { - return this.node.type === 'folder' + return this.user.status === 0 + ? 'icon-user users-preicon' + : 'icon-userban users-preicon' }, }, methods: { + unbanUserClick() { + if (this.banDisabled) return + this.$emit('user-unban-clicked', this.user) + }, + banUserClick() { + if (this.banDisabled) return this.$emit('user-ban-clicked', this.user) }, }, @@ -244,7 +251,9 @@ div.user-drawerbutton { } div.user-padloading { - padding: 15px; + position: absolute; + top: 2px; + right: 9px; } diff --git a/src/views/Users.vue b/src/views/Users.vue index ebd793b03..8262dee97 100644 --- a/src/views/Users.vue +++ b/src/views/Users.vue @@ -33,6 +33,13 @@ + +
    @@ -46,11 +53,12 @@
    @@ -91,6 +99,7 @@ export default { data() { return { + dataFilterBanned: true, dataFilter: '', loadedFilter: null, banDisabled: false, @@ -102,6 +111,13 @@ export default { } }, + computed: { + filteredSurveyUsers() { + if (!this.dataFilterBanned) return this.surveyUsers.results + return this.surveyUsers.results.filter(user => { return user.status === 0 }) + }, + }, + watch: { dataFilter(val) { this.debounceLoadSurveyUsers(val) @@ -110,16 +126,38 @@ export default { beforeMount() { this.dataFilter = this.$route.params.filter - this.loadSurveyUsers(this.dataFilter) + this.loadUsers(this.dataFilter) SetWindowTitle(this.formTitle) }, methods: { banUser(sender) { - console.info(sender) + this.sendBanUser(sender) + }, + + unbanUser(sender) { + this.sendBanUser(sender, 0) }, debounceLoadSurveyUsers: debounce(function(filter = null) { + this.loadUsers(filter) + }, 500), + + sendBanUser(user, status = 1) { + this.banDisabled = true + axios.put(generateOcsUrl('apps/forms/api/v1', 2) + 'surveyusers/status/' + user.id + '/' + status) + .then(response => { + user.status = status + }) + .catch(error => { + console.error(error) + showError(t('forms', 'There was an error while modifying the user account')) + }).finally(() => { + this.banDisabled = false + }) + }, + + loadUsers(filter = null) { if (filter !== null && filter !== '') filter = '/' + filter else filter = '' @@ -128,6 +166,7 @@ export default { this.filterLoading = true axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'surveyusers/list/' + this.dataPage + filter) .then(response => { + console.info('response') this.surveyUsers = response.data this.loadedFilter = filter }) @@ -136,24 +175,8 @@ export default { showError(t('forms', 'There was an error while loading the list of users')) }).finally(() => { this.filterLoading = false + this.loadingUsers = false }) - }, 500), - - async loadSurveyUsers(filter = null) { - this.loadingUsers = true - if (filter !== null && filter !== '') filter = '/' + filter - else filter = '' - - try { - const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'surveyusers/list/' + this.dataPage + filter) - this.loadedFilter = filter - this.surveyUsers = response.data - } catch (error) { - console.error(error) - showError(t('forms', 'There was an error while loading the list of users')) - } finally { - this.loadingUsers = false - } }, }, } @@ -174,7 +197,7 @@ div.users-container { div.loading-pos { position: absolute; right: 12px; - top: 12px; + top: 65px; } div.topbar-relative { @@ -189,4 +212,8 @@ div.topbar-container { background: var(--color-background-darker); } +.user-padleft { + padding-left: 30px; +} + From 547f1bcdff0b6a4ab4a2390f2fcc6c253d0b1b14 Mon Sep 17 00:00:00 2001 From: Zeon Date: 2020年11月13日 10:55:23 +0000 Subject: [PATCH 28/35] Navi atalakitas elott --- l10n/hu.js | 1 + lib/Controller/SureveyUserController.php | 23 ++++++++++------------- src/Forms.vue | 11 +++++++++++ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/l10n/hu.js b/l10n/hu.js index 49b64f0f0..feba74852 100644 --- a/l10n/hu.js +++ b/l10n/hu.js @@ -1,6 +1,7 @@ OC.L10N.register( "forms", { + "Survey users": "Kérdőív kitöltők", "Unban user and include survey results": "Felhasználói fiók tiltásának visszavonása és a válaszok figyelembevétele", "Ban user and exclude survey results": "Felhasználói fiók tiltása és a válaszok figyelmen kívül hagyása", "Hide banned users": "Kitiltott felhasználók elrejtése", diff --git a/lib/Controller/SureveyUserController.php b/lib/Controller/SureveyUserController.php index b01265f6c..335f9c591 100644 --- a/lib/Controller/SureveyUserController.php +++ b/lib/Controller/SureveyUserController.php @@ -766,7 +766,10 @@ private function sendMail(int $template, string $link, string $to) { * @return DataResponse */ public function apiSetStatus(int $user, int $status): DataResponse { - // TODO ACCESS CHECK + if ($this->settingsController->isAccessControlEnabled() && + !$this->settingsController->canViewPersonalData()) + return new DataResponse($this->l10n->t('Access denied'), + Http::STATUS_UNAUTHORIZED); $user = (int)$user; $status = (int)$status; @@ -782,6 +785,8 @@ public function apiSetStatus(int $user, int $status): DataResponse { try { $userObj->setStatus((int)$status); $this->surveyUserMapper->update($userObj); + + // TODO toggle survey results } catch (IMapperException $e) { // TODO FORMSTODO log return new DataResponse($this->l10n->t('Internal error'), @@ -800,18 +805,10 @@ public function apiSetStatus(int $user, int $status): DataResponse { * @return DataResponse List of survey users */ public function apiList(string $filter, int $page): DataResponse { - - // TODO ACCESS CHECK - -// if ($this->settingsController->isAccessToAllEnabled()) -// $forms = $this->formMapper->findAll(); -// else -// $forms = $this->formMapper->findAllByOwnerId($this->currentUser->getUID()); -// -// // The current user can view survey results. This could be -// // refined to a per form basis later if required -// $canView = !$this->settingsController->isAccessControlEnabled() || -// $this->settingsController->canViewResults(); + if ($this->settingsController->isAccessControlEnabled() && + !$this->settingsController->canViewPersonalData()) + return new DataResponse($this->l10n->t('Access denied'), + Http::STATUS_UNAUTHORIZED); $limit = 100; diff --git a/src/Forms.vue b/src/Forms.vue index 49915cc1d..3bd9e22e1 100644 --- a/src/Forms.vue +++ b/src/Forms.vue @@ -28,6 +28,10 @@ button-class="icon-add" :text="t('forms', 'New form')" @click="onNewForm" /> +