Skip to content

Commit a67c699

Browse files
authored
Final rotation config improvements (#365)
Changes according to: #274 (comment) Disabling the time period inputs is not possible, though. resolves #274
2 parents 2ef0bbe + 091920f commit a67c699

File tree

2 files changed

+129
-112
lines changed

2 files changed

+129
-112
lines changed

application/forms/RotationConfigForm.php

Lines changed: 105 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Icinga\Web\Session;
1919
use ipl\Html\Attributes;
2020
use ipl\Html\DeferredText;
21+
use ipl\Html\FormDecoration\DescriptionDecorator;
2122
use ipl\Html\FormElement\FieldsetElement;
2223
use ipl\Html\HtmlDocument;
2324
use ipl\Html\HtmlElement;
@@ -30,6 +31,7 @@
3031
use ipl\Validator\GreaterThanValidator;
3132
use ipl\Web\Common\CsrfCounterMeasure;
3233
use ipl\Web\Compat\CompatForm;
34+
use ipl\Web\FormDecorator\IcingaFormDecorator;
3335
use ipl\Web\FormElement\TermInput;
3436
use ipl\Web\Url;
3537
use LogicException;
@@ -190,6 +192,8 @@ public function __construct(int $scheduleId, Connection $db)
190192
{
191193
$this->db = $db;
192194
$this->scheduleId = $scheduleId;
195+
196+
$this->applyDefaultElementDecorators();
193197
}
194198

195199
/**
@@ -599,7 +603,9 @@ protected function assembleModeSelection(): string
599603
'24-7' => $this->translate('24/7')
600604
];
601605

602-
$modeList = new HtmlElement('ul');
606+
$modeList = new HtmlElement('ul', Attributes::create([
607+
'class' => ['rotation-mode', $this->disableModeSelection ? 'disabled' : '']
608+
]));
603609
foreach ($modes as $mode => $label) {
604610
$radio = $this->createElement('input', 'mode', [
605611
'type' => 'radio',
@@ -681,8 +687,14 @@ protected function assembleModeSelection(): string
681687

682688
$this->addHtml(new HtmlElement(
683689
'div',
684-
Attributes::create(['class' => ['rotation-mode', $this->disableModeSelection ? 'disabled' : '']]),
685-
new HtmlElement('h2', null, Text::create($this->translate('Mode'))),
690+
Attributes::create([
691+
'class' => ['control-group']
692+
]),
693+
new HtmlElement(
694+
'div',
695+
Attributes::create(['class' => 'control-label-group']),
696+
Text::create($this->translate('Rotation Mode'))
697+
),
686698
$modeList
687699
));
688700

@@ -701,12 +713,15 @@ protected function assembleTwentyFourSevenOptions(FieldsetElement $options): Dat
701713
$options->addElement('number', 'interval', [
702714
'required' => true,
703715
'label' => $this->translate('Handoff every'),
716+
'description' => $this->translate('Have multiple rotation members take turns after this interval.'),
704717
'step' => 1,
705718
'min' => 1,
706719
'value' => 1,
707720
'validators' => [new GreaterThanValidator()]
708721
]);
709722
$interval = $options->getElement('interval');
723+
$interval->getDecorators()
724+
->replaceDecorator('Description', DescriptionDecorator::class, ['class' => 'description']);
710725

711726
$frequency = $options->createElement('select', 'frequency', [
712727
'required' => true,
@@ -793,11 +808,15 @@ protected function assemblePartialDayOptions(FieldsetElement $options): DateTime
793808
$options->addElement('number', 'interval', [
794809
'required' => true,
795810
'label' => $this->translate('Handoff every'),
811+
'description' => $this->translate('Have multiple rotation members take turns after this interval.'),
796812
'step' => 1,
797813
'min' => 1,
798814
'value' => 1,
799815
'validators' => [new GreaterThanValidator()]
800816
]);
817+
$interval = $options->getElement('interval');
818+
$interval->getDecorators()
819+
->replaceDecorator('Description', DescriptionDecorator::class, ['class' => 'description']);
801820

802821
$selectedFromTime = $from->getValue();
803822
foreach ($timeOptions as $key => $value) {
@@ -827,7 +846,6 @@ protected function assemblePartialDayOptions(FieldsetElement $options): DateTime
827846
)
828847
);
829848

830-
$interval = $options->getElement('interval');
831849
$interval->prependWrapper(
832850
(new HtmlDocument())->addHtml(
833851
$interval,
@@ -909,8 +927,12 @@ protected function assembleMultiDayOptions(FieldsetElement $options): DateTime
909927
'step' => 1,
910928
'min' => 1,
911929
'value' => 1,
912-
'label' => $this->translate('Handoff every')
930+
'label' => $this->translate('Handoff every'),
931+
'description' => $this->translate('Have multiple rotation members take turns after this interval.')
913932
]);
933+
$interval = $options->getElement('interval');
934+
$interval->getDecorators()
935+
->replaceDecorator('Description', DescriptionDecorator::class, ['class' => 'description']);
914936

915937
$timeOptions = $this->getTimeOptions();
916938
$fromAt = $options->createElement('select', 'from_at', [
@@ -985,7 +1007,6 @@ protected function assembleMultiDayOptions(FieldsetElement $options): DateTime
9851007
)
9861008
);
9871009

988-
$interval = $options->getElement('interval');
9891010
$interval->prependWrapper(
9901011
(new HtmlDocument())->addHtml(
9911012
$interval,
@@ -1026,17 +1047,9 @@ protected function assemble()
10261047

10271048
$this->addElement('hidden', 'priority', ['ignore' => true]);
10281049

1029-
$mode = $this->assembleModeSelection();
1030-
1031-
$autoSubmittedBy = $this->getRequest()->getHeader('X-Icinga-Autosubmittedby')[0] ?? '';
1032-
if ($autoSubmittedBy === 'mode') {
1033-
$this->clearPopulatedValue('options');
1034-
$this->clearPopulatedValue('first_handoff');
1035-
}
1036-
10371050
$this->addElement('text', 'name', [
10381051
'required' => true,
1039-
'label' => $this->translate('Title'),
1052+
'label' => $this->translate('Rotation Name'),
10401053
'validators' => [
10411054
new CallbackValidator(function ($value, $validator) {
10421055
$rotations = Rotation::on($this->db)
@@ -1048,7 +1061,7 @@ protected function assemble()
10481061
}
10491062

10501063
if ($rotations->first() !== null) {
1051-
$validator->addMessage($this->translate('A rotation with this title already exists'));
1064+
$validator->addMessage($this->translate('A rotation with this name already exists'));
10521065

10531066
return false;
10541067
}
@@ -1058,8 +1071,76 @@ protected function assemble()
10581071
]
10591072
]);
10601073

1061-
$options = new FieldsetElement('options');
1062-
$this->addElement($options);
1074+
$termValidator = function (array $terms) {
1075+
$contactTerms = [];
1076+
$groupTerms = [];
1077+
foreach ($terms as $term) {
1078+
/** @var TermInput\Term $term */
1079+
if (strpos($term->getSearchValue(), ':') === false) {
1080+
// TODO: Auto-correct this to a valid type:id pair, if possible
1081+
$term->setMessage($this->translate('Is not a contact nor a group of contacts'));
1082+
continue;
1083+
}
1084+
1085+
list($type, $id) = explode(':', $term->getSearchValue(), 2);
1086+
if ($type === 'contact') {
1087+
$contactTerms[$id] = $term;
1088+
} elseif ($type === 'group') {
1089+
$groupTerms[$id] = $term;
1090+
}
1091+
}
1092+
1093+
if (! empty($contactTerms)) {
1094+
$contacts = (Contact::on(Database::get()))
1095+
->filter(Filter::equal('id', array_keys($contactTerms)));
1096+
foreach ($contacts as $contact) {
1097+
$contactTerms[$contact->id]
1098+
->setLabel($contact->full_name)
1099+
->setClass('contact');
1100+
}
1101+
}
1102+
1103+
if (! empty($groupTerms)) {
1104+
$groups = (Contactgroup::on(Database::get()))
1105+
->filter(Filter::equal('id', array_keys($groupTerms)));
1106+
foreach ($groups as $group) {
1107+
$groupTerms[$group->id]
1108+
->setLabel($group->name)
1109+
->setClass('group');
1110+
}
1111+
}
1112+
};
1113+
1114+
$members = (new TermInput('members'))
1115+
->setIgnored()
1116+
->setRequired()
1117+
->setOrdered()
1118+
->setReadOnly()
1119+
->setVerticalTermDirection()
1120+
->setLabel($this->translate('Rotation Members'))
1121+
->setSuggestionUrl($this->suggestionUrl->with(['showCompact' => true, '_disableLayout' => 1]))
1122+
->on(TermInput::ON_ENRICH, $termValidator)
1123+
->on(TermInput::ON_ADD, $termValidator)
1124+
->on(TermInput::ON_SAVE, $termValidator)
1125+
->on(TermInput::ON_PASTE, $termValidator);
1126+
$this->addElement($members);
1127+
1128+
// TODO: TermInput is not compatible with the new decorators yet: https://github.com/Icinga/ipl-web/pull/317
1129+
$legacyDecorator = new IcingaFormDecorator();
1130+
$members->setDefaultElementDecorator($legacyDecorator);
1131+
$legacyDecorator->decorate($members);
1132+
1133+
$mode = $this->assembleModeSelection();
1134+
1135+
$autoSubmittedBy = $this->getRequest()->getHeader('X-Icinga-Autosubmittedby')[0] ?? '';
1136+
if ($autoSubmittedBy === 'mode') {
1137+
$this->clearPopulatedValue('options');
1138+
$this->clearPopulatedValue('first_handoff');
1139+
}
1140+
1141+
$this->addElement('fieldset', 'options');
1142+
/** @var FieldsetElement $options */
1143+
$options = $this->getElement('options');
10631144

10641145
if ($mode === '24-7') {
10651146
$firstHandoff = $this->assembleTwentyFourSevenOptions($options);
@@ -1094,7 +1175,7 @@ protected function assemble()
10941175
'aria-describedby' => 'first-handoff-description',
10951176
'min' => $earliestHandoff !== null ? $earliestHandoff->format('Y-m-d') : null,
10961177
'max' => $latestHandoff->format('Y-m-d'),
1097-
'label' => $this->translate('First Handoff'),
1178+
'label' => $this->translate('Rotation Start'),
10981179
'value' => $firstHandoffDefault,
10991180
'validators' => [
11001181
new CallbackValidator(
@@ -1106,14 +1187,14 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
11061187
);
11071188
if ($earliestHandoff !== null && $chosenHandoff < $earliestHandoff) {
11081189
$validator->addMessage(sprintf(
1109-
$this->translate('The first handoff can only happen after %s'),
1190+
$this->translate('The rotation can only start after %s'),
11101191
$earliestHandoff->format('Y-m-d') // TODO: Use intl here
11111192
));
11121193

11131194
return false;
11141195
} elseif ($chosenHandoff > $latestHandoff) {
11151196
$validator->addMessage(sprintf(
1116-
$this->translate('The first handoff can only happen before %s'),
1197+
$this->translate('The rotation can only start before %s'),
11171198
$latestHandoff->format('Y-m-d') // TODO: Use intl here
11181199
));
11191200

@@ -1138,10 +1219,10 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
11381219

11391220
$actualFirstHandoff = $ruleGenerator->current()[0]->getStartDate();
11401221
if ($actualFirstHandoff < new DateTime()) {
1141-
return $this->translate('The first handoff will happen immediately');
1222+
return $this->translate('The rotation will start immediately');
11421223
} else {
11431224
return sprintf(
1144-
$this->translate('The first handoff will happen on %s'),
1225+
$this->translate('The rotation will start on %s'),
11451226
(new \IntlDateFormatter(
11461227
\Locale::getDefault(),
11471228
\IntlDateFormatter::MEDIUM,
@@ -1153,61 +1234,6 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
11531234
));
11541235
}
11551236

1156-
$termValidator = function (array $terms) {
1157-
$contactTerms = [];
1158-
$groupTerms = [];
1159-
foreach ($terms as $term) {
1160-
/** @var TermInput\Term $term */
1161-
if (strpos($term->getSearchValue(), ':') === false) {
1162-
// TODO: Auto-correct this to a valid type:id pair, if possible
1163-
$term->setMessage($this->translate('Is not a contact nor a group of contacts'));
1164-
continue;
1165-
}
1166-
1167-
list($type, $id) = explode(':', $term->getSearchValue(), 2);
1168-
if ($type === 'contact') {
1169-
$contactTerms[$id] = $term;
1170-
} elseif ($type === 'group') {
1171-
$groupTerms[$id] = $term;
1172-
}
1173-
}
1174-
1175-
if (! empty($contactTerms)) {
1176-
$contacts = (Contact::on(Database::get()))
1177-
->filter(Filter::equal('id', array_keys($contactTerms)));
1178-
foreach ($contacts as $contact) {
1179-
$contactTerms[$contact->id]
1180-
->setLabel($contact->full_name)
1181-
->setClass('contact');
1182-
}
1183-
}
1184-
1185-
if (! empty($groupTerms)) {
1186-
$groups = (Contactgroup::on(Database::get()))
1187-
->filter(Filter::equal('id', array_keys($groupTerms)));
1188-
foreach ($groups as $group) {
1189-
$groupTerms[$group->id]
1190-
->setLabel($group->name)
1191-
->setClass('group');
1192-
}
1193-
}
1194-
};
1195-
1196-
$this->addElement(
1197-
(new TermInput('members'))
1198-
->setIgnored()
1199-
->setRequired()
1200-
->setOrdered()
1201-
->setReadOnly()
1202-
->setVerticalTermDirection()
1203-
->setLabel($this->translate('Members'))
1204-
->setSuggestionUrl($this->suggestionUrl->with(['showCompact' => true, '_disableLayout' => 1]))
1205-
->on(TermInput::ON_ENRICH, $termValidator)
1206-
->on(TermInput::ON_ADD, $termValidator)
1207-
->on(TermInput::ON_SAVE, $termValidator)
1208-
->on(TermInput::ON_PASTE, $termValidator)
1209-
);
1210-
12111237
$this->addElement('submit', 'submit', [
12121238
'label' => $this->getSubmitLabel()
12131239
]);
@@ -1235,7 +1261,7 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
12351261
$this->getElement('submit')->prependWrapper((new HtmlDocument())->setHtmlContent(...$removeButtons));
12361262
}
12371263

1238-
$this->addElement($this->createCsrfCounterMeasure(Session::getSession()->getId()));
1264+
$this->addCsrfCounterMeasure(Session::getSession()->getId());
12391265
}
12401266

12411267
/**

0 commit comments

Comments
 (0)