diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php index abb4e231..5540e725 100644 --- a/src/Controller/ProfileController.php +++ b/src/Controller/ProfileController.php @@ -261,10 +261,20 @@ class ProfileController extends AbstractController ]); } + $sections = []; + + /** @var UserPreference $pref */ + foreach ($profile->getPreferences() as $pref) { + if ($pref->isEnabled()) { + $sections[$pref->getSection()] = $pref->getSection(); + } + } + return $this->render('user/form.html.twig', [ 'tab' => 'preferences', 'user' => $profile, 'form' => $form->createView(), + 'sections' => $sections ]); } diff --git a/src/Entity/UserPreference.php b/src/Entity/UserPreference.php index 97bd767a..84900582 100644 --- a/src/Entity/UserPreference.php +++ b/src/Entity/UserPreference.php @@ -83,11 +83,12 @@ class UserPreference * @var int */ private $order = 1000; - /** - * @return int + * @var string */ - public function getId() + private $section = 'default'; + + public function getId(): ?int { return $this->id; } @@ -264,4 +265,16 @@ class UserPreference return $this; } + + public function setSection(string $section): UserPreference + { + $this->section = $section; + + return $this; + } + + public function getSection(): string + { + return $this->section; + } } diff --git a/src/EventSubscriber/UserPreferenceSubscriber.php b/src/EventSubscriber/UserPreferenceSubscriber.php index 3198ccbd..89c20ab1 100644 --- a/src/EventSubscriber/UserPreferenceSubscriber.php +++ b/src/EventSubscriber/UserPreferenceSubscriber.php @@ -106,6 +106,7 @@ class UserPreferenceSubscriber implements EventSubscriberInterface ->setName(UserPreference::HOURLY_RATE) ->setValue(0) ->setOrder(100) + ->setSection('rate') ->setType(MoneyType::class) ->setEnabled($enableHourlyRate) ->setOptions($hourlyRateOptions) @@ -115,6 +116,7 @@ class UserPreferenceSubscriber implements EventSubscriberInterface ->setName(UserPreference::INTERNAL_RATE) ->setValue(null) ->setOrder(101) + ->setSection('rate') ->setType(MoneyType::class) ->setEnabled($enableHourlyRate) ->setOptions(array_merge($hourlyRateOptions, ['label' => 'label.rate_internal', 'required' => false])) @@ -124,54 +126,63 @@ class UserPreferenceSubscriber implements EventSubscriberInterface ->setName(UserPreference::TIMEZONE) ->setValue($this->getDefaultTimezone()) ->setOrder(200) + ->setSection('locale') ->setType(TimezoneType::class), (new UserPreference()) ->setName(UserPreference::LOCALE) ->setValue($this->getDefaultLanguage()) ->setOrder(300) + ->setSection('locale') ->setType(LanguageType::class), (new UserPreference()) ->setName(UserPreference::SKIN) ->setValue($this->getDefaultTheme()) ->setOrder(400) + ->setSection('theme') ->setType(SkinType::class), (new UserPreference()) ->setName('theme.layout') ->setValue('fixed') ->setOrder(450) + ->setSection('theme') ->setType(ThemeLayoutType::class), (new UserPreference()) ->setName('theme.collapsed_sidebar') ->setValue(false) ->setOrder(500) + ->setSection('theme') ->setType(CheckboxType::class), (new UserPreference()) ->setName('calendar.initial_view') ->setValue(CalendarViewType::DEFAULT_VIEW) ->setOrder(600) + ->setSection('behaviour') ->setType(CalendarViewType::class), (new UserPreference()) ->setName('login.initial_view') ->setValue(InitialViewType::DEFAULT_VIEW) ->setOrder(700) + ->setSection('behaviour') ->setType(InitialViewType::class), (new UserPreference()) ->setName('timesheet.daily_stats') ->setValue(false) ->setOrder(800) + ->setSection('behaviour') ->setType(CheckboxType::class), (new UserPreference()) ->setName('timesheet.export_decimal') ->setValue(false) ->setOrder(900) + ->setSection('behaviour') ->setType(CheckboxType::class), ]; } @@ -195,6 +206,7 @@ class UserPreferenceSubscriber implements EventSubscriberInterface ->setEnabled($preference->isEnabled()) ->setOptions($preference->getOptions()) ->setOrder($preference->getOrder()) + ->setSection($preference->getSection()) ; } else { $user->addPreference($preference); diff --git a/src/Form/ProjectEditForm.php b/src/Form/ProjectEditForm.php index 9410d847..b89fa9d0 100644 --- a/src/Form/ProjectEditForm.php +++ b/src/Form/ProjectEditForm.php @@ -78,6 +78,7 @@ class ProjectEditForm extends AbstractType 'required' => false, ])) ->add('customer', CustomerType::class, [ + 'placeholder' => (null === $id && null === $customer) ? '' : false, 'query_builder' => function (CustomerRepository $repo) use ($builder, $customer) { $query = new CustomerFormTypeQuery($customer); $query->setUser($builder->getOption('user')); diff --git a/templates/activity/details.html.twig b/templates/activity/details.html.twig index 1db3d693..8889f748 100644 --- a/templates/activity/details.html.twig +++ b/templates/activity/details.html.twig @@ -32,11 +32,11 @@ {{ activity.comment|comment2html(true) }} {% endif %} - +
{% if not activity.visible %} - @@ -46,10 +46,11 @@ + + @@ -57,10 +58,11 @@ + + @@ -68,7 +70,7 @@ {% for metaField in activity.visibleMetaFields %} - + {% endfor %}
{{ 'label.visible'|trans }} + {{ widgets.label_boolean(activity.visible) }}
{{ 'label.customer'|trans }} {{ widgets.label_customer(activity.project.customer) }} - {% if activity.project.customer.teams|length == 0 %} - {{ widgets.icon('unlocked') }} - {% endif %} -   + + {{ widgets.badge_team_access(activity.project.customer.teams) }} + {{ customerActions.customer(activity.project.customer, 'custom') }}
{{ 'label.project'|trans }} {{ widgets.label_project(activity.project) }} - {% if activity.project.teams|length == 0 %} - {{ widgets.icon('unlocked') }} - {% endif %} -   + + {{ widgets.badge_team_access(activity.project.teams) }} + {{ projectActions.project(activity.project, 'custom') }}
{{ metaField.label|trans }}{{ widgets.form_type_value(metaField.type, metaField.value, activity) }}{{ widgets.form_type_value(metaField.type, metaField.value, activity) }}
diff --git a/templates/activity/edit.html.twig b/templates/activity/edit.html.twig index 89f1b197..8ac42476 100644 --- a/templates/activity/edit.html.twig +++ b/templates/activity/edit.html.twig @@ -7,7 +7,7 @@ {% block main %} {% set formEditTemplate = app.request.xmlHttpRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %} {% set formOptions = { - 'title': activity.name|default('create'|trans), + 'title': activity.name|default('create-activity'|trans({}, 'actions')), 'form': form, 'back': path('admin_activity') } %} diff --git a/templates/customer/actions.html.twig b/templates/customer/actions.html.twig index edba6500..dd3018f6 100644 --- a/templates/customer/actions.html.twig +++ b/templates/customer/actions.html.twig @@ -48,7 +48,7 @@ {% set actions = actions|merge({'timesheet': path('admin_timesheet', {'customers[]': customer.id})}) %} {% endif %} {% if customer.visible and is_granted('create_project') %} - {% set actions = actions|merge({'create-project': path('admin_project_create_with_customer', {'customer': customer.id})}) %} + {% set actions = actions|merge({'create-project': {'url': path('admin_project_create_with_customer', {'customer': customer.id}), 'class': 'modal-ajax-form'}}) %} {% endif %} {% if view == 'index' and is_granted('delete', customer) %} {% set actions = actions|merge({'trash': {'url': path('admin_customer_delete', {'id': customer.id}), 'class': 'modal-ajax-form'}}) %} diff --git a/templates/customer/details.html.twig b/templates/customer/details.html.twig index f701ba1c..f7b6fce9 100644 --- a/templates/customer/details.html.twig +++ b/templates/customer/details.html.twig @@ -27,7 +27,7 @@ {{ customer.comment|comment2html(true) }} {% endif %} - +
{% if not customer.visible %} diff --git a/templates/embeds/comments.html.twig b/templates/embeds/comments.html.twig index 9819df29..4d8a030e 100644 --- a/templates/embeds/comments.html.twig +++ b/templates/embeds/comments.html.twig @@ -13,7 +13,7 @@ {% endif %} {% for comment in comments %}
- {{ widgets.user_avatar(comment.createdBy, false, 'img-sm') }} + {{ widgets.user_avatar(comment.createdBy, comment.createdAt|date_full, 'img-sm') }}
{{ widgets.username(comment.createdBy) }} @@ -26,7 +26,6 @@ {% if route_delete is not null %} {% endif %} - {{ comment.message|replace(replacer)|md2html }} diff --git a/templates/project/actions.html.twig b/templates/project/actions.html.twig index 2af8513a..7d552a2d 100644 --- a/templates/project/actions.html.twig +++ b/templates/project/actions.html.twig @@ -46,7 +46,7 @@ {% set actions = actions|merge({'timesheet': path('admin_timesheet', {'customers[]': project.customer.id, 'projects[]': project.id})}) %} {% endif %} {% if project.visible and project.customer.visible and is_granted('create_activity') %} - {% set actions = actions|merge({'create-activity': path('admin_activity_create_with_project', {'project': project.id})}) %} + {% set actions = actions|merge({'create-activity': {'url': path('admin_activity_create_with_project', {'project': project.id}), 'class': 'modal-ajax-form'}}) %} {% endif %} {% if (view == 'index' or view == 'custom') and is_granted('delete', project) %} {% set actions = actions|merge({'trash': {'url': path('admin_project_delete', {'id': project.id}), 'class': 'modal-ajax-form'}}) %} diff --git a/templates/project/details.html.twig b/templates/project/details.html.twig index 4dafc71b..126fd819 100644 --- a/templates/project/details.html.twig +++ b/templates/project/details.html.twig @@ -27,11 +27,11 @@ {{ project.comment|comment2html(true) }}
{% endif %} -
{{ 'label.visible'|trans }}
+
{% if not project.visible %} - @@ -40,23 +40,24 @@ + + {% if is_granted('details', project) %} - - - - - + {% endfor %}
{{ 'label.visible'|trans }} + {{ widgets.label_boolean(project.visible) }}
{{ 'label.customer'|trans }} {{ widgets.label_customer(project.customer) }} - {% if project.customer.teams|length == 0 %} - {{ widgets.icon('unlocked') }} - {% endif %} -   + + {{ widgets.badge_team_access(project.customer.teams) }} + {{ customerActions.customer(project.customer, 'custom') }}
{{ 'label.orderNumber'|trans }} + {{ project.orderNumber }}
{{ 'label.orderDate'|trans }} + {% if project.orderDate is not empty %} {{ project.orderDate|date_full }} {% else %} @@ -66,7 +67,7 @@
{{ 'label.project_start'|trans }} + {% if project.start is not empty %} {{ project.start|date_full }} {% else %} @@ -76,7 +77,7 @@
{{ 'label.project_end'|trans }} + {% if project.end is not empty %} {{ project.end|date_full }} {% else %} @@ -88,7 +89,7 @@ {% for metaField in project.visibleMetaFields %}
{{ metaField.label|trans }}{{ widgets.form_type_value(metaField.type, metaField.value, project) }}{{ widgets.form_type_value(metaField.type, metaField.value, project) }}
diff --git a/templates/project/edit.html.twig b/templates/project/edit.html.twig index 78b0ed9a..0a74ff00 100644 --- a/templates/project/edit.html.twig +++ b/templates/project/edit.html.twig @@ -7,7 +7,7 @@ {% block main %} {% set formEditTemplate = app.request.xmlHttpRequest ? 'default/_form_modal.html.twig' : 'default/_form.html.twig' %} {% set formOptions = { - 'title': project.name|default('create'|trans), + 'title': project.name|default('create-project'|trans({}, 'actions')), 'form': form, 'back': path('admin_project') } %} diff --git a/templates/user/form.html.twig b/templates/user/form.html.twig index 64ec9e3a..3a75585c 100644 --- a/templates/user/form.html.twig +++ b/templates/user/form.html.twig @@ -1,15 +1,20 @@ {% extends 'user/layout.html.twig' %} {% block main %} - - {% embed '@AdminLTE/Widgets/box-widget.html.twig' %} - {% block box_title %}{{ ('profile.' ~ tab)|trans }}{% endblock %} - {% block box_body %} - {{ form_start(form) }} - {{ form_widget(form) }} - - {{ form_end(form) }} - {% endblock %} - {% endembed %} + {{ form_start(form) }} + {% for section, counter in sections %} + {% embed '@AdminLTE/Widgets/box-widget.html.twig' %} + {% block box_body %} + {% for pref in form.children.preferences %} + {% if pref.vars.data.section == section %} + {{ form_row(pref) }} + {% endif %} + {% endfor %} + {% endblock %} + {% endembed %} + {% endfor %} + {{ form_widget(form) }} + + {{ form_end(form) }} {% endblock %} diff --git a/tests/Controller/ProjectControllerTest.php b/tests/Controller/ProjectControllerTest.php index 7bcb65ed..52ce209c 100644 --- a/tests/Controller/ProjectControllerTest.php +++ b/tests/Controller/ProjectControllerTest.php @@ -299,6 +299,7 @@ class ProjectControllerTest extends ControllerBaseTest $client->submit($form, [ 'project_edit_form' => [ 'name' => 'Test 2', + 'customer' => 1, ] ]); $this->assertIsRedirect($client, $this->createUrl('/admin/project/2/details')); diff --git a/tests/Entity/UserPreferenceTest.php b/tests/Entity/UserPreferenceTest.php new file mode 100644 index 00000000..141015ea --- /dev/null +++ b/tests/Entity/UserPreferenceTest.php @@ -0,0 +1,63 @@ +isEnabled()); + self::assertEquals('default', $sut->getSection()); + self::assertEquals(1000, $sut->getOrder()); + self::assertNull($sut->getValue()); + self::assertIsArray($sut->getConstraints()); + self::assertEmpty($sut->getConstraints()); + self::assertNull($sut->getId()); + self::assertNull($sut->getLabel()); + self::assertNull($sut->getName()); + self::assertIsArray($sut->getOptions()); + self::assertEmpty($sut->getOptions()); + self::assertNull($sut->getType()); + self::assertNull($sut->getUser()); + } + + public function testGetValueChangesReturnTypeOnOtherType() + { + $sut = new UserPreference(); + $sut->setValue('1'); + self::assertSame('1', $sut->getValue()); + $sut->setType(IntegerType::class); + self::assertSame(1, $sut->getValue()); + $sut->setType(YesNoType::class); + self::assertSame(true, $sut->getValue()); + $sut->setValue('0'); + $sut->setType(CheckboxType::class); + self::assertSame(false, $sut->getValue()); + } + + public function testGetLabelWithLabelOption() + { + $sut = new UserPreference(); + $sut->setName('foo'); + self::assertEquals('foo', $sut->getLabel()); + $sut->setOptions(['label' => 'bar']); + self::assertEquals('bar', $sut->getLabel()); + } +}