diff --git a/app/AdminModule/Components/UsersGridControl.php b/app/AdminModule/Components/UsersGridControl.php index 702a4fd60..cdb1a8ccf 100644 --- a/app/AdminModule/Components/UsersGridControl.php +++ b/app/AdminModule/Components/UsersGridControl.php @@ -10,14 +10,16 @@ use App\Model\Enums\PaymentType; use App\Model\Enums\SkautIsEventType; use App\Model\Settings\CustomInput\CustomCheckbox; +use App\Model\Settings\CustomInput\CustomDate; +use App\Model\Settings\CustomInput\CustomDateTime; use App\Model\Settings\CustomInput\CustomInputRepository; +use App\Model\Settings\CustomInput\CustomMultiSelect; use App\Model\Settings\CustomInput\CustomSelect; use App\Model\Settings\CustomInput\CustomText; use App\Model\Settings\Settings; use App\Model\Settings\SettingsException; use App\Model\User\CustomInputValue\CustomCheckboxValue; use App\Model\User\CustomInputValue\CustomFileValue; -use App\Model\User\CustomInputValue\CustomSelectValue; use App\Model\User\CustomInputValue\CustomTextValue; use App\Model\User\User; use App\Model\User\UserRepository; @@ -48,7 +50,6 @@ use Ublaboo\DataGrid\DataGrid; use Ublaboo\DataGrid\Exception\DataGridColumnStatusException; use Ublaboo\DataGrid\Exception\DataGridException; -use function array_merge; use function array_slice; use function array_values; use function explode; @@ -351,8 +352,6 @@ public function createComponentUsersGrid(string $name) : void return $customInputValue->getValue() ? $this->translator->translate('admin.common.yes') : $this->translator->translate('admin.common.no'); - case $customInputValue instanceof CustomSelectValue: - return $customInputValue->getValueOption(); case $customInputValue instanceof CustomFileValue: return $customInputValue->getValue() ? Html::el('a') @@ -365,6 +364,8 @@ public function createComponentUsersGrid(string $name) : void Html::el('span')->setAttribute('class', 'fa fa-download') ) : ''; + default: + return $customInputValue->getValueText(); } } @@ -405,22 +406,56 @@ public function createComponentUsersGrid(string $name) : void break; case $customInput instanceof CustomSelect: - $columnCustomInput->setFilterSelect(array_merge(['' => 'admin.common.all'], $customInput->getSelectOptions())) - ->setCondition(static function (QueryBuilder $qb, string $value) use ($customInput) : void { - if ($value === '') { - return; - } else { - $qb->leftJoin('u.customInputValues', 'uCIV3') - ->leftJoin('uCIV3.input', 'uCIVI3') - ->leftJoin('App\Model\User\CustomInputValue\CustomSelectValue', 'uCSV', 'WITH', 'uCIV3.id = uCSV.id') - ->andWhere('uCIVI3.id = :iid3 OR uCIVI3.id IS NULL') - ->andWhere('uCSV.value = :ivalue3') - ->setParameter('iid3', $customInput->getId()) - ->setParameter('ivalue3', $value); - } + $columnCustomInput->setFilterMultiSelect($customInput->getFilterOptions()) + ->setCondition(static function (QueryBuilder $qb, ArrayHash $values) use ($customInput) : void { + $qb->leftJoin('u.customInputValues', 'uCIV3') + ->leftJoin('uCIV3.input', 'uCIVI3') + ->leftJoin('App\Model\User\CustomInputValue\CustomSelectValue', 'uCSV', 'WITH', 'uCIV3.id = uCSV.id') + ->andWhere('uCIVI3.id = :iid3 OR uCIVI3.id IS NULL') + ->andWhere('uCSV.value in (:ivalues3)') + ->setParameter('iid3', $customInput->getId()) + ->setParameter('ivalues3', (array) $values); }) ->setTranslateOptions(); break; + + case $customInput instanceof CustomMultiSelect: + $columnCustomInput->setFilterMultiSelect($customInput->getSelectOptions()) + ->setCondition(static function (QueryBuilder $qb, ArrayHash $values) use ($customInput) : void { + $qb->leftJoin('u.customInputValues', 'uCIV4') + ->leftJoin('uCIV4.input', 'uCIVI4') + ->leftJoin('App\Model\User\CustomInputValue\CustomMultiSelectValue', 'uCMSV', 'WITH', 'uCIV4.id = uCMSV.id') + ->andWhere('uCIVI4.id = :iid4 OR uCIVI4.id IS NULL') + ->andWhere('uCMSV.value in (:ivalues4)') + ->setParameter('iid4', $customInput->getId()) + ->setParameter('ivalues4', (array) $values); + }) + ->setTranslateOptions(); + break; + + case $customInput instanceof CustomDate: + $columnCustomInput->setSortable() + ->setSortableCallback(static function (QueryBuilder $qb, array $sort) use ($customInput, $columnCustomInputName) : void { + $qb->leftJoin('u.customInputValues', 'uCIV5') + ->leftJoin('uCIV5.input', 'uCIVI5') + ->leftJoin('App\Model\User\CustomInputValue\CustomDateValue', 'uCDV', 'WITH', 'uCIV5.id = uCDV.id') + ->andWhere('uCIVI5.id = :iid5 OR uCIVI5.id IS NULL') + ->setParameter('iid5', $customInput->getId()) + ->orderBy('uCDV.value', $sort[$columnCustomInputName]); + }); + break; + + case $customInput instanceof CustomDateTime: + $columnCustomInput->setSortable() + ->setSortableCallback(static function (QueryBuilder $qb, array $sort) use ($customInput, $columnCustomInputName) : void { + $qb->leftJoin('u.customInputValues', 'uCIV6') + ->leftJoin('uCIV6.input', 'uCIVI6') + ->leftJoin('App\Model\User\CustomInputValue\CustomDateTimeValue', 'uCDTV', 'WITH', 'uCIV6.id = uCDTV.id') + ->andWhere('uCIVI6.id = :iid6 OR uCIVI6.id IS NULL') + ->setParameter('iid6', $customInput->getId()) + ->orderBy('uCDTV.value', $sort[$columnCustomInputName]); + }); + break; } } diff --git a/app/AdminModule/ConfigurationModule/Components/CustomInputsGridControl.php b/app/AdminModule/ConfigurationModule/Components/CustomInputsGridControl.php index e7252d24d..227c4c8e2 100644 --- a/app/AdminModule/ConfigurationModule/Components/CustomInputsGridControl.php +++ b/app/AdminModule/ConfigurationModule/Components/CustomInputsGridControl.php @@ -4,9 +4,12 @@ namespace App\AdminModule\ConfigurationModule\Components; +use App\Model\Acl\Role; use App\Model\Settings\CustomInput\CustomInput; use App\Model\Settings\CustomInput\CustomInputRepository; +use App\Model\Settings\CustomInput\CustomMultiSelect; use App\Model\Settings\CustomInput\CustomSelect; +use App\Services\AclService; use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\ORMException; use Nette\Application\AbortException; @@ -15,6 +18,7 @@ use Ublaboo\DataGrid\DataGrid; use Ublaboo\DataGrid\Exception\DataGridColumnStatusException; use Ublaboo\DataGrid\Exception\DataGridException; +use function count; /** * Komponenta pro správu vlastních polí přihlášky. @@ -27,10 +31,16 @@ class CustomInputsGridControl extends Control private CustomInputRepository $customInputRepository; - public function __construct(ITranslator $translator, CustomInputRepository $customInputRepository) - { + private AclService $aclService; + + public function __construct( + ITranslator $translator, + CustomInputRepository $customInputRepository, + AclService $aclService + ) { $this->translator = $translator; $this->customInputRepository = $customInputRepository; + $this->aclService = $aclService; } /** @@ -59,6 +69,13 @@ public function createComponentCustomInputsGrid(string $name) : void $grid->addColumnText('name', 'admin.configuration.custom_inputs_name'); + $grid->addColumnText('roles', 'admin.configuration.custom_inputs_roles', 'rolesText') + ->setRendererOnCondition(function () { + return $this->translator->translate('admin.configuration.custom_inputs_roles_all'); + }, function (CustomInput $input) { + return count($this->aclService->getRolesWithoutRolesOptions([Role::GUEST, Role::UNAPPROVED, Role::NONREGISTERED])) === $input->getRoles()->count(); + }); + $grid->addColumnText('type', 'admin.configuration.custom_inputs_type') ->setRenderer(function (CustomInput $input) { return $this->translator->translate('admin.common.custom_' . $input->getType()); @@ -76,7 +93,7 @@ public function createComponentCustomInputsGrid(string $name) : void $grid->addColumnText('options', 'admin.configuration.custom_inputs_options') ->setRenderer(static function (CustomInput $input) { - return $input instanceof CustomSelect ? $input->getOptions() : null; + return $input instanceof CustomSelect || $input instanceof CustomMultiSelect ? $input->getOptionsText() : null; }); $grid->addToolbarButton('Application:add') diff --git a/app/AdminModule/ConfigurationModule/Forms/CustomInputFormFactory.php b/app/AdminModule/ConfigurationModule/Forms/CustomInputFormFactory.php index b9e84d098..b4f2c2fb6 100644 --- a/app/AdminModule/ConfigurationModule/Forms/CustomInputFormFactory.php +++ b/app/AdminModule/ConfigurationModule/Forms/CustomInputFormFactory.php @@ -5,19 +5,27 @@ namespace App\AdminModule\ConfigurationModule\Forms; use App\AdminModule\Forms\BaseFormFactory; +use App\Model\Acl\Role; +use App\Model\Acl\RoleRepository; use App\Model\Settings\CustomInput\CustomCheckbox; +use App\Model\Settings\CustomInput\CustomDate; +use App\Model\Settings\CustomInput\CustomDateTime; use App\Model\Settings\CustomInput\CustomFile; use App\Model\Settings\CustomInput\CustomInput; use App\Model\Settings\CustomInput\CustomInputRepository; +use App\Model\Settings\CustomInput\CustomMultiSelect; use App\Model\Settings\CustomInput\CustomSelect; use App\Model\Settings\CustomInput\CustomText; +use App\Services\AclService; +use App\Utils\Helpers; use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\ORMException; use Nette; use Nette\Application\UI\Form; use stdClass; +use function array_keys; +use function array_map; use function explode; -use function implode; use function trim; /** @@ -38,10 +46,20 @@ class CustomInputFormFactory private CustomInputRepository $customInputRepository; - public function __construct(BaseFormFactory $baseFormFactory, CustomInputRepository $customInputRepository) - { + private AclService $aclService; + + private RoleRepository $roleRepository; + + public function __construct( + BaseFormFactory $baseFormFactory, + CustomInputRepository $customInputRepository, + AclService $aclService, + RoleRepository $roleRepository + ) { $this->baseFormFactory = $baseFormFactory; $this->customInputRepository = $customInputRepository; + $this->aclService = $aclService; + $this->roleRepository = $roleRepository; } /** @@ -58,8 +76,13 @@ public function create(int $id) : Form $form->addText('name', 'admin.configuration.custom_inputs_name') ->addRule(Form::FILLED, 'admin.configuration.custom_inputs_name_empty'); + $rolesOptions = $this->aclService->getRolesWithoutRolesOptions([Role::GUEST, Role::UNAPPROVED, Role::NONREGISTERED]); + $rolesSelect = $form->addMultiSelect('roles', 'admin.configuration.custom_inputs_roles', $rolesOptions) + ->addRule(Form::FILLED, 'admin.configuration.custom_inputs_roles_empty'); + $typeSelect = $form->addSelect('type', 'admin.configuration.custom_inputs_type', $this->prepareCustomInputTypesOptions()); $typeSelect->addCondition($form::EQUAL, CustomInput::SELECT)->toggle('custom-input-select'); + $typeSelect->addCondition($form::EQUAL, CustomInput::MULTISELECT)->toggle('custom-input-select'); $form->addCheckbox('mandatory', 'admin.configuration.custom_inputs_edit_mandatory'); @@ -79,14 +102,17 @@ public function create(int $id) : Form $form->setDefaults([ 'id' => $id, 'name' => $this->customInput->getName(), + 'roles' => Helpers::getIds($this->customInput->getRoles()), 'type' => $this->customInput->getType(), 'mandatory' => $this->customInput->isMandatory(), ]); - if ($this->customInput instanceof CustomSelect) { + if ($this->customInput instanceof CustomSelect || $this->customInput instanceof CustomMultiSelect) { $customInput = $this->customInput; - $optionsText->setDefaultValue($customInput->getOptions()); + $optionsText->setDefaultValue($customInput->getOptionsText()); } + } else { + $rolesSelect->setDefaultValue(array_keys($rolesOptions)); } $form->onSuccess[] = [$this, 'processForm']; @@ -118,24 +144,42 @@ public function processForm(Form $form, stdClass $values) : void case CustomInput::SELECT: $this->customInput = new CustomSelect(); + $options = array_map( + static function (string $o) { + return trim($o); + }, + explode(',', $values->options) + ); + $this->customInput->setOptions($options); + break; - $options = explode(',', $values->options); - $optionsTrimmed = []; - foreach ($options as $option) { - $optionsTrimmed[] = trim($option); - } - - $this->customInput->setOptions(implode(', ', $optionsTrimmed)); - + case CustomInput::MULTISELECT: + $this->customInput = new CustomMultiSelect(); + $options = array_map( + static function (string $o) { + return trim($o); + }, + explode(',', $values->options) + ); + $this->customInput->setOptions($options); break; case CustomInput::FILE: $this->customInput = new CustomFile(); break; + + case CustomInput::DATE: + $this->customInput = new CustomDate(); + break; + + case CustomInput::DATETIME: + $this->customInput = new CustomDateTime(); + break; } } $this->customInput->setName($values->name); + $this->customInput->setRoles($this->roleRepository->findRolesByIds($values->roles)); $this->customInput->setMandatory($values->mandatory); $this->customInputRepository->save($this->customInput); diff --git a/app/AdminModule/Forms/AddRoleFormFactory.php b/app/AdminModule/Forms/AddRoleFormFactory.php index 2ce45797f..42e6552ef 100644 --- a/app/AdminModule/Forms/AddRoleFormFactory.php +++ b/app/AdminModule/Forms/AddRoleFormFactory.php @@ -113,7 +113,6 @@ public function processForm(Form $form, stdClass $values) : void $role->setRegisterable($parent->isRegisterable()); $role->setRegisterableFrom($parent->getRegisterableFrom()); $role->setRegisterableTo($parent->getRegisterableTo()); - $role->setDisplayArrivalDeparture($parent->isDisplayArrivalDeparture()); } else { $nonregistered = $this->roleRepository->findBySystemName(Role::NONREGISTERED); foreach ($nonregistered->getPages() as $page) { diff --git a/app/AdminModule/Forms/EditRoleFormFactory.php b/app/AdminModule/Forms/EditRoleFormFactory.php index 63b8454bb..0874767fc 100644 --- a/app/AdminModule/Forms/EditRoleFormFactory.php +++ b/app/AdminModule/Forms/EditRoleFormFactory.php @@ -120,8 +120,6 @@ public function create(int $id) : Form // $form->addCheckbox('syncedWithSkautIs', 'admin.acl.roles_synced_with_skaut_is'); - $form->addCheckbox('displayArrivalDeparture', 'admin.acl.roles_display_arrival_departure'); - $form->addCheckbox('feeFromSubevents', 'admin.acl.roles_fee_from_subevents_checkbox') ->addCondition(Form::EQUAL, false) ->toggle('fee'); @@ -189,7 +187,6 @@ public function create(int $id) : Form 'capacity' => $this->role->getCapacity(), 'approvedAfterRegistration' => $this->role->isApprovedAfterRegistration(), // 'syncedWithSkautIs' => $this->role->isSyncedWithSkautIS(), - 'displayArrivalDeparture' => $this->role->isDisplayArrivalDeparture(), 'feeFromSubevents' => $this->role->getFee() === null, 'fee' => $this->role->getFee(), 'minimumAge' => $this->role->getMinimumAge(), @@ -226,7 +223,6 @@ public function processForm(Form $form, stdClass $values) : void $this->role->setCapacity($capacity); $this->role->setApprovedAfterRegistration($values->approvedAfterRegistration); // $this->role->setSyncedWithSkautIS($values->syncedWithSkautIs); - $this->role->setDisplayArrivalDeparture($values->displayArrivalDeparture); $this->role->setMinimumAge($values->minimumAge); $this->role->setPermissions($this->permissionRepository->findPermissionsByIds($values->permissions)); $this->role->setPages($this->pageRepository->findPagesBySlugs($values->pages)); diff --git a/app/AdminModule/Forms/EditUserSeminarFormFactory.php b/app/AdminModule/Forms/EditUserSeminarFormFactory.php index 3023272ae..498cd68b7 100644 --- a/app/AdminModule/Forms/EditUserSeminarFormFactory.php +++ b/app/AdminModule/Forms/EditUserSeminarFormFactory.php @@ -9,14 +9,20 @@ use App\Model\Mailing\Template; use App\Model\Mailing\TemplateVariable; use App\Model\Settings\CustomInput\CustomCheckbox; +use App\Model\Settings\CustomInput\CustomDate; +use App\Model\Settings\CustomInput\CustomDateTime; use App\Model\Settings\CustomInput\CustomFile; use App\Model\Settings\CustomInput\CustomInputRepository; +use App\Model\Settings\CustomInput\CustomMultiSelect; use App\Model\Settings\CustomInput\CustomSelect; use App\Model\Settings\CustomInput\CustomText; use App\Model\Settings\Settings; use App\Model\User\CustomInputValue\CustomCheckboxValue; +use App\Model\User\CustomInputValue\CustomDateTimeValue; +use App\Model\User\CustomInputValue\CustomDateValue; use App\Model\User\CustomInputValue\CustomFileValue; use App\Model\User\CustomInputValue\CustomInputValueRepository; +use App\Model\User\CustomInputValue\CustomMultiSelectValue; use App\Model\User\CustomInputValue\CustomSelectValue; use App\Model\User\CustomInputValue\CustomTextValue; use App\Model\User\User; @@ -26,7 +32,9 @@ use App\Services\FilesService; use App\Services\MailService; use App\Services\SettingsService; +use App\Utils\Helpers; use App\Utils\Validators; +use InvalidArgumentException; use Nette; use Nette\Application\UI\Form; use Nette\Forms\Controls\MultiSelectBox; @@ -34,10 +42,10 @@ use Nette\Utils\Random; use Nette\Utils\Strings; use Nettrine\ORM\EntityManagerDecorator; +use Nextras\FormComponents\Controls\DateControl; use Nextras\FormComponents\Controls\DateTimeControl; use stdClass; use Throwable; -use function property_exists; use const UPLOAD_ERR_OK; /** @@ -120,7 +128,7 @@ public function create(int $id) : Form $form->addHidden('id'); if (! $this->user->isExternalLector()) { - $form->addMultiSelect( + $rolesSelect = $form->addMultiSelect( 'roles', 'admin.users.users_roles', $this->aclService->getRolesWithoutRolesOptionsWithCapacity([Role::GUEST, Role::UNAPPROVED]) @@ -133,38 +141,84 @@ public function create(int $id) : Form $form->addCheckbox('attended', 'admin.users.users_attended_form'); - if ($this->user->hasDisplayArrivalDepartureRole()) { - $arrivalDateTime = new DateTimeControl('admin.users.users_arrival'); - $form->addComponent($arrivalDateTime, 'arrival'); - $departureDateTime = new DateTimeControl('admin.users.users_departure'); - $form->addComponent($departureDateTime, 'departure'); - } + foreach ($this->customInputRepository->findAll() as $customInput) { + $customInputId = 'custom' . $customInput->getId(); - foreach ($this->customInputRepository->findAllOrderedByPosition() as $customInput) { - if ($customInput instanceof CustomText) { - $custom = $form->addText('custom' . $customInput->getId(), $customInput->getName()); - /** @var ?CustomTextValue $customInputValue */ - $customInputValue = $this->user->getCustomInputValue($customInput); - if ($customInputValue) { - $custom->setDefaultValue($customInputValue->getValue()); - } - } elseif ($customInput instanceof CustomCheckbox) { - $custom = $form->addCheckbox('custom' . $customInput->getId(), $customInput->getName()); - /** @var ?CustomCheckboxValue $customInputValue */ - $customInputValue = $this->user->getCustomInputValue($customInput); - if ($customInputValue) { - $custom->setDefaultValue($customInputValue->getValue()); - } - } elseif ($customInput instanceof CustomSelect) { - $custom = $form->addSelect('custom' . $customInput->getId(), $customInput->getName(), $customInput->getSelectOptions()); - /** @var ?CustomSelectValue $customInputValue */ - $customInputValue = $this->user->getCustomInputValue($customInput); - if ($customInputValue) { - $custom->setDefaultValue($customInputValue->getValue()); - } - } elseif ($customInput instanceof CustomFile) { - $form->addUpload('custom' . $customInput->getId(), $customInput->getName()); + switch (true) { + case $customInput instanceof CustomText: + $custom = $form->addText($customInputId, $customInput->getName()); + /** @var ?CustomTextValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + break; + + case $customInput instanceof CustomCheckbox: + $custom = $form->addCheckbox($customInputId, $customInput->getName()); + /** @var ?CustomCheckboxValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + break; + + case $customInput instanceof CustomSelect: + $custom = $form->addSelect($customInputId, $customInput->getName(), $customInput->getSelectOptions()); + /** @var ?CustomSelectValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + break; + + case $customInput instanceof CustomMultiSelect: + $custom = $form->addMultiSelect($customInputId, $customInput->getName(), $customInput->getSelectOptions()); + /** @var ?CustomMultiSelectValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + break; + + case $customInput instanceof CustomFile: + $custom = $form->addUpload($customInputId, $customInput->getName()); + break; + + case $customInput instanceof CustomDate: + $custom = new DateControl($customInput->getName()); + /** @var ?CustomDateValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + $form->addComponent($custom, $customInputId); + break; + + case $customInput instanceof CustomDateTime: + $custom = new DateTimeControl($customInput->getName()); + /** @var ?CustomDateTimeValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + $form->addComponent($custom, $customInputId); + break; + + default: + throw new InvalidArgumentException(); } + + $custom->setOption('id', 'form-group-' . $customInputId); + + $rolesSelect->addCondition(self::class . '::toggleCustomInputVisibility', Helpers::getIds($customInput->getRoles())) + ->toggle('form-group-' . $customInputId); } } @@ -183,8 +237,6 @@ public function create(int $id) : Form 'roles' => $this->roleRepository->findRolesIds($this->user->getRoles()), 'approved' => $this->user->isApproved(), 'attended' => $this->user->isAttended(), - 'arrival' => $this->user->getArrival(), - 'departure' => $this->user->getDeparture(), 'about' => $this->user->getAbout(), 'privateNote' => $this->user->getNote(), ]); @@ -217,7 +269,7 @@ public function processForm(Form $form, stdClass $values) : void $this->user->setApproved($values->approved); $this->user->setAttended($values->attended); - foreach ($this->customInputRepository->findAllOrderedByPosition() as $customInput) { + foreach ($this->customInputRepository->findByRolesOrderedByPosition($this->user->getRoles()) as $customInput) { $customInputValue = $this->user->getCustomInputValue($customInput); $customInputName = 'custom' . $customInput->getId(); $oldValue = null; @@ -241,6 +293,12 @@ public function processForm(Form $form, stdClass $values) : void $oldValue = $customInputValue->getValue(); $newValue = $values->$customInputName; $customInputValue->setValue($newValue); + } elseif ($customInput instanceof CustomMultiSelect) { + /** @var CustomMultiSelectValue $customInputValue */ + $customInputValue = $customInputValue ?: new CustomMultiSelectValue(); + $oldValue = $customInputValue->getValue(); + $newValue = $values->$customInputName; + $customInputValue->setValue($newValue); } elseif ($customInput instanceof CustomFile) { /** @var CustomFileValue $customInputValue */ $customInputValue = $customInputValue ?: new CustomFileValue(); @@ -252,6 +310,18 @@ public function processForm(Form $form, stdClass $values) : void $this->filesService->save($newValue, $path); $customInputValue->setValue($path); } + } elseif ($customInput instanceof CustomDate) { + /** @var CustomDateValue $customInputValue */ + $customInputValue = $customInputValue ?: new CustomDateValue(); + $oldValue = $customInputValue->getValue(); + $newValue = $values->$customInputName; + $customInputValue->setValue($newValue); + } elseif ($customInput instanceof CustomDateTime) { + /** @var CustomDateTimeValue $customInputValue */ + $customInputValue = $customInputValue ?: new CustomDateTimeValue(); + $oldValue = $customInputValue->getValue(); + $newValue = $values->$customInputName; + $customInputValue->setValue($newValue); } $customInputValue->setUser($this->user); @@ -262,14 +332,6 @@ public function processForm(Form $form, stdClass $values) : void $customInputValueChanged = true; } } - - if (property_exists($values, 'arrival')) { - $this->user->setArrival($values->arrival); - } - - if (property_exists($values, 'departure')) { - $this->user->setDeparture($values->departure); - } } $this->user->setAbout($values->about); @@ -307,6 +369,17 @@ public function validateRolesCapacities(MultiSelectBox $field) : bool return $this->validators->validateRolesCapacities($selectedRoles, $this->user); } + /** + * Přepíná zobrazení vlastních polí podle kombinace rolí. + * Je nutná, na výsledku nezáleží (používá se javascript funkce). + * + * @param int[] $customInputRoles + */ + public static function toggleCustomInputVisibility(MultiSelectBox $field, array $customInputRoles) : bool + { + return false; + } + /** * Vygeneruje cestu souboru. */ diff --git a/app/AdminModule/Presenters/UsersPresenter.php b/app/AdminModule/Presenters/UsersPresenter.php index 3a96ed9bf..e181e597e 100644 --- a/app/AdminModule/Presenters/UsersPresenter.php +++ b/app/AdminModule/Presenters/UsersPresenter.php @@ -80,10 +80,8 @@ public function renderDetail(int $id) : void $this->template->detailUser = $user; - $this->template->customInputs = $this->customInputRepository->findAllOrderedByPosition(); - $this->template->customInputTypeText = CustomInput::TEXT; + $this->template->customInputs = $this->customInputRepository->findByRolesOrderedByPosition($user->getRoles()); $this->template->customInputTypeCheckbox = CustomInput::CHECKBOX; - $this->template->customInputTypeSelect = CustomInput::SELECT; $this->template->customInputTypeFile = CustomInput::FILE; $this->template->roleAdminName = $this->roleRepository->findBySystemName(Role::ADMIN)->getName(); diff --git a/app/AdminModule/Presenters/templates/Users/detail.latte b/app/AdminModule/Presenters/templates/Users/detail.latte index 1ea46d3ef..b73656666 100644 --- a/app/AdminModule/Presenters/templates/Users/detail.latte +++ b/app/AdminModule/Presenters/templates/Users/detail.latte @@ -106,38 +106,24 @@ - {if $detailUser->hasDisplayArrivalDepartureRole()} -
-
{_admin.users.users_arrival}
-
{$detailUser->getArrival()|date:'j. n. Y H:i'}
-
- -
-
{_admin.users.users_departure}
-
{$detailUser->getDeparture()|date:'j. n. Y H:i'}
-
- {/if} -
{$customInput->getName()}
{var $customInputValue = $detailUser->getCustomInputValue($customInput)} {if $customInputValue} {switch $customInput->getType()} - {case $customInputTypeText} - {$customInputValue->getValue()} {case $customInputTypeCheckbox} {if $customInputValue->getValue()} {_admin.common.yes} {else} {_admin.common.no} {/if} - {case $customInputTypeSelect} - {$customInputValue->getValueOption()} {case $customInputTypeFile} {array_values(array_slice(explode('/', $customInputValue->getValue()), -1))[0]} + {default} + {$customInputValue->getValueText()} {/switch} {/if}
@@ -170,6 +156,17 @@
{else} {control editUserSeminarForm} + {/if} diff --git a/app/Model/Acl/Role.php b/app/Model/Acl/Role.php index fe1fe0e8a..2c624f02e 100644 --- a/app/Model/Acl/Role.php +++ b/app/Model/Acl/Role.php @@ -189,13 +189,6 @@ class Role */ protected int $minimumAge = 0; - /** - * Evidovat příjezd a odjezd. - * - * @ORM\Column(type="boolean") - */ - protected bool $displayArrivalDeparture = false; - /** * Synchronizovat účastníky v roli se skautIS. * @@ -465,16 +458,6 @@ public function setMinimumAge(int $age) : void $this->minimumAge = $age; } - public function isDisplayArrivalDeparture() : bool - { - return $this->displayArrivalDeparture; - } - - public function setDisplayArrivalDeparture(bool $displayArrivalDeparture) : void - { - $this->displayArrivalDeparture = $displayArrivalDeparture; - } - public function isSyncedWithSkautIS() : bool { return $this->syncedWithSkautIS; diff --git a/app/Model/Acl/RoleRepository.php b/app/Model/Acl/RoleRepository.php index a71ad9db3..62960c112 100644 --- a/app/Model/Acl/RoleRepository.php +++ b/app/Model/Acl/RoleRepository.php @@ -125,7 +125,7 @@ public function findRolesIds(Collection $roles) : array * * @return Collection|Role[] */ - public function findFilteredRoles(bool $registerableNowOnly, bool $subeventsRoleOnly, bool $arrivalDepartureOnly, bool $includeUsers, ?User $user = null) : Collection + public function findFilteredRoles(bool $registerableNowOnly, bool $subeventsRoleOnly, bool $includeUsers, ?User $user = null) : Collection { $qb = $this->createQueryBuilder('r'); @@ -150,10 +150,6 @@ public function findFilteredRoles(bool $registerableNowOnly, bool $subeventsRole $query = $query->andWhere('r.fee IS NULL'); } - if ($arrivalDepartureOnly) { - $query = $query->andWhere('r.displayArrivalDeparture = TRUE'); - } - if ($includeUsers) { $query = $query->orWhere('r in (:users_roles)')->setParameter('users_roles', $user->getRoles()); } diff --git a/app/Model/Settings/CustomInput/CustomDate.php b/app/Model/Settings/CustomInput/CustomDate.php new file mode 100644 index 000000000..a908a0ce0 --- /dev/null +++ b/app/Model/Settings/CustomInput/CustomDate.php @@ -0,0 +1,20 @@ + + */ +class CustomDate extends CustomInput +{ + protected string $type = CustomInput::DATE; +} diff --git a/app/Model/Settings/CustomInput/CustomDateTime.php b/app/Model/Settings/CustomInput/CustomDateTime.php new file mode 100644 index 000000000..437c6dbc6 --- /dev/null +++ b/app/Model/Settings/CustomInput/CustomDateTime.php @@ -0,0 +1,20 @@ + + */ +class CustomDateTime extends CustomInput +{ + protected string $type = CustomInput::DATETIME; +} diff --git a/app/Model/Settings/CustomInput/CustomInput.php b/app/Model/Settings/CustomInput/CustomInput.php index 1166c726b..ae367b87a 100644 --- a/app/Model/Settings/CustomInput/CustomInput.php +++ b/app/Model/Settings/CustomInput/CustomInput.php @@ -4,11 +4,13 @@ namespace App\Model\Settings\CustomInput; +use App\Model\Acl\Role; use App\Model\User\CustomInputValue\CustomInputValue; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Nettrine\ORM\Entity\Attributes\Id; +use function implode; /** * Abstraktní entita vlastní pole přihlášky. @@ -21,7 +23,10 @@ * "custom_checkbox" = "CustomCheckbox", * "custom_text" = "CustomText", * "custom_select" = "CustomSelect", - * "custom_file" = "CustomFile" + * "custom_multiselect" = "CustomMultiSelect", + * "custom_file" = "CustomFile", + * "custom_date" = "CustomDate", + * "custom_datetime" = "CustomDateTime" * }) * * @author Jan Staněk @@ -43,16 +48,35 @@ abstract class CustomInput */ public const SELECT = 'select'; + /** + * Výběrové pole s více možnostmi. + */ + public const MULTISELECT = 'multiselect'; + /** * Soubor. */ public const FILE = 'file'; + /** + * Datum. + */ + public const DATE = 'date'; + + /** + * Datum čas. + */ + public const DATETIME = 'datetime'; + + /** @var string[] */ public static array $types = [ self::TEXT, + self::DATE, + self::DATETIME, self::CHECKBOX, self::SELECT, + self::MULTISELECT, self::FILE, ]; @@ -92,6 +116,15 @@ abstract class CustomInput */ protected Collection $customInputValues; + /** + * Role, pro které se pole zobrazuje. + * + * @ORM\ManyToMany(targetEntity="\App\Model\Acl\Role") + * + * @var Collection|Role[] + */ + protected Collection $roles; + public function __construct() { $this->customInputValues = new ArrayCollection(); @@ -144,4 +177,27 @@ public function getCustomInputValues() : Collection { return $this->customInputValues; } + + /** + * @return Role[]|Collection + */ + public function getRoles() : Collection + { + return $this->roles; + } + + /** + * @param Role[]|Collection $roles + */ + public function setRoles(Collection $roles) : void + { + $this->roles = $roles; + } + + public function getRolesText() : string + { + return implode(', ', $this->roles->map(static function (Role $role) { + return $role->getName(); + })->toArray()); + } } diff --git a/app/Model/Settings/CustomInput/CustomInputRepository.php b/app/Model/Settings/CustomInput/CustomInputRepository.php index 43fa8783d..b8ad3a36c 100644 --- a/app/Model/Settings/CustomInput/CustomInputRepository.php +++ b/app/Model/Settings/CustomInput/CustomInputRepository.php @@ -4,6 +4,8 @@ namespace App\Model\Settings\CustomInput; +use App\Model\Acl\Role; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\NoResultException; @@ -39,6 +41,23 @@ public function findAllOrderedByPosition() : array ->getResult(); } + /** + * Vrací pole podle rolí uživatele, seřazené podle pozice. + * + * @param Collection|Role[] $roles + * + * @return CustomInput[] + */ + public function findByRolesOrderedByPosition(Collection $roles) : array + { + return $this->createQueryBuilder('i') + ->join('i.roles', 'r') + ->where('r IN (:roles)')->setParameter('roles', $roles) + ->orderBy('i.position') + ->getQuery() + ->getResult(); + } + /** * Vrátí pozici posledního pole. * diff --git a/app/Model/Settings/CustomInput/CustomMultiSelect.php b/app/Model/Settings/CustomInput/CustomMultiSelect.php new file mode 100644 index 000000000..e9c72f360 --- /dev/null +++ b/app/Model/Settings/CustomInput/CustomMultiSelect.php @@ -0,0 +1,68 @@ + + */ +class CustomMultiSelect extends CustomInput +{ + protected string $type = CustomInput::MULTISELECT; + + /** + * Možnosti výběrového pole oddělené čárkou. + * + * @ORM\Column(type="simple_array") + * + * @var string[] + */ + protected array $options = []; + + /** + * @return string[] + */ + public function getOptions() : array + { + return $this->options; + } + + /** + * @param string[] $options + */ + public function setOptions(array $options) : void + { + $this->options = $options; + } + + /** + * Vrátí možnosti jako možnosti pro select. + * + * @return string[] + */ + public function getSelectOptions() : array + { + $options = []; + + for ($i = 0; $i < count($this->options); $i++) { + $options[$i+1] = $this->options[$i]; + } + + return $this->options; + } + + public function getOptionsText() : string + { + return implode(', ', $this->options); + } +} diff --git a/app/Model/Settings/CustomInput/CustomSelect.php b/app/Model/Settings/CustomInput/CustomSelect.php index 079d64414..0e4d7d821 100644 --- a/app/Model/Settings/CustomInput/CustomSelect.php +++ b/app/Model/Settings/CustomInput/CustomSelect.php @@ -6,7 +6,7 @@ use Doctrine\ORM\Mapping as ORM; use function count; -use function explode; +use function implode; /** * Entita vlastní výběrové pole přihlášky. @@ -23,16 +23,24 @@ class CustomSelect extends CustomInput /** * Možnosti výběrového pole oddělené čárkou. * - * @ORM\Column(type="string") + * @ORM\Column(type="simple_array") + * + * @var string[] */ - protected string $options; + protected array $options = []; - public function getOptions() : string + /** + * @return string[] + */ + public function getOptions() : array { return $this->options; } - public function setOptions(string $options) : void + /** + * @param string[] $options + */ + public function setOptions(array $options) : void { $this->options = $options; } @@ -50,11 +58,31 @@ public function getSelectOptions() : array $options[0] = ''; } - $optionaArray = explode(', ', $this->options); - for ($i = 0; $i < count($optionaArray); $i++) { - $options[$i+1] = $optionaArray[$i]; + for ($i = 0; $i < count($this->options); $i++) { + $options[$i+1] = $this->options[$i]; } return $options; } + + /** + * Vrátí možnosti jako možnosti pro filter. + * + * @return string[] + */ + public function getFilterOptions() : array + { + $options = []; + + for ($i = 0; $i < count($this->options); $i++) { + $options[$i+1] = $this->options[$i]; + } + + return $options; + } + + public function getOptionsText() : string + { + return implode(', ', $this->options); + } } diff --git a/app/Model/User/CustomInputValue/CustomCheckboxValue.php b/app/Model/User/CustomInputValue/CustomCheckboxValue.php index 36f64694c..70bd9dc89 100644 --- a/app/Model/User/CustomInputValue/CustomCheckboxValue.php +++ b/app/Model/User/CustomInputValue/CustomCheckboxValue.php @@ -21,15 +21,20 @@ class CustomCheckboxValue extends CustomInputValue * * @ORM\Column(type="boolean") */ - protected ?bool $value = null; + protected bool $value = false; - public function getValue() : ?bool + public function getValue() : bool { return $this->value; } - public function setValue(?bool $value) : void + public function setValue(bool $value) : void { $this->value = $value; } + + public function getValueText() : ?string + { + return (string) $this->value; + } } diff --git a/app/Model/User/CustomInputValue/CustomDateTimeValue.php b/app/Model/User/CustomInputValue/CustomDateTimeValue.php new file mode 100644 index 000000000..79ca94d42 --- /dev/null +++ b/app/Model/User/CustomInputValue/CustomDateTimeValue.php @@ -0,0 +1,42 @@ + + */ +class CustomDateTimeValue extends CustomInputValue +{ + /** + * Hodnota pole přihlášky typu datum a čas. + * + * @ORM\Column(type="datetime_immutable", nullable=true) + */ + protected ?DateTimeImmutable $value = null; + + public function getValue() : ?DateTimeImmutable + { + return $this->value; + } + + public function setValue(?DateTimeImmutable $value) : void + { + $this->value = $value; + } + + public function getValueText() : ?string + { + return $this->value ? $this->value->format(Helpers::DATETIME_FORMAT) : null; + } +} diff --git a/app/Model/User/CustomInputValue/CustomDateValue.php b/app/Model/User/CustomInputValue/CustomDateValue.php new file mode 100644 index 000000000..0c8a109b6 --- /dev/null +++ b/app/Model/User/CustomInputValue/CustomDateValue.php @@ -0,0 +1,42 @@ + + */ +class CustomDateValue extends CustomInputValue +{ + /** + * Hodnota pole přihlášky typu datum. + * + * @ORM\Column(type="date_immutable", nullable=true) + */ + protected ?DateTimeImmutable $value = null; + + public function getValue() : ?DateTimeImmutable + { + return $this->value; + } + + public function setValue(?DateTimeImmutable $value) : void + { + $this->value = $value; + } + + public function getValueText() : ?string + { + return $this->value ? $this->value->format(Helpers::DATE_FORMAT) : null; + } +} diff --git a/app/Model/User/CustomInputValue/CustomFileValue.php b/app/Model/User/CustomInputValue/CustomFileValue.php index 0b7b11e02..b66e7c67d 100644 --- a/app/Model/User/CustomInputValue/CustomFileValue.php +++ b/app/Model/User/CustomInputValue/CustomFileValue.php @@ -32,4 +32,9 @@ public function setValue(?string $value) : void { $this->value = $value; } + + public function getValueText() : ?string + { + return null; + } } diff --git a/app/Model/User/CustomInputValue/CustomInputValue.php b/app/Model/User/CustomInputValue/CustomInputValue.php index 38c619c57..e2ee8963c 100644 --- a/app/Model/User/CustomInputValue/CustomInputValue.php +++ b/app/Model/User/CustomInputValue/CustomInputValue.php @@ -20,7 +20,10 @@ * "custom_checkbox_value" = "CustomCheckboxValue", * "custom_text_value" = "CustomTextValue", * "custom_select_value" = "CustomSelectValue", - * "custom_file_value" = "CustomFileValue" + * "custom_multiselect_value" = "CustomMultiSelectValue", + * "custom_file_value" = "CustomFileValue", + * "custom_date_value" = "CustomDateValue", + * "custom_datetime_value" = "CustomDateTimeValue" * }) * * @author Jan Staněk @@ -67,4 +70,6 @@ public function setUser(User $user) : void { $this->user = $user; } + + abstract public function getValueText() : ?string; } diff --git a/app/Model/User/CustomInputValue/CustomMultiSelectValue.php b/app/Model/User/CustomInputValue/CustomMultiSelectValue.php new file mode 100644 index 000000000..b9326bee9 --- /dev/null +++ b/app/Model/User/CustomInputValue/CustomMultiSelectValue.php @@ -0,0 +1,65 @@ + + */ +class CustomMultiSelectValue extends CustomInputValue +{ + /** + * Vybrané položky výběrového pole s více možnostmi přihlášky. + * + * @ORM\Column(type="simple_array", nullable=true) + * + * @var string[] + */ + protected array $value = []; + + /** + * @return string[] + */ + public function getValue() : array + { + return $this->value; + } + + /** + * @param string[] $value + */ + public function setValue(array $value) : void + { + $this->value = $value; + } + + /** + * Vrátí název vybrané možnosti. + */ + public function getValueText() : ?string + { + /** @var CustomMultiSelect $input */ + $input = $this->getInput(); + + if (empty($this->value)) { + return null; + } else { + $selectedValues = []; + foreach ($this->value as $value) { + $selectedValues[] = $input->getSelectOptions()[$value]; + } + + return implode(', ', $selectedValues); + } + } +} diff --git a/app/Model/User/CustomInputValue/CustomSelectValue.php b/app/Model/User/CustomInputValue/CustomSelectValue.php index 60318223c..eb5e677e3 100644 --- a/app/Model/User/CustomInputValue/CustomSelectValue.php +++ b/app/Model/User/CustomInputValue/CustomSelectValue.php @@ -6,7 +6,6 @@ use App\Model\Settings\CustomInput\CustomSelect; use Doctrine\ORM\Mapping as ORM; -use function explode; /** * Entita hodnota vlastního výběrového pole přihlášky. @@ -38,11 +37,11 @@ public function setValue(?int $value) : void /** * Vrátí název vybrané možnosti. */ - public function getValueOption() : ?string + public function getValueText() : ?string { /** @var CustomSelect $input */ $input = $this->getInput(); - return $this->value !== 0 ? explode(', ', $input->getOptions())[$this->value - 1] : null; + return $this->value !== 0 ? $input->getSelectOptions()[$this->value] : null; } } diff --git a/app/Model/User/CustomInputValue/CustomTextValue.php b/app/Model/User/CustomInputValue/CustomTextValue.php index d06ae09e7..9c52d5952 100644 --- a/app/Model/User/CustomInputValue/CustomTextValue.php +++ b/app/Model/User/CustomInputValue/CustomTextValue.php @@ -32,4 +32,9 @@ public function setValue(string $value) : void { $this->value = $value; } + + public function getValueText() : ?string + { + return $this->value; + } } diff --git a/app/Model/User/User.php b/app/Model/User/User.php index 4f5014a7c..178134df6 100644 --- a/app/Model/User/User.php +++ b/app/Model/User/User.php @@ -216,20 +216,6 @@ class User */ protected bool $attended = false; - /** - * Příjezd. - * - * @ORM\Column(type="datetime_immutable", nullable=true) - */ - protected ?DateTimeImmutable $arrival = null; - - /** - * Odjezd. - * - * @ORM\Column(type="datetime_immutable", nullable=true) - */ - protected ?DateTimeImmutable $departure = null; - /** * Role. * @@ -664,26 +650,6 @@ public function setAttended(bool $attended) : void $this->attended = $attended; } - public function getArrival() : ?DateTimeImmutable - { - return $this->arrival; - } - - public function setArrival(?DateTimeImmutable $arrival) : void - { - $this->arrival = $arrival; - } - - public function getDeparture() : ?DateTimeImmutable - { - return $this->departure; - } - - public function setDeparture(?DateTimeImmutable $departure) : void - { - $this->departure = $departure; - } - /** * @return Collection|Role[] */ @@ -720,17 +686,6 @@ public function isInRole(Role $role) : bool })->count() !== 0; } - /** - * Je uživatel v roli, u které se eviduje příjezd a odjezd? - */ - public function hasDisplayArrivalDepartureRole() : bool - { - $criteria = Criteria::create() - ->where(Criteria::expr()->eq('displayArrivalDeparture', true)); - - return ! $this->roles->matching($criteria)->isEmpty(); - } - /** * Vrací, zda má uživatel nějakou roli, která nemá cenu podle podakcí. */ diff --git a/app/Services/AclService.php b/app/Services/AclService.php index f240f4efb..a5e4ac561 100644 --- a/app/Services/AclService.php +++ b/app/Services/AclService.php @@ -244,7 +244,7 @@ public function getRolesWithoutRolesOptionsWithApprovedUsersCount(array $without */ public function getRolesOptionsWithCapacity(bool $registerableNowOnly, bool $includeUsers, ?User $user = null) : array { - $roles = $this->roleRepository->findFilteredRoles($registerableNowOnly, false, false, $includeUsers, $user); + $roles = $this->roleRepository->findFilteredRoles($registerableNowOnly, false, $includeUsers, $user); $options = []; foreach ($roles as $role) { diff --git a/app/Services/ExcelExportService.php b/app/Services/ExcelExportService.php index 9751ab1de..7a65d76d1 100644 --- a/app/Services/ExcelExportService.php +++ b/app/Services/ExcelExportService.php @@ -13,9 +13,6 @@ use App\Model\Settings\CustomInput\CustomInputRepository; use App\Model\Structure\SubeventRepository; use App\Model\User\CustomInputValue\CustomCheckboxValue; -use App\Model\User\CustomInputValue\CustomFileValue; -use App\Model\User\CustomInputValue\CustomSelectValue; -use App\Model\User\CustomInputValue\CustomTextValue; use App\Model\User\User; use App\Utils\Helpers; use Doctrine\Common\Collections\ArrayCollection; @@ -429,18 +426,12 @@ public function exportUsersList(Collection $users, string $filename) : ExcelResp foreach ($this->customInputRepository->findAllOrderedByPosition() as $customInput) { $customInputValue = $user->getCustomInputValue($customInput); - if ($customInputValue instanceof CustomTextValue) { - $value = $customInputValue->getValue(); - } elseif ($customInputValue instanceof CustomCheckboxValue) { + if ($customInputValue instanceof CustomCheckboxValue) { $value = $customInputValue->getValue() ? $this->translator->translate('common.export.common.yes') : $this->translator->translate('common.export.common.no'); - } elseif ($customInputValue instanceof CustomSelectValue) { - $value = $customInputValue->getValueOption(); - } elseif ($customInputValue instanceof CustomFileValue) { - continue; } else { - $value = ''; + $value = $customInputValue->getValueText(); } $sheet->setCellValueByColumnAndRow($column++, $row, $value); diff --git a/app/WebModule/Components/ApplicationContentControl.php b/app/WebModule/Components/ApplicationContentControl.php index cfb787299..2b8438b4f 100644 --- a/app/WebModule/Components/ApplicationContentControl.php +++ b/app/WebModule/Components/ApplicationContentControl.php @@ -7,6 +7,7 @@ use App\Model\Acl\Role; use App\Model\Acl\RoleRepository; use App\Model\Cms\Content\ContentDto; +use App\Model\Settings\CustomInput\CustomInputRepository; use App\Model\Settings\Settings; use App\Model\Settings\SettingsException; use App\Model\Structure\SubeventRepository; @@ -19,7 +20,6 @@ use Nette\Application\UI\Form; use stdClass; use Throwable; -use function json_encode; /** * Komponenta s přihláškou. @@ -43,6 +43,8 @@ class ApplicationContentControl extends Control public IApplicationsGridControlFactory $applicationsGridControlFactory; + public CustomInputRepository $customInputRepository; + public function __construct( ApplicationFormFactory $applicationFormFactory, Authenticator $authenticator, @@ -50,7 +52,8 @@ public function __construct( RoleRepository $roleRepository, SettingsService $settingsService, SubeventRepository $subeventRepository, - IApplicationsGridControlFactory $applicationsGridControlFactory + IApplicationsGridControlFactory $applicationsGridControlFactory, + CustomInputRepository $customInputRepository ) { $this->applicationFormFactory = $applicationFormFactory; $this->authenticator = $authenticator; @@ -59,6 +62,7 @@ public function __construct( $this->settingsService = $settingsService; $this->subeventRepository = $subeventRepository; $this->applicationsGridControlFactory = $applicationsGridControlFactory; + $this->customInputRepository = $customInputRepository; } /** @@ -89,7 +93,7 @@ public function render(?ContentDto $content = null) : void $template->unapprovedRole = $user->isInRole($this->roleRepository->findBySystemName(Role::UNAPPROVED)->getName()); $template->nonregisteredRole = $user->isInRole($this->roleRepository->findBySystemName(Role::NONREGISTERED)->getName()); - $template->noRegisterableRole = $this->roleRepository->findFilteredRoles(true, false, false, false)->isEmpty(); + $template->noRegisterableRole = $this->roleRepository->findFilteredRoles(true, false, false)->isEmpty(); $template->registrationStart = $this->roleRepository->getRegistrationStart(); $template->registrationEnd = $this->roleRepository->getRegistrationEnd(); $template->bankAccount = $this->settingsService->getValue(Settings::ACCOUNT_NUMBER); @@ -105,7 +109,6 @@ public function render(?ContentDto $content = null) : void } $template->explicitSubeventsExists = $explicitSubeventsExists; - $template->rolesWithSubevents = json_encode($this->roleRepository->findRolesIds($this->roleRepository->findFilteredRoles(false, true, false, false))); $template->render(); } diff --git a/app/WebModule/Components/templates/application_content.latte b/app/WebModule/Components/templates/application_content.latte index 37ade68c7..1a58cd4d7 100644 --- a/app/WebModule/Components/templates/application_content.latte +++ b/app/WebModule/Components/templates/application_content.latte @@ -46,37 +46,43 @@ {else}
diff --git a/app/WebModule/Forms/AdditionalInformationForm.php b/app/WebModule/Forms/AdditionalInformationForm.php index 147e4fda3..154ec6f74 100644 --- a/app/WebModule/Forms/AdditionalInformationForm.php +++ b/app/WebModule/Forms/AdditionalInformationForm.php @@ -7,16 +7,21 @@ use App\Model\Mailing\Template; use App\Model\Mailing\TemplateVariable; use App\Model\Settings\CustomInput\CustomCheckbox; +use App\Model\Settings\CustomInput\CustomDate; +use App\Model\Settings\CustomInput\CustomDateTime; use App\Model\Settings\CustomInput\CustomFile; -use App\Model\Settings\CustomInput\CustomInput; use App\Model\Settings\CustomInput\CustomInputRepository; +use App\Model\Settings\CustomInput\CustomMultiSelect; use App\Model\Settings\CustomInput\CustomSelect; use App\Model\Settings\CustomInput\CustomText; use App\Model\Settings\Settings; use App\Model\Settings\SettingsException; use App\Model\User\CustomInputValue\CustomCheckboxValue; +use App\Model\User\CustomInputValue\CustomDateTimeValue; +use App\Model\User\CustomInputValue\CustomDateValue; use App\Model\User\CustomInputValue\CustomFileValue; use App\Model\User\CustomInputValue\CustomInputValueRepository; +use App\Model\User\CustomInputValue\CustomMultiSelectValue; use App\Model\User\CustomInputValue\CustomSelectValue; use App\Model\User\CustomInputValue\CustomTextValue; use App\Model\User\User; @@ -25,19 +30,20 @@ use App\Services\FilesService; use App\Services\MailService; use App\Services\SettingsService; +use InvalidArgumentException; use Nette\Application\UI; use Nette\Application\UI\Form; use Nette\Http\FileUpload; use Nette\Utils\Random; use Nette\Utils\Strings; use Nettrine\ORM\EntityManagerDecorator; +use Nextras\FormComponents\Controls\DateControl; use Nextras\FormComponents\Controls\DateTimeControl; use stdClass; use Throwable; use function array_slice; use function array_values; use function explode; -use function property_exists; use const UPLOAD_ERR_OK; /** @@ -123,64 +129,107 @@ public function createComponentForm() : Form $form = $this->baseFormFactory->create(); - foreach ($this->customInputRepository->findAllOrderedByPosition() as $customInput) { + foreach ($this->customInputRepository->findByRolesOrderedByPosition($this->user->getRoles()) as $customInput) { $custom = null; - if ($customInput instanceof CustomText) { - $custom = $form->addText('custom' . $customInput->getId(), $customInput->getName()) - ->setDisabled(! $isAllowedEditCustomInputs); - /** @var ?CustomTextValue $customInputValue */ - $customInputValue = $this->user->getCustomInputValue($customInput); - if ($customInputValue) { - $custom->setDefaultValue($customInputValue->getValue()); - } - } elseif ($customInput instanceof CustomCheckbox) { - $custom = $form->addCheckbox('custom' . $customInput->getId(), $customInput->getName()) - ->setDisabled(! $isAllowedEditCustomInputs); - /** @var ?CustomCheckboxValue $customInputValue */ - $customInputValue = $this->user->getCustomInputValue($customInput); - if ($customInputValue) { - $custom->setDefaultValue($customInputValue->getValue()); - } - } elseif ($customInput instanceof CustomSelect) { - $custom = $form->addSelect('custom' . $customInput->getId(), $customInput->getName(), $customInput->getSelectOptions()) - ->setDisabled(! $isAllowedEditCustomInputs); - /** @var ?CustomSelectValue $customInputValue */ - $customInputValue = $this->user->getCustomInputValue($customInput); - if ($customInputValue) { - $custom->setDefaultValue($customInputValue->getValue()); - } - } elseif ($customInput instanceof CustomFile) { - $custom = $form->addUpload('custom' . $customInput->getId(), $customInput->getName()) - ->setDisabled(! $isAllowedEditCustomInputs); - /** @var ?CustomFileValue $customInputValue */ - $customInputValue = $this->user->getCustomInputValue($customInput); - if ($customInputValue && $customInputValue->getValue()) { - $custom->setHtmlAttribute('data-current-file-link', $customInputValue->getValue()) - ->setHtmlAttribute('data-current-file-name', array_values(array_slice(explode('/', $customInputValue->getValue()), -1))[0]); - } + switch (true) { + case $customInput instanceof CustomText: + $custom = $form->addText('custom' . $customInput->getId(), $customInput->getName()); + + /** @var ?CustomTextValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + break; + + case $customInput instanceof CustomCheckbox: + $custom = $form->addCheckbox('custom' . $customInput->getId(), $customInput->getName()); + + /** @var ?CustomCheckboxValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + break; + + case $customInput instanceof CustomSelect: + $custom = $form->addSelect('custom' . $customInput->getId(), $customInput->getName(), $customInput->getSelectOptions()); + + /** @var ?CustomSelectValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + break; + + case $customInput instanceof CustomMultiSelect: + $custom = $form->addMultiSelect('custom' . $customInput->getId(), $customInput->getName(), $customInput->getSelectOptions()); + + /** @var ?CustomMultiSelectValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + break; + + case $customInput instanceof CustomFile: + $custom = $form->addUpload('custom' . $customInput->getId(), $customInput->getName()); + + /** @var ?CustomFileValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue && $customInputValue->getValue()) { + $custom->setHtmlAttribute('data-current-file-link', $customInputValue->getValue()) + ->setHtmlAttribute('data-current-file-name', array_values(array_slice(explode('/', $customInputValue->getValue()), -1))[0]); + } + + break; + + case $customInput instanceof CustomDate: + $custom = new DateControl($customInput->getName()); + + /** @var ?CustomDateValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + $form->addComponent($custom, 'custom' . $customInput->getId()); + break; + + case $customInput instanceof CustomDateTime: + $custom = new DateTimeControl($customInput->getName()); + + /** @var ?CustomDateTimeValue $customInputValue */ + $customInputValue = $this->user->getCustomInputValue($customInput); + if ($customInputValue) { + $custom->setDefaultValue($customInputValue->getValue()); + } + + $form->addComponent($custom, 'custom' . $customInput->getId()); + break; + + default: + throw new InvalidArgumentException(); } - if ($customInput->isMandatory() && $customInput->getType() !== CustomInput::FILE) { + $custom->setDisabled(! $isAllowedEditCustomInputs); + + if ($customInput->isMandatory()) { $custom->addRule(Form::FILLED, 'web.profile.custom_input_empty'); } } $form->addTextArea('about', 'web.profile.about_me'); - if ($this->user->hasDisplayArrivalDepartureRole()) { - $arrivalDateTime = new DateTimeControl('web.profile.arrival'); - $form->addComponent($arrivalDateTime, 'arrival'); - $departureDateTime = new DateTimeControl('web.profile.departure'); - $form->addComponent($departureDateTime, 'departure'); - } - $form->addSubmit('submit', 'web.profile.update_additional_information'); $form->setDefaults([ 'about' => $this->user->getAbout(), - 'arrival' => $this->user->getArrival(), - 'departure' => $this->user->getDeparture(), ]); $form->onSuccess[] = [$this, 'processForm']; @@ -199,7 +248,7 @@ public function processForm(Form $form, stdClass $values) : void $customInputValueChanged = false; if ($this->applicationService->isAllowedEditCustomInputs()) { - foreach ($this->customInputRepository->findAllOrderedByPosition() as $customInput) { + foreach ($this->customInputRepository->findByRolesOrderedByPosition($this->user->getRoles()) as $customInput) { $customInputValue = $this->user->getCustomInputValue($customInput); $customInputName = 'custom' . $customInput->getId(); $oldValue = null; @@ -223,6 +272,12 @@ public function processForm(Form $form, stdClass $values) : void $oldValue = $customInputValue->getValue(); $newValue = $values->$customInputName; $customInputValue->setValue($newValue); + } elseif ($customInput instanceof CustomMultiSelect) { + /** @var CustomMultiSelectValue $customInputValue */ + $customInputValue = $customInputValue ?: new CustomMultiSelectValue(); + $oldValue = $customInputValue->getValue(); + $newValue = $values->$customInputName; + $customInputValue->setValue($newValue); } elseif ($customInput instanceof CustomFile) { /** @var CustomFileValue $customInputValue */ $customInputValue = $customInputValue ?: new CustomFileValue(); @@ -234,6 +289,18 @@ public function processForm(Form $form, stdClass $values) : void $this->filesService->save($newValue, $path); $customInputValue->setValue($path); } + } elseif ($customInput instanceof CustomDate) { + /** @var CustomDateValue $customInputValue */ + $customInputValue = $customInputValue ?: new CustomDateValue(); + $oldValue = $customInputValue->getValue(); + $newValue = $values->$customInputName; + $customInputValue->setValue($newValue); + } elseif ($customInput instanceof CustomDateTime) { + /** @var CustomDateTimeValue $customInputValue */ + $customInputValue = $customInputValue ?: new CustomDateTimeValue(); + $oldValue = $customInputValue->getValue(); + $newValue = $values->$customInputName; + $customInputValue->setValue($newValue); } $customInputValue->setUser($this->user); @@ -248,14 +315,6 @@ public function processForm(Form $form, stdClass $values) : void $this->user->setAbout($values->about); - if (property_exists($values, 'arrival')) { - $this->user->setArrival($values->arrival); - } - - if (property_exists($values, 'departure')) { - $this->user->setDeparture($values->departure); - } - $this->userRepository->save($this->user); if ($customInputValueChanged) { diff --git a/app/WebModule/Forms/ApplicationFormFactory.php b/app/WebModule/Forms/ApplicationFormFactory.php index 70cbcb2b1..0dc756ac2 100644 --- a/app/WebModule/Forms/ApplicationFormFactory.php +++ b/app/WebModule/Forms/ApplicationFormFactory.php @@ -8,8 +8,11 @@ use App\Model\Acl\RoleRepository; use App\Model\Enums\Sex; use App\Model\Settings\CustomInput\CustomCheckbox; +use App\Model\Settings\CustomInput\CustomDate; +use App\Model\Settings\CustomInput\CustomDateTime; use App\Model\Settings\CustomInput\CustomFile; use App\Model\Settings\CustomInput\CustomInputRepository; +use App\Model\Settings\CustomInput\CustomMultiSelect; use App\Model\Settings\CustomInput\CustomSelect; use App\Model\Settings\CustomInput\CustomText; use App\Model\Settings\Settings; @@ -17,8 +20,11 @@ use App\Model\Structure\Subevent; use App\Model\Structure\SubeventRepository; use App\Model\User\CustomInputValue\CustomCheckboxValue; +use App\Model\User\CustomInputValue\CustomDateTimeValue; +use App\Model\User\CustomInputValue\CustomDateValue; use App\Model\User\CustomInputValue\CustomFileValue; use App\Model\User\CustomInputValue\CustomInputValueRepository; +use App\Model\User\CustomInputValue\CustomMultiSelectValue; use App\Model\User\CustomInputValue\CustomSelectValue; use App\Model\User\CustomInputValue\CustomTextValue; use App\Model\User\User; @@ -29,6 +35,7 @@ use App\Services\SettingsService; use App\Services\SkautIsService; use App\Services\SubeventService; +use App\Utils\Helpers; use App\Utils\Validators; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\NonUniqueResultException; @@ -37,7 +44,6 @@ use Nette; use Nette\Application\UI\Form; use Nette\Forms\Controls\MultiSelectBox; -use Nette\Forms\IControl; use Nette\Http\FileUpload; use Nette\Localization\ITranslator; use Nette\Utils\Random; @@ -198,8 +204,6 @@ public function create(int $id) : Form $this->addSubeventsSelect($form); - $this->addArrivalDeparture($form); - $this->addCustomInputs($form); $form->addCheckbox('agreement', $this->settingsService->getValue(Settings::APPLICATION_AGREEMENT)) @@ -259,8 +263,15 @@ public function processForm(Form $form, stdClass $values) : void $this->user->setPostcode($values->postcode); $this->user->setState($values->state); + //role + if (property_exists($values, 'roles')) { + $roles = $this->roleRepository->findRolesByIds($values->roles); + } else { + $roles = $this->roleRepository->findFilteredRoles(true, false, false); + } + //vlastni pole - foreach ($this->customInputRepository->findAll() as $customInput) { + foreach ($this->customInputRepository->findByRolesOrderedByPosition($roles) as $customInput) { $customInputValue = $this->user->getCustomInputValue($customInput); $customInputName = 'custom' . $customInput->getId(); @@ -276,6 +287,10 @@ public function processForm(Form $form, stdClass $values) : void /** @var CustomSelectValue $customInputValue */ $customInputValue = $customInputValue ?: new CustomSelectValue(); $customInputValue->setValue($values->$customInputName); + } elseif ($customInput instanceof CustomMultiSelect) { + /** @var CustomMultiSelectValue $customInputValue */ + $customInputValue = $customInputValue ?: new CustomMultiSelectValue(); + $customInputValue->setValue($values->$customInputName); } elseif ($customInput instanceof CustomFile) { /** @var CustomFileValue $customInputValue */ $customInputValue = $customInputValue ?: new CustomFileValue(); @@ -286,6 +301,14 @@ public function processForm(Form $form, stdClass $values) : void $this->filesService->save($file, $path); $customInputValue->setValue($path); } + } elseif ($customInput instanceof CustomDate) { + /** @var CustomDateValue $customInputValue */ + $customInputValue = $customInputValue ?: new CustomDateValue(); + $customInputValue->setValue($values->$customInputName); + } elseif ($customInput instanceof CustomDateTime) { + /** @var CustomDateTimeValue $customInputValue */ + $customInputValue = $customInputValue ?: new CustomDateTimeValue(); + $customInputValue->setValue($values->$customInputName); } $customInputValue->setUser($this->user); @@ -293,22 +316,6 @@ public function processForm(Form $form, stdClass $values) : void $this->customInputValueRepository->save($customInputValue); } - //prijezd, odjezd - if (property_exists($values, 'arrival')) { - $this->user->setArrival($values->arrival); - } - - if (property_exists($values, 'departure')) { - $this->user->setDeparture($values->departure); - } - - //role - if (property_exists($values, 'roles')) { - $roles = $this->roleRepository->findRolesByIds($values->roles); - } else { - $roles = $this->roleRepository->findFilteredRoles(true, false, false, false); - } - //podakce $subevents = $this->subeventRepository->explicitSubeventsExists() && ! empty($values->subevents) ? $this->subeventRepository->findSubeventsByIds($values->subevents) @@ -348,29 +355,51 @@ public function processForm(Form $form, stdClass $values) : void private function addCustomInputs(Form $form) : void { foreach ($this->customInputRepository->findAllOrderedByPosition() as $customInput) { + $customInputId = 'custom' . $customInput->getId(); + $customInputName = $customInput->getName(); + switch (true) { case $customInput instanceof CustomText: - $custom = $form->addText('custom' . $customInput->getId(), $customInput->getName()); + $custom = $form->addText($customInputId, $customInputName); break; case $customInput instanceof CustomCheckbox: - $custom = $form->addCheckbox('custom' . $customInput->getId(), $customInput->getName()); + $custom = $form->addCheckbox($customInputId, $customInputName); break; case $customInput instanceof CustomSelect: - $custom = $form->addSelect('custom' . $customInput->getId(), $customInput->getName(), $customInput->getSelectOptions()); + $custom = $form->addSelect($customInputId, $customInputName, $customInput->getSelectOptions()); + break; + + case $customInput instanceof CustomMultiSelect: + $custom = $form->addMultiSelect($customInputId, $customInputName, $customInput->getSelectOptions()); break; case $customInput instanceof CustomFile: - $custom = $form->addUpload('custom' . $customInput->getId(), $customInput->getName()); + $custom = $form->addUpload($customInputId, $customInputName); + break; + + case $customInput instanceof CustomDate: + $custom = new DateControl($customInputName); + $form->addComponent($custom, $customInputId); + break; + + case $customInput instanceof CustomDateTime: + $custom = new DateTimeControl($customInputName); + $form->addComponent($custom, $customInputId); break; default: throw new InvalidArgumentException(); } + $custom->setOption('id', 'form-group-' . $customInputId); + if ($customInput->isMandatory()) { - $custom->addRule(Form::FILLED, 'web.application_content.custom_input_empty'); + /** @var MultiSelectBox $rolesSelect */ + $rolesSelect = $form['roles']; + $custom->addConditionOn($rolesSelect, self::class . '::toggleCustomInputRequired', ['id' => $customInputId, 'roles' => Helpers::getIds($customInput->getRoles())]) + ->addRule(Form::FILLED, 'web.application_content.custom_input_empty'); } } } @@ -392,16 +421,18 @@ private function addSubeventsSelect(Form $form) : void $subeventsSelect = $form->addMultiSelect('subevents', 'web.application_content.subevents')->setItems( $subeventsOptions ); + $subeventsSelect->setOption('id', 'form-group-subevents'); $subeventsSelect ->setRequired(false) ->addRule([$this, 'validateSubeventsCapacities'], 'web.application_content.subevents_capacity_occupied'); /** @var MultiSelectBox $rolesSelect */ $rolesSelect = $form['roles']; - - $subeventsSelect - ->addConditionOn($rolesSelect, [$this, 'toggleSubeventsRequired']) - ->addRule(Form::FILLED, 'web.application_content.subevents_empty'); + $subeventsSelect->addConditionOn( + $rolesSelect, + self::class . '::toggleSubeventsRequired', + Helpers::getIds($this->roleRepository->findFilteredRoles(false, true, false)) + )->addRule(Form::FILLED, 'web.application_content.subevents_empty'); //generovani chybovych hlasek pro vsechny kombinace podakci foreach ($this->subeventRepository->findFilteredSubevents(true, false, false, false) as $subevent) { @@ -447,7 +478,7 @@ private function addRolesSelect(Form $form) : void ->addRule([$this, 'validateRolesMinimumAge'], 'web.application_content.roles_require_minimum_age'); //generovani chybovych hlasek pro vsechny kombinace roli - foreach ($this->roleRepository->findFilteredRoles(true, false, false, true, $this->user) as $role) { + foreach ($this->roleRepository->findFilteredRoles(true, false, true, $this->user) as $role) { if (! $role->getIncompatibleRoles()->isEmpty()) { $rolesSelect->addRule( [$this, 'validateRolesIncompatible'], @@ -473,15 +504,12 @@ private function addRolesSelect(Form $form) : void } } - $ids = []; - foreach ($this->roleRepository->findFilteredRoles(false, false, true, false) as $role) { - $ids[] = (string) $role->getId(); + foreach ($this->customInputRepository->findAll() as $customInput) { + $customInputId = 'custom' . $customInput->getId(); + $rolesSelect->addCondition(self::class . '::toggleCustomInputVisibility', Helpers::getIds($customInput->getRoles())) + ->toggle('form-group-' . $customInputId); } - $rolesSelect->addCondition(self::class . '::toggleArrivalDeparture', $ids) - ->toggle('arrivalInput') - ->toggle('departureInput'); - //pokud je na vyber jen jedna role, je oznacena if (count($registerableOptions) === 1) { $rolesSelect->setDisabled(true); @@ -489,20 +517,6 @@ private function addRolesSelect(Form $form) : void } } - /** - * Přidá pole pro zadání příjezdu a odjezdu. - */ - private function addArrivalDeparture(Form $form) : void - { - $arrivalDateTime = new DateTimeControl('web.application_content.arrival'); - $arrivalDateTime->setOption('id', 'arrivalInput'); - $form->addComponent($arrivalDateTime, 'arrival'); - - $departureDateTime = new DateTimeControl('web.application_content.departure'); - $departureDateTime->setOption('id', 'departureInput'); - $form->addComponent($departureDateTime, 'departure'); - } - /** * Ověří kapacity podakcí. */ @@ -599,11 +613,12 @@ public function validateRolesMinimumAge(MultiSelectBox $field) : bool } /** - * Vrací, zda je výběr podakcí povinný pro kombinaci rolí. + * Přepíná povinnost podakcí podle kombinace rolí. + + * @param int[] $rolesWithSubevents */ - public function toggleSubeventsRequired(MultiSelectBox $field) : bool + public static function toggleSubeventsRequired(MultiSelectBox $field, array $rolesWithSubevents) : bool { - $rolesWithSubevents = $this->roleRepository->findRolesIds($this->roleRepository->findFilteredRoles(false, true, false, false)); foreach ($field->getValue() as $roleId) { if (in_array($roleId, $rolesWithSubevents)) { return true; @@ -614,9 +629,28 @@ public function toggleSubeventsRequired(MultiSelectBox $field) : bool } /** - * Přepne zobrazení polí pro příjezd a odjezd. + * Přepíná povinnost vlastních polí podle kombinace rolí. + * + * @param array $customInput + */ + public static function toggleCustomInputRequired(MultiSelectBox $field, array $customInput) : bool + { + foreach ($field->getValue() as $roleId) { + if (in_array($roleId, $customInput['roles'])) { + return true; + } + } + + return false; + } + + /** + * Přepíná zobrazení vlastních polí podle kombinace rolí. + * Je nutná, na výsledku nezáleží (používá se javascript funkce). + * + * @param int[] $customInputRoles */ - public static function toggleArrivalDeparture(IControl $control) : bool + public static function toggleCustomInputVisibility(MultiSelectBox $field, array $customInputRoles) : bool { return false; } diff --git a/app/WebModule/Forms/RolesFormFactory.php b/app/WebModule/Forms/RolesFormFactory.php index 2383e257c..f3bf54156 100644 --- a/app/WebModule/Forms/RolesFormFactory.php +++ b/app/WebModule/Forms/RolesFormFactory.php @@ -97,7 +97,7 @@ public function create(int $id) : Form ->addRule([$this, 'validateRolesMinimumAge'], 'web.application_content.roles_require_minimum_age') ->setDisabled(! $this->applicationService->isAllowedEditRegistration($this->user)); - foreach ($this->roleRepository->findFilteredRoles(true, false, false, true, $this->user) as $role) { + foreach ($this->roleRepository->findFilteredRoles(true, false, true, $this->user) as $role) { if (! $role->getIncompatibleRoles()->isEmpty()) { $rolesSelect->addRule( [$this, 'validateRolesIncompatible'], diff --git a/app/assets/admin/schedule/store.js b/app/assets/admin/schedule/store.js index 1b5d01e48..56772adfa 100644 --- a/app/assets/admin/schedule/store.js +++ b/app/assets/admin/schedule/store.js @@ -205,7 +205,7 @@ export default new Vuex.Store({ .then(response => { const responseObject = JSON.parse(response.data); info.event.setProp('id', responseObject.program.id); - info.event.setExtendedProp('id', responseObject.program.id); // todo: odstranit workaround po oprave bugu + info.event.setExtendedProp('id', responseObject.program.id); // todo: odstranit workaround po oprave bugu (https://github.com/fullcalendar/fullcalendar/issues/4730) commit('incrementProgramsCount', info.event.extendedProps.block.id); commit('setMessage', {type: responseObject.status, text: responseObject.message}); }).catch(error => { @@ -222,7 +222,7 @@ export default new Vuex.Store({ updateProgram({commit}, info) { commit('incrementLoading'); const data = { - id: info.event.id !== "" ? info.event.id : info.event.extendedProps.id, // todo: odstranit workaround po oprave bugu + id: info.event.id !== "" ? info.event.id : info.event.extendedProps.id, // todo: odstranit workaround po oprave bugu (https://github.com/fullcalendar/fullcalendar/issues/4730) block_id: info.event.extendedProps.block.id, room_id: info.event.getResources()[0].id !== "0" ? info.event.getResources()[0].id : null, start: info.event.start.toISOString() @@ -248,7 +248,7 @@ export default new Vuex.Store({ updateProgramRoom({commit}, info) { commit('incrementLoading'); const data = { - id: info.event.id !== "" ? info.event.id : info.event.extendedProps.id, // todo: odstranit workaround po oprave bugu + id: info.event.id !== "" ? info.event.id : info.event.extendedProps.id, // todo: odstranit workaround po oprave bugu (https://github.com/fullcalendar/fullcalendar/issues/4730) block_id: info.event.extendedProps.block.id, room_id: info.resourceId !== "0" ? info.resourceId : null, start: info.event.start.toISOString() @@ -271,7 +271,7 @@ export default new Vuex.Store({ */ removeProgram({commit, state}, info) { commit('incrementLoading'); - Vue.axios.delete('remove-program/' + (info.event.id !== "" ? info.event.id : info.event.extendedProps.id)) // todo: odstranit workaround po oprave bugu + Vue.axios.delete('remove-program/' + (info.event.id !== "" ? info.event.id : info.event.extendedProps.id)) // todo: odstranit workaround po oprave bugu (https://github.com/fullcalendar/fullcalendar/issues/4730) .then(response => { const responseObject = JSON.parse(response.data); info.event.remove(); diff --git a/app/lang/admin.cs_CZ.neon b/app/lang/admin.cs_CZ.neon index 11e2a191b..8d1b7422c 100644 --- a/app/lang/admin.cs_CZ.neon +++ b/app/lang/admin.cs_CZ.neon @@ -22,7 +22,10 @@ common: custom_text: "Textové pole" custom_checkbox: "Zaškrtávací pole" custom_select: "Výběrové pole" + custom_multiselect: "Výběrové pole s více možnostmi" custom_file: "Soubor" + custom_date: "Datum" + custom_datetime: "Datum a čas" access_denied: "Pro přístup k této stránce nemáte oprávnění." @@ -312,8 +315,6 @@ users: users_attended_yes: "Přítomen" users_attended_form: "přítomen" users_attended_detail: "Přítomen" - users_arrival: "Příjezd" - users_departure: "Odjezd" users_private_note: "Neveřejná poznámka" users_not_registered_mandatory_blocks: "Nepřihlášené povinné bloky" users_from: "Od" @@ -438,7 +439,6 @@ acl: roles_fee_from_subevents: "Podle podakcí" roles_approved_after_registration: "nevyžadovat schválení registrace organizátorem" roles_synced_with_skaut_is: "uvést uživatele v této roli jako účastníky akce ve skautIS" - roles_display_arrival_departure: "evidovat příjezd a odjezd" roles_permissions: "Oprávnění" roles_pages: "Viditelné stránky" roles_redirect_after_login: "Po přihlášení přesměrovat na" @@ -652,6 +652,9 @@ configuration: custom_inputs_heading: "Vlastní pole" custom_inputs_name: "Název" custom_inputs_name_empty: "Zadejte název pole." + custom_inputs_roles: "Pro role" + custom_inputs_roles_empty: "Vyberte alespoň jednu roli." + custom_inputs_roles_all: "Všechny role" custom_inputs_type: "Typ" custom_inputs_mandatory: "Povinnost" custom_inputs_mandatory_mandatory: "Povinné" diff --git a/app/lang/web.cs_CZ.neon b/app/lang/web.cs_CZ.neon index 63b54b31a..f6b936ea3 100644 --- a/app/lang/web.cs_CZ.neon +++ b/app/lang/web.cs_CZ.neon @@ -99,8 +99,6 @@ profile: additional_information_group: "Doplňující informace" custom_input_empty: "Pole je povinné." about_me: "O mně" - arrival: "Příjezd" - departure: "Odjezd" update_additional_information: "Upravit doplňující informace" additional_information_update_successfull: "Doplňující informace úspěšně upraveny." additional_information_edit_not_allowed: "Úprava údajů z přihlášky již není povolena." @@ -166,8 +164,6 @@ application_content: roles_not_registerable: "Registrace do některé z rolí již není možná." roles_capacity_occupied: "Všechna místa v některé roli jsou obsazena." roles_require_minimum_age: "Některá role vyžaduje vyšší věk." - arrival: "Příjezd" - departure: "Odjezd" agreement_empty: "Musíte souhlasit s poskytnutím údajů." register: "Registrovat" register_synchronization_failed: "Synchronizace se skautIS se nepodařila. Zkuste se znovu přihlásit a upravit údaje v profilu." diff --git a/migrations/Version20200523200237.php b/migrations/Version20200523200237.php new file mode 100644 index 000000000..735664f45 --- /dev/null +++ b/migrations/Version20200523200237.php @@ -0,0 +1,47 @@ +abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('CREATE TABLE custom_input_role (custom_input_id INT NOT NULL, role_id INT NOT NULL, INDEX IDX_2BFB0DF7D390FDB2 (custom_input_id), INDEX IDX_2BFB0DF7D60322AC (role_id), PRIMARY KEY(custom_input_id, role_id)) DEFAULT CHARACTER SET UTF8 COLLATE `UTF8_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE custom_date (id INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET UTF8 COLLATE `UTF8_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE custom_datetime (id INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET UTF8 COLLATE `UTF8_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE custom_multiselect (id INT NOT NULL, options LONGTEXT NOT NULL COMMENT \'(DC2Type:simple_array)\', PRIMARY KEY(id)) DEFAULT CHARACTER SET UTF8 COLLATE `UTF8_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE custom_datetime_value (id INT NOT NULL, value DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', PRIMARY KEY(id)) DEFAULT CHARACTER SET UTF8 COLLATE `UTF8_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE custom_date_value (id INT NOT NULL, value DATE DEFAULT NULL COMMENT \'(DC2Type:date_immutable)\', PRIMARY KEY(id)) DEFAULT CHARACTER SET UTF8 COLLATE `UTF8_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE custom_multiselect_value (id INT NOT NULL, value LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:simple_array)\', PRIMARY KEY(id)) DEFAULT CHARACTER SET UTF8 COLLATE `UTF8_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE custom_input_role ADD CONSTRAINT FK_2BFB0DF7D390FDB2 FOREIGN KEY (custom_input_id) REFERENCES custom_input (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE custom_input_role ADD CONSTRAINT FK_2BFB0DF7D60322AC FOREIGN KEY (role_id) REFERENCES role (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE custom_date ADD CONSTRAINT FK_ABE8FF87BF396750 FOREIGN KEY (id) REFERENCES custom_input (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE custom_datetime ADD CONSTRAINT FK_762121AEBF396750 FOREIGN KEY (id) REFERENCES custom_input (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE custom_multiselect ADD CONSTRAINT FK_A0C83E4ABF396750 FOREIGN KEY (id) REFERENCES custom_input (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE custom_datetime_value ADD CONSTRAINT FK_A1E3BBF396750 FOREIGN KEY (id) REFERENCES custom_input_value (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE custom_date_value ADD CONSTRAINT FK_98788C12BF396750 FOREIGN KEY (id) REFERENCES custom_input_value (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE custom_multiselect_value ADD CONSTRAINT FK_4D1C00CCBF396750 FOREIGN KEY (id) REFERENCES custom_input_value (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE role DROP display_arrival_departure'); + $this->addSql('ALTER TABLE custom_select CHANGE options options LONGTEXT NOT NULL COMMENT \'(DC2Type:simple_array)\''); + $this->addSql('ALTER TABLE user DROP arrival, DROP departure'); + } + + public function down(Schema $schema) : void + { + } +} diff --git a/www/css/admin/style.css b/www/css/admin/style.css index fd7e9e391..ef40f8840 100644 --- a/www/css/admin/style.css +++ b/www/css/admin/style.css @@ -42,6 +42,12 @@ body { border-top-right-radius: 0; } +/* povinna pole */ +.col-form-label .required::after { + content: " *"; + color: red; +} + /* mala tlacitka */ .btn-xs { padding: 1px 5px; diff --git a/www/css/web/layout.css b/www/css/web/layout.css index 7681044da..652262f63 100644 --- a/www/css/web/layout.css +++ b/www/css/web/layout.css @@ -82,8 +82,8 @@ p { line-height: 2.5em; } -/* application content */ -.application-content .control-label .required::after { +/* povinna pole */ +.col-form-label .required::after { content: " *"; color: red; }