1818use  Icinga \Web \Session ;
1919use  ipl \Html \Attributes ;
2020use  ipl \Html \DeferredText ;
21+ use  ipl \Html \FormDecoration \DescriptionDecorator ;
2122use  ipl \Html \FormElement \FieldsetElement ;
2223use  ipl \Html \HtmlDocument ;
2324use  ipl \Html \HtmlElement ;
@@ -602,7 +603,9 @@ protected function assembleModeSelection(): string
602603            '24-7 '  => $ this  ->translate ('24/7 ' )
603604        ];
604605
605-         $ modeList  = new  HtmlElement ('ul ' );
606+         $ modeList  = new  HtmlElement ('ul ' , Attributes::create ([
607+             'class '  => ['rotation-mode ' , $ this  ->disableModeSelection  ? 'disabled '  : '' ]
608+         ]));
606609        foreach  ($ modes  as  $ mode  => $ label ) {
607610            $ radio  = $ this  ->createElement ('input ' , 'mode ' , [
608611                'type '  => 'radio ' ,
@@ -684,8 +687,14 @@ protected function assembleModeSelection(): string
684687
685688        $ this  ->addHtml (new  HtmlElement (
686689            'div ' ,
687-             Attributes::create (['class '  => ['rotation-mode ' , $ this  ->disableModeSelection  ? 'disabled '  : '' ]]),
688-             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+             ),
689698            $ modeList
690699        ));
691700
@@ -704,12 +713,15 @@ protected function assembleTwentyFourSevenOptions(FieldsetElement $options): Dat
704713        $ options ->addElement ('number ' , 'interval ' , [
705714            'required '  => true ,
706715            'label '  => $ this  ->translate ('Handoff every ' ),
716+             'description '  => $ this  ->translate ('Have multiple rotation members take turns after this interval. ' ),
707717            'step '  => 1 ,
708718            'min '  => 1 ,
709719            'value '  => 1 ,
710720            'validators '  => [new  GreaterThanValidator ()]
711721        ]);
712722        $ interval  = $ options ->getElement ('interval ' );
723+         $ interval ->getDecorators ()
724+             ->replaceDecorator ('Description ' , DescriptionDecorator::class, ['class '  => 'description ' ]);
713725
714726        $ frequency  = $ options ->createElement ('select ' , 'frequency ' , [
715727            'required '  => true ,
@@ -796,11 +808,15 @@ protected function assemblePartialDayOptions(FieldsetElement $options): DateTime
796808        $ options ->addElement ('number ' , 'interval ' , [
797809            'required '  => true ,
798810            'label '  => $ this  ->translate ('Handoff every ' ),
811+             'description '  => $ this  ->translate ('Have multiple rotation members take turns after this interval. ' ),
799812            'step '  => 1 ,
800813            'min '  => 1 ,
801814            'value '  => 1 ,
802815            'validators '  => [new  GreaterThanValidator ()]
803816        ]);
817+         $ interval  = $ options ->getElement ('interval ' );
818+         $ interval ->getDecorators ()
819+             ->replaceDecorator ('Description ' , DescriptionDecorator::class, ['class '  => 'description ' ]);
804820
805821        $ selectedFromTime  = $ from ->getValue ();
806822        foreach  ($ timeOptions  as  $ key  => $ value ) {
@@ -830,7 +846,6 @@ protected function assemblePartialDayOptions(FieldsetElement $options): DateTime
830846            )
831847        );
832848
833-         $ interval  = $ options ->getElement ('interval ' );
834849        $ interval ->prependWrapper (
835850            (new  HtmlDocument ())->addHtml (
836851                $ interval ,
@@ -912,8 +927,12 @@ protected function assembleMultiDayOptions(FieldsetElement $options): DateTime
912927            'step '  => 1 ,
913928            'min '  => 1 ,
914929            'value '  => 1 ,
915-             'label '  => $ this  ->translate ('Handoff every ' )
930+             'label '  => $ this  ->translate ('Handoff every ' ),
931+             'description '  => $ this  ->translate ('Have multiple rotation members take turns after this interval. ' )
916932        ]);
933+         $ interval  = $ options ->getElement ('interval ' );
934+         $ interval ->getDecorators ()
935+             ->replaceDecorator ('Description ' , DescriptionDecorator::class, ['class '  => 'description ' ]);
917936
918937        $ timeOptions  = $ this  ->getTimeOptions ();
919938        $ fromAt  = $ options ->createElement ('select ' , 'from_at ' , [
@@ -988,7 +1007,6 @@ protected function assembleMultiDayOptions(FieldsetElement $options): DateTime
9881007            )
9891008        );
9901009
991-         $ interval  = $ options ->getElement ('interval ' );
9921010        $ interval ->prependWrapper (
9931011            (new  HtmlDocument ())->addHtml (
9941012                $ interval ,
@@ -1029,17 +1047,9 @@ protected function assemble()
10291047
10301048        $ this  ->addElement ('hidden ' , 'priority ' , ['ignore '  => true ]);
10311049
1032-         $ mode  = $ this  ->assembleModeSelection ();
1033- 
1034-         $ autoSubmittedBy  = $ this  ->getRequest ()->getHeader ('X-Icinga-Autosubmittedby ' )[0 ] ?? '' ;
1035-         if  ($ autoSubmittedBy  === 'mode ' ) {
1036-             $ this  ->clearPopulatedValue ('options ' );
1037-             $ this  ->clearPopulatedValue ('first_handoff ' );
1038-         }
1039- 
10401050        $ this  ->addElement ('text ' , 'name ' , [
10411051            'required '  => true ,
1042-             'label '  => $ this  ->translate ('Title ' ),
1052+             'label '  => $ this  ->translate ('Rotation Name ' ),
10431053            'validators '  => [
10441054                new  CallbackValidator (function  ($ value , $ validator ) {
10451055                    $ rotations  = Rotation::on ($ this  ->db )
@@ -1051,7 +1061,7 @@ protected function assemble()
10511061                    }
10521062
10531063                    if  ($ rotations ->first () !== null ) {
1054-                         $ validator ->addMessage ($ this  ->translate ('A rotation with this title  already exists ' ));
1064+                         $ validator ->addMessage ($ this  ->translate ('A rotation with this name  already exists ' ));
10551065
10561066                        return  false ;
10571067                    }
@@ -1061,6 +1071,73 @@ protected function assemble()
10611071            ]
10621072        ]);
10631073
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+ 
10641141        $ this  ->addElement ('fieldset ' , 'options ' );
10651142        /** @var FieldsetElement $options */ 
10661143        $ options  = $ this  ->getElement ('options ' );
@@ -1098,7 +1175,7 @@ protected function assemble()
10981175            'aria-describedby '  => 'first-handoff-description ' ,
10991176            'min '  => $ earliestHandoff  !== null  ? $ earliestHandoff ->format ('Y-m-d ' ) : null ,
11001177            'max '  => $ latestHandoff ->format ('Y-m-d ' ),
1101-             'label '  => $ this  ->translate ('First Handoff ' ),
1178+             'label '  => $ this  ->translate ('Rotation Start ' ),
11021179            'value '  => $ firstHandoffDefault ,
11031180            'validators '  => [
11041181                new  CallbackValidator (
@@ -1110,14 +1187,14 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
11101187                        );
11111188                        if  ($ earliestHandoff  !== null  && $ chosenHandoff  < $ earliestHandoff ) {
11121189                            $ validator ->addMessage (sprintf (
1113-                                 $ this  ->translate ('The first handoff  can only happen  after %s ' ),
1190+                                 $ this  ->translate ('The rotation  can only start  after %s ' ),
11141191                                $ earliestHandoff ->format ('Y-m-d ' ) // TODO: Use intl here 
11151192                            ));
11161193
11171194                            return  false ;
11181195                        } elseif  ($ chosenHandoff  > $ latestHandoff ) {
11191196                            $ validator ->addMessage (sprintf (
1120-                                 $ this  ->translate ('The first handoff  can only happen  before %s ' ),
1197+                                 $ this  ->translate ('The rotation  can only start  before %s ' ),
11211198                                $ latestHandoff ->format ('Y-m-d ' ) // TODO: Use intl here 
11221199                            ));
11231200
@@ -1142,10 +1219,10 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
11421219
11431220                    $ actualFirstHandoff  = $ ruleGenerator ->current ()[0 ]->getStartDate ();
11441221                    if  ($ actualFirstHandoff  < new  DateTime ()) {
1145-                         return  $ this  ->translate ('The first handoff  will happen  immediately ' );
1222+                         return  $ this  ->translate ('The rotation  will start  immediately ' );
11461223                    } else  {
11471224                        return  sprintf (
1148-                             $ this  ->translate ('The first handoff  will happen  on %s ' ),
1225+                             $ this  ->translate ('The rotation  will start  on %s ' ),
11491226                            (new  \IntlDateFormatter (
11501227                                \Locale::getDefault (),
11511228                                \IntlDateFormatter::MEDIUM ,
@@ -1157,65 +1234,6 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
11571234            ));
11581235        }
11591236
1160-         $ termValidator  = function  (array  $ terms ) {
1161-             $ contactTerms  = [];
1162-             $ groupTerms  = [];
1163-             foreach  ($ terms  as  $ term ) {
1164-                 /** @var TermInput\Term $term */ 
1165-                 if  (strpos ($ term ->getSearchValue (), ': ' ) === false ) {
1166-                     // TODO: Auto-correct this to a valid type:id pair, if possible 
1167-                     $ term ->setMessage ($ this  ->translate ('Is not a contact nor a group of contacts ' ));
1168-                     continue ;
1169-                 }
1170- 
1171-                 list ($ type , $ id ) = explode (': ' , $ term ->getSearchValue (), 2 );
1172-                 if  ($ type  === 'contact ' ) {
1173-                     $ contactTerms [$ id ] = $ term ;
1174-                 } elseif  ($ type  === 'group ' ) {
1175-                     $ groupTerms [$ id ] = $ term ;
1176-                 }
1177-             }
1178- 
1179-             if  (! empty ($ contactTerms )) {
1180-                 $ contacts  = (Contact::on (Database::get ()))
1181-                     ->filter (Filter::equal ('id ' , array_keys ($ contactTerms )));
1182-                 foreach  ($ contacts  as  $ contact ) {
1183-                     $ contactTerms [$ contact ->id ]
1184-                         ->setLabel ($ contact ->full_name )
1185-                         ->setClass ('contact ' );
1186-                 }
1187-             }
1188- 
1189-             if  (! empty ($ groupTerms )) {
1190-                 $ groups  = (Contactgroup::on (Database::get ()))
1191-                     ->filter (Filter::equal ('id ' , array_keys ($ groupTerms )));
1192-                 foreach  ($ groups  as  $ group ) {
1193-                     $ groupTerms [$ group ->id ]
1194-                         ->setLabel ($ group ->name )
1195-                         ->setClass ('group ' );
1196-                 }
1197-             }
1198-         };
1199- 
1200-         $ members  = (new  TermInput ('members ' ))
1201-             ->setIgnored ()
1202-             ->setRequired ()
1203-             ->setOrdered ()
1204-             ->setReadOnly ()
1205-             ->setVerticalTermDirection ()
1206-             ->setLabel ($ this  ->translate ('Members ' ))
1207-             ->setSuggestionUrl ($ this  ->suggestionUrl ->with (['showCompact '  => true , '_disableLayout '  => 1 ]))
1208-             ->on (TermInput::ON_ENRICH , $ termValidator )
1209-             ->on (TermInput::ON_ADD , $ termValidator )
1210-             ->on (TermInput::ON_SAVE , $ termValidator )
1211-             ->on (TermInput::ON_PASTE , $ termValidator );
1212-         $ this  ->addElement ($ members );
1213- 
1214-         // TODO: TermInput is not compatible with the new decorators yet: https://github.com/Icinga/ipl-web/pull/317 
1215-         $ legacyDecorator  = new  IcingaFormDecorator ();
1216-         $ members ->setDefaultElementDecorator ($ legacyDecorator );
1217-         $ legacyDecorator ->decorate ($ members );
1218- 
12191237        $ this  ->addElement ('submit ' , 'submit ' , [
12201238            'label '  => $ this  ->getSubmitLabel ()
12211239        ]);
@@ -1243,7 +1261,7 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
12431261            $ this  ->getElement ('submit ' )->prependWrapper ((new  HtmlDocument ())->setHtmlContent (...$ removeButtons ));
12441262        }
12451263
1246-         $ this  ->addElement ( $ this -> createCsrfCounterMeasure ( Session::getSession ()->getId () ));
1264+         $ this  ->addCsrfCounterMeasure ( Session::getSession ()->getId ());
12471265    }
12481266
12491267    /** 
0 commit comments