From c15e99bb5a8e66b86dd855b1dfb212fac2037bc3 Mon Sep 17 00:00:00 2001 From: Mark Johnson Date: Wed, 20 Nov 2024 13:38:16 +0000 Subject: [PATCH] Add behat generators, named selectors and page URLs for smart menus. (#765) This creates generators and named selectors for smart menus and smart menu items, plus some navigation URLs for the smart menu settings forms. --- classes/form/smartmenu_edit_form.php | 53 +- classes/form/smartmenu_item_edit_form.php | 79 +-- classes/smartmenu.php | 131 +++++ classes/smartmenu_item.php | 124 ++++- tests/behat/behat_theme_boost_union.php | 342 +++++++++++++ .../behat_theme_boost_union_generator.php | 479 ++++++++++++++++++ tests/generator/lib.php | 357 +++++++++++++ 7 files changed, 1448 insertions(+), 117 deletions(-) create mode 100644 tests/behat/behat_theme_boost_union.php create mode 100644 tests/generator/behat_theme_boost_union_generator.php create mode 100644 tests/generator/lib.php diff --git a/classes/form/smartmenu_edit_form.php b/classes/form/smartmenu_edit_form.php index c7ae6d4471e..fe4731a6946 100644 --- a/classes/form/smartmenu_edit_form.php +++ b/classes/form/smartmenu_edit_form.php @@ -80,10 +80,7 @@ public function definition() { $mform->addRule('location', get_string('required'), 'required'); // Add mode as select element. - $modeoptions = [ - smartmenu::MODE_SUBMENU => get_string('smartmenusmodesubmenu', 'theme_boost_union'), - smartmenu::MODE_INLINE => get_string('smartmenusmodeinline', 'theme_boost_union'), - ]; + $modeoptions = smartmenu::get_mode_options(); $mform->addElement('select', 'mode', get_string('smartmenusmenumode', 'theme_boost_union'), $modeoptions); $mform->setDefault('mode', smartmenu::MODE_SUBMENU); $mform->setType('mode', PARAM_INT); @@ -102,12 +99,7 @@ public function definition() { $mform->addHelpButton('type', 'smartmenusmenutype', 'theme_boost_union'); // Add show description as select element. - $showdescriptionoptions = [ - smartmenu::DESC_NEVER => get_string('smartmenusmenushowdescriptionnever', 'theme_boost_union'), - smartmenu::DESC_ABOVE => get_string('smartmenusmenushowdescriptionabove', 'theme_boost_union'), - smartmenu::DESC_BELOW => get_string('smartmenusmenushowdescriptionbelow', 'theme_boost_union'), - smartmenu::DESC_HELP => get_string('smartmenusmenushowdescriptionhelp', 'theme_boost_union'), - ]; + $showdescriptionoptions = smartmenu::get_showdescription_options(); $mform->addElement('select', 'showdesc', get_string('smartmenusmenushowdescription', 'theme_boost_union'), $showdescriptionoptions); $mform->setDefault('showdesc', smartmenu::DESC_NEVER); @@ -115,11 +107,7 @@ public function definition() { $mform->addHelpButton('showdesc', 'smartmenusmenushowdescription', 'theme_boost_union'); // Add more menu behavior as select element. - $moremenuoptions = [ - smartmenu::MOREMENU_DONOTCHANGE => get_string('dontchange', 'theme_boost_union'), - smartmenu::MOREMENU_INTO => get_string('smartmenusmenumoremenubehaviorforceinto', 'theme_boost_union'), - smartmenu::MOREMENU_OUTSIDE => get_string('smartmenusmenumoremenubehaviorkeepoutside', 'theme_boost_union'), - ]; + $moremenuoptions = smartmenu::get_moremenu_options(); $mform->addElement('select', 'moremenubehavior', get_string('smartmenusmenumoremenubehavior', 'theme_boost_union'), $moremenuoptions); $mform->setDefault('moremenubehavior', smartmenu::MOREMENU_DONOTCHANGE); @@ -132,12 +120,7 @@ public function definition() { $mform->setType('cssclass', PARAM_TEXT); // Add card size as select element. - $cardsizeoptions = [ - smartmenu::CARDSIZE_TINY => get_string('smartmenusmenucardsizetiny', 'theme_boost_union').' (50px)', - smartmenu::CARDSIZE_SMALL => get_string('smartmenusmenucardsizesmall', 'theme_boost_union').' (100px)', - smartmenu::CARDSIZE_MEDIUM => get_string('smartmenusmenucardsizemedium', 'theme_boost_union').' (150px)', - smartmenu::CARDSIZE_LARGE => get_string('smartmenusmenucardsizelarge', 'theme_boost_union').' (200px)', - ]; + $cardsizeoptions = smartmenu::get_cardsize_options(); $mform->addElement('select', 'cardsize', get_string('smartmenusmenucardsize', 'theme_boost_union'), $cardsizeoptions); $mform->setDefault('cardsize', smartmenu::CARDSIZE_TINY); $mform->setType('cardsize', PARAM_INT); @@ -145,16 +128,7 @@ public function definition() { $mform->addHelpButton('cardsize', 'smartmenusmenucardsize', 'theme_boost_union'); // Add card form as select element. - $cardformoptions = [ - smartmenu::CARDFORM_SQUARE => - get_string('smartmenusmenucardformsquare', 'theme_boost_union').' (1/1)', - smartmenu::CARDFORM_PORTRAIT => - get_string('smartmenusmenucardformportrait', 'theme_boost_union').' (2/3)', - smartmenu::CARDFORM_LANDSCAPE => - get_string('smartmenusmenucardformlandscape', 'theme_boost_union').' (3/2)', - smartmenu::CARDFORM_FULLWIDTH => - get_string('smartmenusmenucardformfullwidth', 'theme_boost_union'), - ]; + $cardformoptions = smartmenu::get_cardform_options(); $mform->addElement('select', 'cardform', get_string('smartmenusmenucardform', 'theme_boost_union'), $cardformoptions); $mform->setDefault('cardform', smartmenu::CARDFORM_SQUARE); @@ -163,12 +137,7 @@ public function definition() { $mform->addHelpButton('cardform', 'smartmenusmenucardform', 'theme_boost_union'); // Add card overflow behaviour as select element. - $cardoverflowoptions = [ - smartmenu::CARDOVERFLOWBEHAVIOUR_NOWRAP => - get_string('smartmenusmenucardoverflowbehaviornowrap', 'theme_boost_union'), - smartmenu::CARDOVERFLOWBEHAVIOUR_WRAP => - get_string('smartmenusmenucardoverflowbehaviorwrap', 'theme_boost_union'), - ]; + $cardoverflowoptions = smartmenu::get_cardoverflowbehaviour_options(); $mform->addElement('select', 'cardoverflowbehavior', get_string('smartmenusmenucardoverflowbehavior', 'theme_boost_union'), $cardoverflowoptions); $mform->setDefault('cardoverflowbehaviour', smartmenu::CARDOVERFLOWBEHAVIOUR_NOWRAP); @@ -199,10 +168,7 @@ public function definition() { $mform->addHelpButton('roles', 'smartmenusbyrole', 'theme_boost_union'); // Add context as select element. - $rolecontext = [ - smartmenu::ANYCONTEXT => get_string('any'), - smartmenu::SYSTEMCONTEXT => get_string('coresystem'), - ]; + $rolecontext = smartmenu::get_rolecontext_options(); $mform->addElement('select', 'rolecontext', get_string('smartmenusrolecontext', 'theme_boost_union'), $rolecontext); $mform->setDefault('rolecontext', smartmenu::ANYCONTEXT); $mform->setType('rolecontext', PARAM_INT); @@ -249,10 +215,7 @@ public function definition() { $mform->addHelpButton('cohorts', 'smartmenusbycohort', 'theme_boost_union'); // Add operator as select element. - $operatoroptions = [ - smartmenu::ANY => get_string('any'), - smartmenu::ALL => get_string('all'), - ]; + $operatoroptions = smartmenu::get_operator_options(); $mform->addElement('select', 'operator', get_string('smartmenusoperator', 'theme_boost_union'), $operatoroptions); $mform->setDefault('operator', smartmenu::ANY); $mform->setType('operator', PARAM_INT); diff --git a/classes/form/smartmenu_item_edit_form.php b/classes/form/smartmenu_item_edit_form.php index 7ada173ec4b..799f4695e7e 100644 --- a/classes/form/smartmenu_item_edit_form.php +++ b/classes/form/smartmenu_item_edit_form.php @@ -98,10 +98,7 @@ public function definition() { $mform->addHelpButton('url', 'smartmenusmenuitemurl', 'theme_boost_union'); // Add mode as select element. - $modeoptions = [ - smartmenu_item::MODE_INLINE => get_string('smartmenusmodeinline', 'theme_boost_union'), - smartmenu_item::MODE_SUBMENU => get_string('smartmenusmodesubmenu', 'theme_boost_union'), - ]; + $modeoptions = smartmenu_item::get_mode_options(); $mform->addElement('select', 'mode', get_string('smartmenusmenuitemmode', 'theme_boost_union'), $modeoptions); $mform->setDefault('mode', smartmenu_item::MODE_INLINE); $mform->setType('mode', PARAM_INT); @@ -138,14 +135,7 @@ public function definition() { $mform->addHelpButton('enrolmentrole', 'smartmenusdynamiccoursesenrolmentrole', 'theme_boost_union'); // Add completion status (for the dynamic courses menu item type) as autocomplete element. - $completionstatusoptions = [ - smartmenu_item::COMPLETION_ENROLLED => - get_string('smartmenusdynamiccoursescompletionstatusenrolled', 'theme_boost_union'), - smartmenu_item::COMPLETION_INPROGRESS => - get_string('smartmenusdynamiccoursescompletionstatusinprogress', 'theme_boost_union'), - smartmenu_item::COMPLETION_COMPLETED => - get_string('smartmenusdynamiccoursescompletionstatuscompleted', 'theme_boost_union'), - ]; + $completionstatusoptions = smartmenu_item::get_completionstatus_options(); $completionstatuswidget = $mform->addElement('autocomplete', 'completionstatus', get_string('smartmenusmenuitemtypedynamiccourses', 'theme_boost_union').': '. get_string('smartmenusdynamiccoursescompletionstatus', 'theme_boost_union'), $completionstatusoptions); @@ -155,14 +145,7 @@ public function definition() { $mform->addHelpButton('completionstatus', 'smartmenusdynamiccoursescompletionstatus', 'theme_boost_union'); // Add date range (for the dynamic courses menu item type) as autocomplete element. - $daterangeoptions = [ - smartmenu_item::RANGE_PAST => - get_string('smartmenusdynamiccoursesdaterangepast', 'theme_boost_union'), - smartmenu_item::RANGE_PRESENT => - get_string('smartmenusdynamiccoursesdaterangepresent', 'theme_boost_union'), - smartmenu_item::RANGE_FUTURE => - get_string('smartmenusdynamiccoursesdaterangefuture', 'theme_boost_union'), - ]; + $daterangeoptions = smartmenu_item::get_daterange_options(); $daterangewidget = $mform->addElement('autocomplete', 'daterange', get_string('smartmenusmenuitemtypedynamiccourses', 'theme_boost_union').': '. get_string('smartmenusdynamiccoursesdaterange', 'theme_boost_union'), $daterangeoptions); @@ -196,13 +179,7 @@ public function definition() { $PAGE->requires->js_call_amd('theme_boost_union/fontawesome-popover', 'init', ['#id_menuicon', $systemcontextid]); // Add title presentation and select element. - $displayoptions = [ - smartmenu_item::DISPLAY_SHOWTITLEICON => - get_string('smartmenusmenuitemdisplayoptionsshowtitleicon', 'theme_boost_union'), - smartmenu_item::DISPLAY_HIDETITLE => get_string('smartmenusmenuitemdisplayoptionshidetitle', 'theme_boost_union'), - smartmenu_item::DISPLAY_HIDETITLEMOBILE => - get_string('smartmenusmenuitemdisplayoptionshidetitlemobile', 'theme_boost_union'), - ]; + $displayoptions = smartmenu_item::get_display_options(); $mform->addElement('select', 'display', get_string('smartmenusmenuitemdisplayoptions', 'theme_boost_union'), $displayoptions); $mform->setDefault('display', smartmenu_item::DISPLAY_SHOWTITLEICON); @@ -215,10 +192,7 @@ public function definition() { $mform->addHelpButton('tooltip', 'smartmenusmenuitemtooltip', 'theme_boost_union'); // Add link target as select element. - $targetoptions = [ - smartmenu_item::TARGET_SAME => get_string('smartmenusmenuitemlinktargetsamewindow', 'theme_boost_union'), - smartmenu_item::TARGET_NEW => get_string('smartmenusmenuitemlinktargetnewtab', 'theme_boost_union'), - ]; + $targetoptions = smartmenu_item::get_target_options(); $mform->addElement('select', 'target', get_string('smartmenusmenuitemlinktarget', 'theme_boost_union'), $targetoptions); $mform->setDefault('target', smartmenu_item::TARGET_SAME); @@ -256,24 +230,7 @@ public function definition() { $mform->addHelpButton('cssclass', 'smartmenusmenuitemcssclass', 'theme_boost_union'); // Add course list ordering (for the dynamic courses menu item type) as select element. - $listsortoptions = [ - smartmenu_item::LISTSORT_FULLNAME_ASC => - get_string('smartmenusmenuitemlistsortfullnameasc', 'theme_boost_union'), - smartmenu_item::LISTSORT_FULLNAME_DESC => - get_string('smartmenusmenuitemlistsortfullnamedesc', 'theme_boost_union'), - smartmenu_item::LISTSORT_SHORTNAME_ASC => - get_string('smartmenusmenuitemlistsortshortnameasc', 'theme_boost_union'), - smartmenu_item::LISTSORT_SHORTNAME_DESC => - get_string('smartmenusmenuitemlistsortshortnamedesc', 'theme_boost_union'), - smartmenu_item::LISTSORT_COURSEID_ASC => - get_string('smartmenusmenuitemlistsortcourseidasc', 'theme_boost_union'), - smartmenu_item::LISTSORT_COURSEID_DESC => - get_string('smartmenusmenuitemlistsortcourseiddesc', 'theme_boost_union'), - smartmenu_item::LISTSORT_COURSEIDNUMBER_ASC => - get_string('smartmenusmenuitemlistsortcourseidnumberasc', 'theme_boost_union'), - smartmenu_item::LISTSORT_COURSEIDNUMBER_DESC => - get_string('smartmenusmenuitemlistsortcourseidnumberdesc', 'theme_boost_union'), - ]; + $listsortoptions = smartmenu_item::get_listsort_options(); $mform->addElement('select', 'listsort', get_string('smartmenusmenuitemtypedynamiccourses', 'theme_boost_union').': '. get_string('smartmenusmenuitemlistsort', 'theme_boost_union'), $listsortoptions); @@ -283,10 +240,7 @@ public function definition() { $mform->addHelpButton('listsort', 'smartmenusmenuitemlistsort', 'theme_boost_union'); // Add course name presentation (for the dynamic courses menu item type) as select element. - $displayfieldoptions = [ - smartmenu_item::FIELD_FULLNAME => get_string('smartmenusmenuitemdisplayfieldcoursefullname', 'theme_boost_union'), - smartmenu_item::FIELD_SHORTNAME => get_string('smartmenusmenuitemdisplayfieldcourseshortname', 'theme_boost_union'), - ]; + $displayfieldoptions = smartmenu_item::get_displayfield_options(); $mform->addElement('select', 'displayfield', get_string('smartmenusmenuitemtypedynamiccourses', 'theme_boost_union').': '. get_string('smartmenusmenuitemdisplayfield', 'theme_boost_union'), $displayfieldoptions); @@ -318,14 +272,7 @@ public function definition() { $mform->addHelpButton('image', 'smartmenusmenuitemcardimage', 'theme_boost_union'); // Add card text position as select element. - $textpositionoptions = [ - smartmenu_item::POSITION_BELOW => - get_string('smartmenusmenuitemtextpositionbelowimage', 'theme_boost_union'), - smartmenu_item::POSITION_OVERLAYTOP => - get_string('smartmenusmenuitemtextpositionoverlaytop', 'theme_boost_union'), - smartmenu_item::POSITION_OVERLAYBOTTOM => - get_string('smartmenusmenuitemtextpositionoverlaybottom', 'theme_boost_union'), - ]; + $textpositionoptions = smartmenu_item::get_textposition_options(); $mform->addElement('select', 'textposition', get_string('smartmenusmenuitemtextposition', 'theme_boost_union'), $textpositionoptions); $mform->setDefault('textposition', smartmenu_item::POSITION_BELOW); @@ -368,10 +315,7 @@ public function definition() { $mform->addHelpButton('roles', 'smartmenusbyrole', 'theme_boost_union'); // Add context as select element. - $rolecontext = [ - smartmenu::ANYCONTEXT => get_string('any'), - smartmenu::SYSTEMCONTEXT => get_string('coresystem'), - ]; + $rolecontext = smartmenu::get_rolecontext_options(); $mform->addElement('select', 'rolecontext', get_string('smartmenusrolecontext', 'theme_boost_union'), $rolecontext); $mform->setDefault('rolecontext', smartmenu::ANYCONTEXT); $mform->setType('rolecontext', PARAM_INT); @@ -418,10 +362,7 @@ public function definition() { $mform->addHelpButton('cohorts', 'smartmenusbycohort', 'theme_boost_union'); // Add operator as select element. - $operatoroptions = [ - smartmenu::ANY => get_string('any'), - smartmenu::ALL => get_string('all'), - ]; + $operatoroptions = smartmenu::get_operator_options(); $mform->addElement('select', 'operator', get_string('smartmenusoperator', 'theme_boost_union'), $operatoroptions); $mform->setDefault('operator', smartmenu::ANY); $mform->setType('operator', PARAM_INT); diff --git a/classes/smartmenu.php b/classes/smartmenu.php index 6c6f1be27f0..2393e5e3e5d 100644 --- a/classes/smartmenu.php +++ b/classes/smartmenu.php @@ -592,6 +592,7 @@ public function build($resetcache=false) { return false; } + $this->menu->classes[] = 'boost-union-smartmenu'; $this->menu->classes[] = $this->get_cardform(); // Html class for the card form size, Potrait, Square, landscape. $this->menu->classes[] = $this->get_cardsize(); // HTML class for the card Size, tiny, small, medium, large. $this->menu->classes[] = $this->get_cardwrap(); // HtML class for the card overflow behaviour. @@ -885,6 +886,136 @@ public static function get_type($type) { return $types[$type] ?? false; } + /** + * Return options for the mode setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_mode_options(): array { + return [ + self::MODE_SUBMENU => get_string('smartmenusmodesubmenu', 'theme_boost_union'), + self::MODE_INLINE => get_string('smartmenusmodeinline', 'theme_boost_union'), + ]; + } + + /** + * Return options for the showdescription setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_showdescription_options(): array { + return [ + self::DESC_NEVER => get_string('smartmenusmenushowdescriptionnever', 'theme_boost_union'), + self::DESC_ABOVE => get_string('smartmenusmenushowdescriptionabove', 'theme_boost_union'), + self::DESC_BELOW => get_string('smartmenusmenushowdescriptionbelow', 'theme_boost_union'), + self::DESC_HELP => get_string('smartmenusmenushowdescriptionhelp', 'theme_boost_union'), + ]; + } + + /** + * Return options for the moremenu setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_moremenu_options(): array { + return [ + self::MOREMENU_DONOTCHANGE => get_string('dontchange', 'theme_boost_union'), + self::MOREMENU_INTO => get_string('smartmenusmenumoremenubehaviorforceinto', 'theme_boost_union'), + self::MOREMENU_OUTSIDE => get_string('smartmenusmenumoremenubehaviorkeepoutside', 'theme_boost_union'), + ]; + } + + /** + * Return options for the cardsize setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_cardsize_options(): array { + return [ + self::CARDSIZE_TINY => get_string('smartmenusmenucardsizetiny', 'theme_boost_union').' (50px)', + self::CARDSIZE_SMALL => get_string('smartmenusmenucardsizesmall', 'theme_boost_union').' (100px)', + self::CARDSIZE_MEDIUM => get_string('smartmenusmenucardsizemedium', 'theme_boost_union').' (150px)', + self::CARDSIZE_LARGE => get_string('smartmenusmenucardsizelarge', 'theme_boost_union').' (200px)', + ]; + } + + /** + * Return options for the cardform setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_cardform_options(): array { + return[ + self::CARDFORM_SQUARE => + get_string('smartmenusmenucardformsquare', 'theme_boost_union').' (1/1)', + self::CARDFORM_PORTRAIT => + get_string('smartmenusmenucardformportrait', 'theme_boost_union').' (2/3)', + self::CARDFORM_LANDSCAPE => + get_string('smartmenusmenucardformlandscape', 'theme_boost_union').' (3/2)', + self::CARDFORM_FULLWIDTH => + get_string('smartmenusmenucardformfullwidth', 'theme_boost_union'), + ]; + } + + /** + * Return options for the cardoverflowbehaviour setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_cardoverflowbehaviour_options(): array { + return [ + self::CARDOVERFLOWBEHAVIOUR_NOWRAP => + get_string('smartmenusmenucardoverflowbehaviornowrap', 'theme_boost_union'), + self::CARDOVERFLOWBEHAVIOUR_WRAP => + get_string('smartmenusmenucardoverflowbehaviorwrap', 'theme_boost_union'), + ]; + } + + /** + * Return options for the rolecontext setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_rolecontext_options(): array { + return [ + self::ANYCONTEXT => get_string('any'), + self::SYSTEMCONTEXT => get_string('coresystem'), + ]; + } + + /** + * Return options for the byadmin setting. + * + * @return array + */ + public static function get_byadmin_options(): array { + return [ + self::BYADMIN_ALL => get_string('smartmenusbyadmin_all', 'theme_boost_union'), + self::BYADMIN_ADMINS => get_string('smartmenusbyadmin_admins', 'theme_boost_union'), + self::BYADMIN_NONADMINS => get_string('smartmenusbyadmin_nonadmins', 'theme_boost_union'), + ]; + } + + /** + * Return options for the operator setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_operator_options(): array { + return [ + self::ANY => get_string('any'), + self::ALL => get_string('all'), + ]; + } + /** * Insert or update the menu instance to DB. Convert the multiple options select elements to json. * setup menu order after insert. diff --git a/classes/smartmenu_item.php b/classes/smartmenu_item.php index c40d37e28d5..9427257db8d 100644 --- a/classes/smartmenu_item.php +++ b/classes/smartmenu_item.php @@ -1043,6 +1043,7 @@ public function build() { } // Add custom css class. + $class[] = 'boost-union-smartitem'; $class[] = $this->item->cssclass; // Add classes for hide items in specific viewport. $class[] = $this->item->desktop ? 'd-lg-none' : 'd-lg-inline-flex'; @@ -1337,14 +1338,131 @@ public static function get_types(?int $type = null) { */ public static function get_display_options(?int $option = null) { $displayoptions = [ - self::DISPLAY_SHOWTITLEICON => get_string('smartmenusmenuitemdisplayoptionsshowtitleicon', 'theme_boost_union'), - self::DISPLAY_HIDETITLE => get_string('smartmenushidetitle', 'theme_boost_union'), - self::DISPLAY_HIDETITLEMOBILE => get_string('smartmenushidetitlemobile', 'theme_boost_union'), + self::DISPLAY_SHOWTITLEICON => get_string('smartmenusmenuitemdisplayoptionsshowtitleicon', 'theme_boost_union'), + self::DISPLAY_HIDETITLE => get_string('smartmenusmenuitemdisplayoptionshidetitle', 'theme_boost_union'), + self::DISPLAY_HIDETITLEMOBILE => get_string('smartmenusmenuitemdisplayoptionshidetitlemobile', 'theme_boost_union'), ]; return ($option !== null && isset($displayoptions[$option])) ? $displayoptions[$option] : $displayoptions; } + /** + * Return the options for the target setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_target_options(): array { + return [ + self::TARGET_SAME => get_string('smartmenusmenuitemlinktargetsamewindow', 'theme_boost_union'), + self::TARGET_NEW => get_string('smartmenusmenuitemlinktargetnewtab', 'theme_boost_union'), + ]; + } + + /** + * Return the options for the completionstatus setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_completionstatus_options(): array { + return [ + self::COMPLETION_ENROLLED => + get_string('smartmenusdynamiccoursescompletionstatusenrolled', 'theme_boost_union'), + self::COMPLETION_INPROGRESS => + get_string('smartmenusdynamiccoursescompletionstatusinprogress', 'theme_boost_union'), + self::COMPLETION_COMPLETED => + get_string('smartmenusdynamiccoursescompletionstatuscompleted', 'theme_boost_union'), + ]; + } + + /** + * Return the options for the daterange setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_daterange_options(): array { + return [ + self::RANGE_PAST => + get_string('smartmenusdynamiccoursesdaterangepast', 'theme_boost_union'), + self::RANGE_PRESENT => + get_string('smartmenusdynamiccoursesdaterangepresent', 'theme_boost_union'), + self::RANGE_FUTURE => + get_string('smartmenusdynamiccoursesdaterangefuture', 'theme_boost_union'), + ]; + } + + /** + * Return the options for the listsort setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_listsort_options(): array { + return [ + self::LISTSORT_FULLNAME_ASC => + get_string('smartmenusmenuitemlistsortfullnameasc', 'theme_boost_union'), + self::LISTSORT_FULLNAME_DESC => + get_string('smartmenusmenuitemlistsortfullnamedesc', 'theme_boost_union'), + self::LISTSORT_SHORTNAME_ASC => + get_string('smartmenusmenuitemlistsortshortnameasc', 'theme_boost_union'), + self::LISTSORT_SHORTNAME_DESC => + get_string('smartmenusmenuitemlistsortshortnamedesc', 'theme_boost_union'), + self::LISTSORT_COURSEID_ASC => + get_string('smartmenusmenuitemlistsortcourseidasc', 'theme_boost_union'), + self::LISTSORT_COURSEID_DESC => + get_string('smartmenusmenuitemlistsortcourseiddesc', 'theme_boost_union'), + self::LISTSORT_COURSEIDNUMBER_ASC => + get_string('smartmenusmenuitemlistsortcourseidnumberasc', 'theme_boost_union'), + self::LISTSORT_COURSEIDNUMBER_DESC => + get_string('smartmenusmenuitemlistsortcourseidnumberdesc', 'theme_boost_union'), + ]; + } + + /** + * Return the options for the displayfield setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_displayfield_options(): array { + return [ + self::FIELD_FULLNAME => get_string('smartmenusmenuitemdisplayfieldcoursefullname', 'theme_boost_union'), + self::FIELD_SHORTNAME => get_string('smartmenusmenuitemdisplayfieldcourseshortname', 'theme_boost_union'), + ]; + } + + /** + * Return the options for the mode setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_mode_options(): array { + return [ + self::MODE_INLINE => get_string('smartmenusmodeinline', 'theme_boost_union'), + self::MODE_SUBMENU => get_string('smartmenusmodesubmenu', 'theme_boost_union'), + ]; + } + + /** + * Return the options for the testposition setting. + * + * @return array + * @throws \coding_exception + */ + public static function get_textposition_options(): array { + return [ + self::POSITION_BELOW => + get_string('smartmenusmenuitemtextpositionbelowimage', 'theme_boost_union'), + self::POSITION_OVERLAYTOP => + get_string('smartmenusmenuitemtextpositionoverlaytop', 'theme_boost_union'), + self::POSITION_OVERLAYBOTTOM => + get_string('smartmenusmenuitemtextpositionoverlaybottom', 'theme_boost_union'), + ]; + } + /** * Insert or update the menu instance to DB. Convert the multiple options select elements to json. * setup menu path after insert/update. diff --git a/tests/behat/behat_theme_boost_union.php b/tests/behat/behat_theme_boost_union.php new file mode 100644 index 00000000000..495c8a4145e --- /dev/null +++ b/tests/behat/behat_theme_boost_union.php @@ -0,0 +1,342 @@ +. + +/** + * Standard behat step data providers for theme_boost_union + * + * @package theme_boost_union + * @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net} + * @author Mark Johnson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_theme_boost_union extends behat_base { + /** + * Convert page names to URLs for steps like 'When I am on the "[page name]" page'. + * + * Tabbed settings pages support an optional "> Tabname" suffix, otherwise they will load the default tab. + * + * Recognised page names are: + * | Settings | Settings overview | + * | Look | "Look" settings page | + * | Feel | "Feel" settings page | + * | Content | "Content" settings page | + * | Functionality | "Functionality" settings page | + * | Flavours | Flavours listing page | + * | Smart menus | Smart menus listing page | + * + * @param string $page name of the page, with the component name removed e.g. 'Admin notification'. + * @return moodle_url the corresponding URL. + * @throws Exception with a meaningful error message if the specified page cannot be found. + */ + protected function resolve_page_url(string $page): moodle_url { + $parts = explode('>', strtolower($page)); + $section = trim($parts[0]); + if (count($parts) < 2) { + return match ($section) { + 'settings' => new moodle_url('/theme/boost_union/settings_overview.php'), + 'look' => new moodle_url('/admin/settings.php?section=theme_boost_union_look'), + 'feel' => new moodle_url('/admin/settings.php?section=theme_boost_union_feel'), + 'content' => new moodle_url('/admin/settings.php?section=theme_boost_union_content'), + 'functionality' => new moodle_url('/admin/settings.php?section=theme_boost_union_functionality'), + 'flavours' => new moodle_url('/theme/boost_union/flavours/overview.php'), + 'smart menus' => new moodle_url('/theme/boost_union/smartmenus/menus.php'), + default => throw new Exception('Unrecognised theme_boost_union page "' . $page . '."') + }; + } + $suffix = trim($parts[1]); + $tabs = []; + switch ($section) { + case 'look': + $tabs = [ + 'general', + 'scss', + 'page', + 'sitebranding', + 'activitybranding', + 'loginpage', + 'dashboard', + 'blocks', + 'course', + 'emailbranding', + 'resources', + 'h5p', + 'mobile', + ]; + $suffix = match ($suffix) { + 'general', 'general settings' => 'general', + 'my courses', 'dashboard / my courses' => 'dashboard', + default => str_replace([' ', '-'], ['', ''], $suffix) + }; + break; + + case 'feel': + $tabs = [ + 'navigation', + 'blocks', + 'pagelayouts', + 'links', + 'misc', + ]; + $suffix = match ($suffix) { + 'miscellaneous' => 'misc', + default => str_replace([' ', '-'], ['', ''], $suffix) + }; + break; + + case 'content': + $tabs = [ + 'footer', + 'staticpages', + 'infobanner', + 'tiles', + 'slider', + ]; + $suffix = match ($suffix) { + 'advertisement tiles' => 'tiles', + default => str_replace([' ', '-'], ['', ''], $suffix) + }; + $section = match ($suffix) { + 'infobanner' => 'infobanners', + 'tiles', 'slider' => '', + default => $section, + }; + break; + + case 'functionality': + $tabs = [ + 'courses', + 'administration', + ]; + break; + + default: + throw new Exception('Unrecognised theme_boost_union page "' . $page . '."'); + } + + if (!in_array($suffix, $tabs)) { + throw new Exception('Unrecognised theme_boost_union page "' . $page . '."'); + } + return new moodle_url('/admin/settings.php?section=theme_boost_union_' . $section . '_' . $suffix); + } + + /** + * Convert page names to URLs for steps like 'When I am on the "[identifier]" "[page type]" page'. + * + * Recognised page names are: + * | pagetype | name meaning | description | + * | Flavour > Preview | Flavour title | The flavour preview page (flavours/preview.php) | + * | Flavour > Edit | Flavour title | The flavour edit page (flavours/edit.php) | + * | Smart menu > Edit | Menu title | The smart menu edit page (smartmenus/edit.php) | + * | Smart menu > Items | Menu title | The smart menu items page (smartmenus/items.php) | + * | Smart menu item | Menu title > Item title | The smart menu item edit page (smartmenus/edit_items.php) | + * | Course completion | Course identifier | The course completion form | + * + * @param string $type identifies which type of page this is, e.g. 'Smart menu item'. + * @param string $identifier identifies the particular page, e.g. 'Menu 1 > Item 1'. + * @return moodle_url the corresponding URL. + * @throws Exception with a meaningful error message if the specified page cannot be found. + */ + protected function resolve_page_instance_url(string $type, string $identifier): moodle_url { + $parts = explode('>', strtolower($type)); + $pagetype = trim($parts[0]); + + switch ($pagetype) { + case 'flavour': + $page = trim($parts[1]); + if (!in_array($page, ['preview', 'edit'])) { + throw new Exception('Unrecognised theme_boost_union page type "' . $type . '."'); + } + return new moodle_url( + '/theme/boost_union/flavours/' . $page . '.php', + [ + 'id' => $this->get_flavour_id_by_title($identifier), + 'sesskey' => $this->get_sesskey(), + ] + ); + + case 'smart menu': + $page = trim($parts[1]); + if (!in_array($page, ['edit', 'items'])) { + throw new Exception('Unrecognised theme_boost_union page type "' . $type . '."'); + } + $idparam = $page == 'edit' ? 'id' : 'menu'; + return new moodle_url( + '/theme/boost_union/smartmenus/' . $page . '.php', + [ + $idparam => $this->get_smartmenu_id_by_title($identifier), + 'sesskey' => $this->get_sesskey(), + ] + ); + + case 'smart menu item': + $idparts = explode('>', $identifier); + $menutitle = trim($idparts[0]); + $itemtitle = trim($idparts[1]); + $menuid = $this->get_smartmenu_id_by_title($menutitle); + return new moodle_url( + '/theme/boost_union/smartmenus/edit_items.php', + [ + 'id' => $this->get_smartmenu_item_id_by_title($menuid, $itemtitle), + 'sesskey' => $this->get_sesskey(), + ] + ); + + case 'course completion': + return new moodle_url( + '/course/completion.php', + [ + 'id' => $this->get_course_id($identifier), + ] + ); + + default: + throw new Exception('Unrecognised quiz page type "' . $type . '."'); + } + } + + /** + * Return named selectors for use with steps like `the following "locator" "identifier" should exist`. + * + * Supported selectors: + * - "Smart menu" - The menu button for a smart menu. Locator = Smart menu title. + * - "Smart menu item" - The menu button for a smart menu item. Locator = Item title. + * - "Main menu smart menu" - The submenu for a smart menu within the main menu. Locator = Smart menu title. + * - "Main menu smart menu item" - A smart menu item within the main menu. Locator = Item title. + * - "Menu bar smart menu" - The submenu for a smart menu within top menu bar. Locator = Smart menu title. + * - "Menu bar smart menu item" - A smart menu item within top menu bar. Locator = Item title. + * - "User menu smart menu" - The submenu for a smart menu within top user menu. Locator = Smart menu title. + * - "User menu smart menu item" - A smart menu item within a user menu submenu. Locator = Item title. + * - "Bottom bar smart menu" - The submenu for a smart menu within bottom menu bar. Locator = Smart menu title. + * - "Bottom bar smart menu item" - A smart menu item within bottom menu bar. Locator = Item title. + * + * @return array|behat_component_named_selector[] + */ + public static function get_exact_named_selectors(): array { + return [ + new behat_component_named_selector( + 'Smart menu', + [".//a[@role = 'menuitem'][contains(text(), %locator%)]"], + ), + new behat_component_named_selector( + 'Smart menu item', + [".//a[contains(@class, 'boost-union-smartitem')][contains(text(), %locator%)]"], + ), + new behat_component_named_selector( + 'Main menu smart menu', + [ + ".//div[contains(@class, 'primary-navigation')]//li[contains(@class, 'boost-union-smartmenu')]" . + "/a[contains(text(), %locator%)]/../div[@role = 'menu']", + ], + ), + new behat_component_named_selector( + 'Main menu smart menu item', + [ + ".//div[contains(@class, 'primary-navigation')]" . + "//a[contains(@class, 'boost-union-smartitem')][contains(text(), %locator%)]", + ], + ), + new behat_component_named_selector( + 'Menu bar smart menu', + [ + ".//nav[contains(@class, 'boost-union-menubar')]//li[contains(@class, 'boost-union-smartmenu')]" . + "/a[contains(text(), %locator%)]/../div[@role = 'menu']", + ], + ), + new behat_component_named_selector( + 'Menu bar smart menu item', + [ + ".//nav[contains(@class, 'boost-union-menubar')]" . + "//a[contains(@class, 'boost-union-smartitem')][contains(text(), %locator%)]", + ], + ), + new behat_component_named_selector( + 'User menu smart menu', + [ + ".//div[@id = 'usermenu-carousel']//div[contains(@class, 'carousel-item')][@aria-label = %locator%]", + ], + ), + new behat_component_named_selector( + 'User menu smart menu item', + [ + ".//div[@id = 'usermenu-carousel']//div[contains(@class, 'carousel-item')]" . + "//a[contains(@class, 'boost-union-smartitem')][contains(text(), %locator%)]", + ], + ), + new behat_component_named_selector( + 'Bottom bar smart menu', + [ + ".//nav[contains(@class, 'boost-union-bottom-menu')]//li[contains(@class, 'boost-union-smartmenu')]" . + "/a[contains(text(), %locator%)]/../div[@role = 'menu']"], + ), + new behat_component_named_selector( + 'Bottom bar smart menu item', + [ + ".//nav[contains(@class, 'boost-union-bottom-menu')]" . + "//a[contains(@class, 'boost-union-smartitem')][contains(text(), %locator%)]", + ], + ), + ]; + } + + /** + * Given the title of a Flavour, return the ID. + * + * @param string $title + * @return int + * @throws dml_exception + */ + protected function get_flavour_id_by_title(string $title): int { + global $DB; + $id = $DB->get_field('theme_boost_union_flavours', 'id', ['title' => $title]); + if (!$id) { + throw new Exception('Cannot find Boost Union flavour with title "' . $title . '"'); + } + return $id; + } + + /** + * Given the title of a Smart menu, return the ID. + * + * @param string $title + * @return int + * @throws dml_exception + */ + protected function get_smartmenu_id_by_title(string $title): int { + global $DB; + $id = $DB->get_field('theme_boost_union_menus', 'id', ['title' => $title]); + if (!$id) { + throw new Exception('Cannot find Boost Union smart menu with title "' . $title . '"'); + } + return $id; + } + + /** + * Given the menu ID and title of a Smart menu item, return the item ID. + * + * @param int $menuid + * @param string $title + * @return int + * @throws dml_exception + */ + protected function get_smartmenu_item_id_by_title(int $menuid, string $title): int { + global $DB; + $id = $DB->get_field('theme_boost_union_menuitems', 'id', ['menu' => $menuid, 'title' => $title]); + if (!$id) { + throw new Exception('Cannot find Boost Union smart menu item with title "' . $title . '"'); + } + return $id; + } +} diff --git a/tests/generator/behat_theme_boost_union_generator.php b/tests/generator/behat_theme_boost_union_generator.php new file mode 100644 index 00000000000..0f95c2236df --- /dev/null +++ b/tests/generator/behat_theme_boost_union_generator.php @@ -0,0 +1,479 @@ +. + +use theme_boost_union\smartmenu; +use theme_boost_union\smartmenu_item; + +/** + * Behat generator for Boost Union + * + * @package theme_boost_union + * @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net} + * @author Mark Johnson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_theme_boost_union_generator extends behat_generator_base { + /** + * Define entities that can be generated with the 'the following "x" exist(s):' steps. + * + * Currently supported entities: + * - "smart menus" - Requires a location, supports all options. + * - Most properties can be specified as they appear on the editing form. + * - "location", "roles", "cohorts" and "languages" support multiple values as a comma-separated list. + * - "roles" are specified by shortname, "cohorts" are specified by idnumber and "languages" are specified by + * short code (for example, en, de). + * - "smart menu items" - Requires a menu title, supports all options except file uploads. + * - Most properties can be specified as they appear on the editing form. + * - "menu" is the title of the Smart menu this item belongs to. + * - "categories", "completionstatus", "daterange", "customfields", "roles", "cohorts" and "languages" + * support multiple values as a comma-separated list. + * - "roles" are specified by shortname, "cohorts" are specified by idnumber and "languages" are specified by + * short code (for example, en, de). + * - "customfields" are specified in `name: value` format, where name is the name or shortname of the custom field. + * + * @return array[] + */ + protected function get_creatable_entities(): array { + return [ + 'smart menus' => [ + 'singular' => 'smart menu', + 'datagenerator' => 'smartmenu', + 'required' => ['location'], + 'switchids' => [ + 'location' => 'location', + 'showdescription' => 'showdescription', + 'type' => 'type', + 'mode' => 'mode', + 'moremenubehaviour' => 'moremenubehaviour', + 'cardform' => 'cardform', + 'cardsize' => 'cardsize', + 'cardoverflowbehaviour' => 'cardoverflowbehaviour', + 'roles' => 'roles', + 'rolecontext' => 'rolecontext', + 'cohorts' => 'cohorts', + 'operator' => 'operator', + 'languages' => 'languages', + ], + ], + + 'smart menu items' => [ + 'singular' => 'smart menu item', + 'datagenerator' => 'smartmenu_item', + 'required' => ['menu'], + 'switchids' => [ + 'menu' => 'menu', + 'itemtype' => 'type', + 'categories' => 'category', + 'enrolmentrole' => 'enrolmentrole', + 'completionstatus' => 'completionstatus', + 'daterange' => 'daterange', + 'customfields' => 'customfields', + 'listsort' => 'listsort', + 'displayfield' => 'displayfield', + 'itemmode' => 'mode', + 'display' => 'display', + 'target' => 'target', + 'textposition' => 'textposition', + 'roles' => 'roles', + 'rolecontext' => 'rolecontext', + 'cohorts' => 'cohorts', + 'operator' => 'operator', + 'languages' => 'languages', + ], + ], + ]; + } + + /** + * Return the ID of an identifier from an array of ids => identifiers. + * + * If the identifier does not appear in the array, throw an exception with a list of allowed values. + * + * @param string $name + * @param string $identifier + * @param array $options A list of allowed options, keyed by ID. + * @return int + */ + protected function option_id(string $name, string $identifier, array $options): int { + $id = array_search(trim($identifier), $options); + if ($id === false) { + throw new Exception("Invalid {$name}: '{$identifier}'. Allowed values are '" . + implode("', '", $options) . "'"); + } + return $id; + } + + /** + * Given a comma-separated list of location strings, return an array of location IDs. + * + * @param string $locations A comma-separated list of locations, must be strings from the smartmenu::get_locations() array. + * @return array + */ + protected function get_location_id(string $locations): array { + $locationoptions = smartmenu::get_locations(); + $locationids = []; + foreach (explode(',', $locations) as $location) { + $location = trim($location); + if (empty($location)) { + continue; + } + $locationid = array_search($location, $locationoptions); + if ($locationid === false) { + throw new Exception("Invalid location '{$location}'. Allowed locations are: " . implode(',', $locationoptions)); + } + $locationids[] = $locationid; + } + return $locationids; + } + + /** + * Return the ID for the given showdescription setting. + * + * @param string $showdescription + * @return int + * @throws Exception + */ + protected function get_showdescription_id(string $showdescription): int { + return $this->option_id('showdescription', $showdescription, smartmenu::get_showdescription_options()); + } + + /** + * Return the ID for the given menu type. + * + * @param string $type + * @return int + * @throws Exception + */ + protected function get_type_id(string $type): int { + return $this->option_id('type', $type, smartmenu::get_types()); + } + + /** + * Return the ID for the given menu mode. + * + * @param string $mode + * @return int + * @throws Exception + */ + protected function get_mode_id(string $mode): int { + return $this->option_id('mode', $mode, smartmenu::get_mode_options()); + } + + /** + * Return the ID for the given moremenubehavriour setting. + * + * @param string $moremenu + * @return int + * @throws Exception + */ + protected function get_moremenubehaviour_id(string $moremenu): int { + return $this->option_id('moremenu', $moremenu, smartmenu::get_moremenu_options()); + } + + /** + * Return the ID for the given cardsize setting. + * + * @param string $cardsize + * @return int + * @throws Exception + */ + protected function get_cardsize_id(string $cardsize): int { + return $this->option_id('cardsize', $cardsize, smartmenu::get_cardsize_options()); + } + + /** + * Return the ID for the given cardform setting. + * + * @param string $cardform + * @return int + * @throws Exception + */ + protected function get_cardform_id(string $cardform): int { + return $this->option_id('cardform', $cardform, smartmenu::get_cardform_options()); + } + + /** + * Return the ID for the given cardoverflowbehaviour setting. + * + * @param string $cardoverflowbehaviour + * @return int + * @throws Exception + */ + protected function get_cardoverflowbehaviour_id(string $cardoverflowbehaviour): int { + return $this->option_id('cardoverflowbehaviour', $cardoverflowbehaviour, smartmenu::get_cardoverflowbehaviour_options()); + } + + /** + * Given a comma-separated list of role shortnames, return an array of role IDs. + * + * @param string $roles + * @return array + * @throws Exception + */ + protected function get_roles_id(string $roles): array { + $roleids = []; + foreach (explode(',', $roles) as $shortname) { + $shortname = trim($shortname); + if (empty($shortname)) { + continue; + } + $roleids[] = $this->get_role_id(strtolower($shortname)); + } + return $roleids; + } + + /** + * Return the ID for the given rolecontext setting. + * + * @param string $rolecontext + * @return int + * @throws Exception + */ + protected function get_rolecontext_id(string $rolecontext): int { + return $this->option_id('rolecontext', $rolecontext, smartmenu::get_rolecontext_options()); + } + + /** + * Given a comma-separated list of cohort idnumbers, return an array of the cohort IDs. + * + * @param string $cohorts + * @return array + * @throws dml_exception + */ + protected function get_cohorts_id(string $cohorts): array { + global $DB; + $cohortids = []; + foreach (explode(',', $cohorts) as $idnumber) { + $idnumber = trim($idnumber); + if (empty($idnumber)) { + continue; + } + $cohortids[] = $DB->get_field('cohort', 'id', ['idnumber' => $idnumber]); + } + return $cohortids; + } + + /** + * Return the ID for the given operator setting. + * + * @param string $operator + * @return int + * @throws Exception + */ + protected function get_operator_id(string $operator): int { + return $this->option_id('operator', $operator, smartmenu::get_operator_options()); + } + + /** + * Given a comma-separated list of language short codes, return an array of each trimmed value. + * + * @param string $languages + * @return array + */ + protected function get_languages_id(string $languages): array { + return array_map('trim', explode(',', $languages)); + } + + /** + * Given the title of an existing Smart menu, return the menu ID. + * + * @param string $title + * @return int + * @throws dml_exception + */ + protected function get_menu_id(string $title): int { + global $DB; + $id = $DB->get_field('theme_boost_union_menus', 'id', ['title' => $title]); + if (!$id) { + throw new Exception('Menu not found with title ' . $title); + } + return $id; + } + + /** + * Return the ID for a given smart menu item type. + * + * @param string $type + * @return int + * @throws Exception + */ + protected function get_itemtype_id(string $type): int { + return $this->option_id('itemtype', $type, smartmenu_item::get_types()); + } + + /** + * Given a comma-separated list of course category ID numbers, return an array of the category IDs. + * + * @param string $categories + * @return array + * @throws dml_exception + */ + protected function get_categories_id(string $categories): array { + global $DB; + $categories = explode(',', $categories); + $categoryids = []; + foreach ($categories as $idnumber) { + $idnumber = trim($idnumber); + if (empty($idnumber)) { + continue; + } + $categoryids[] = $DB->get_field('course_categories', 'id', ['idnumber' => $idnumber]); + } + return $categoryids; + } + + /** + * Return role IDs for the enrolmentrole setting. {@see get_roles_id} + * + * @param string $roles + * @return array + * @throws Exception + */ + protected function get_enrolmentrole_id(string $roles): array { + return $this->get_roles_id($roles); + } + + /** + * Given a comma-separated list of completion status settings, return an array of completion status IDs. + * + * @param string $completionstatuses + * @return array + * @throws Exception + */ + protected function get_completionstatus_id(string $completionstatuses): array { + $completionstatusids = []; + $statuses = smartmenu_item::get_completionstatus_options(); + foreach (explode(',', $completionstatuses) as $completionstatus) { + $completionstatusids[] = $this->option_id('completionstatus', $completionstatus, $statuses); + } + return $completionstatusids; + } + + /** + * Given a comma-separated list of date range settings, return an array of date range IDs. + * + * @param string $dateranges + * @return array + * @throws Exception + */ + protected function get_daterange_id(string $dateranges): array { + $daterangeids = []; + $ranges = smartmenu_item::get_daterange_options(); + foreach (explode(',', $dateranges) as $daterange) { + $daterangeids[] = $this->option_id('daterange', $daterange, $ranges); + } + return $daterangeids; + } + + /** + * Return an array of custom field settings. + * + * Given a comma-separated list of `name: value` pairs for custom fields, return an array of [name => value]. + * + * @param string $customfields + * @return array + * @throws dml_exception + */ + protected function get_customfields_id(string $customfields): array { + global $DB; + $customfields = explode(',', $customfields); + $customfieldids = []; + foreach ($customfields as $customfield) { + if (empty(trim($customfield))) { + continue; + } + [$identifier, $value] = explode(':', $customfield); + $identifier = trim($identifier); + $shortname = $DB->get_field_select( + 'customfield_field', + 'shortname', + 'name = ? OR shortname = ?', + [$identifier, $identifier] + ); + if (!$shortname) { + throw new Exception('No custom field found with name or shortname ' . $identifier); + } + $customfieldids[$shortname] = trim($value); + } + return $customfieldids; + } + + /** + * Return the ID of the given listsort option. + * + * @param string $listsort + * @return int + * @throws Exception + */ + protected function get_listsort_id(string $listsort): int { + return $this->option_id('listsort', $listsort, smartmenu_item::get_listsort_options()); + } + + /** + * Return the ID of the given displayfield option. + * + * @param string $displayfield + * @return int + * @throws Exception + */ + protected function get_displayfield_id(string $displayfield): int { + return $this->option_id('displayfield', $displayfield, smartmenu_item::get_displayfield_options()); + } + + /** + * Return the ID of the given Smart menu item mode setting. + * + * @param string $mode + * @return int + * @throws Exception + */ + protected function get_itemmode_id(string $mode): int { + return $this->option_id('itemmode', $mode, smartmenu_item::get_mode_options()); + } + + /** + * Return the ID of the given display setting. + * + * @param string $display + * @return int + * @throws Exception + */ + protected function get_display_id(string $display): int { + return $this->option_id('display', $display, smartmenu_item::get_display_options()); + } + + /** + * Return the ID of the given target setting. + * + * @param string $target + * @return int + * @throws Exception + */ + protected function get_target_id(string $target): int { + return $this->option_id('target', $target, smartmenu_item::get_target_options()); + } + + /** + * Return the ID of the given textposition setting. + * + * @param string $textposition + * @return int + * @throws Exception + */ + protected function get_textposition_id(string $textposition): int { + return $this->option_id('textposition', $textposition, smartmenu_item::get_textposition_options()); + } +} diff --git a/tests/generator/lib.php b/tests/generator/lib.php new file mode 100644 index 00000000000..7fef482f2d2 --- /dev/null +++ b/tests/generator/lib.php @@ -0,0 +1,357 @@ +. + +use theme_boost_union\smartmenu; +use theme_boost_union\smartmenu_item; + +/** + * Boost Union test data generator + * + * @package theme_boost_union + * @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net} + * @author Mark Johnson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class theme_boost_union_generator extends component_generator_base { + /** + * Generate a smart menu. + * + * An array of one or more locations are required, which must match the smartmenu:LOCATION_* constants. + * All other properties are optional. + * + * @param array $data + * @return stdClass + */ + public function create_smartmenu(array $data): \stdClass { + global $DB; + $location = $data['location'] ?? []; + if (empty($location)) { + throw new Exception('A Smart menu location must be specified.'); + } + $validlocations = [ + smartmenu::LOCATION_MAIN, + smartmenu::LOCATION_MENU, + smartmenu::LOCATION_BOTTOM, + smartmenu::LOCATION_USER, + ]; + if (!empty(array_diff($validlocations, $location))) { + throw new Exception('Invalid Smart menu location.'); + } + $validdescriptions = [ + smartmenu::DESC_NEVER, + smartmenu::DESC_ABOVE, + smartmenu::DESC_BELOW, + smartmenu::DESC_HELP, + ]; + $showdescription = $data['showdescription'] ?? smartmenu::DESC_NEVER; + if (!in_array($showdescription, $validdescriptions)) { + throw new Exception('Invalid showdescription.'); + } + $validtypes = [ + smartmenu::TYPE_LIST, + smartmenu::TYPE_CARD, + ]; + $type = $data['type'] ?? smartmenu::TYPE_LIST; + if (!in_array($type, $validtypes)) { + throw new Exception('Invalid showdescription.'); + } + $validmodes = [ + smartmenu::MODE_SUBMENU, + smartmenu::MODE_INLINE, + ]; + $mode = $data['mode'] ?? smartmenu::MODE_SUBMENU; + if (!in_array($mode, $validmodes)) { + throw new Exception('Invalid mode.'); + } + $validbehaviours = [ + smartmenu::MOREMENU_DONOTCHANGE, + smartmenu::MOREMENU_INTO, + smartmenu::MOREMENU_OUTSIDE, + ]; + $moremenubehaviour = $data['moremenubehaviour'] ?? smartmenu::MOREMENU_DONOTCHANGE; + if (!in_array($moremenubehaviour, $validbehaviours)) { + throw new Exception('Invalid moremenubehaviour.'); + } + + $cardsize = null; + $cardform = null; + $cardoverflowbehaviour = null; + if ($type === smartmenu::TYPE_CARD) { + $validcardsizes = [ + smartmenu::CARDSIZE_TINY, + smartmenu::CARDSIZE_SMALL, + smartmenu::CARDSIZE_MEDIUM, + smartmenu::CARDSIZE_LARGE, + ]; + $cardsize = $data['cardsize'] ?? smartmenu::CARDSIZE_SMALL; + if (!in_array($cardsize, $validcardsizes)) { + throw new Exception('Invalid cardsize.'); + } + $validcardforms = [ + smartmenu::CARDFORM_SQUARE, + smartmenu::CARDFORM_PORTRAIT, + smartmenu::CARDFORM_LANDSCAPE, + smartmenu::CARDFORM_FULLWIDTH, + ]; + $cardform = $data['cardform'] ?? smartmenu::CARDFORM_SQUARE; + if (!in_array($cardform, $validcardforms)) { + throw new Exception('Invalid cardform.'); + } + $validbehaviours = [ + smartmenu::CARDOVERFLOWBEHAVIOUR_NOWRAP, + smartmenu::CARDOVERFLOWBEHAVIOUR_WRAP, + ]; + $cardoverflowbehaviour = strtolower($data['cardoverflowbehaviour']) ?? smartmenu::CARDOVERFLOWBEHAVIOUR_NOWRAP; + if (!in_array($cardoverflowbehaviour, $validbehaviours)) { + throw new Exception('Invalid cardoverflowbehaviour.'); + } + } + [ + $roles, + $rolecontext, + $cohorts, + $operator, + $languages, + $startdate, + $enddate, + ] = $this->parse_restrictions($data); + + $sortorder = $data['sortorder'] ?? $DB->count_records('theme_boost_union_menus') + 1; + $record = (object)[ + 'title' => $data['title'] ?? 'Smart menu ' . random_string(), + 'description' => $data['description'] ?? 'Smart menu description ' . random_string(), + 'description_format' => $data['description_format'] ?? FORMAT_HTML, + 'showdescription' => $showdescription, + 'sortorder' => $sortorder, + 'location' => json_encode($location), + 'type' => $type, + 'mode' => $mode, + 'cssclass' => $data['cssclass'] ?? null, + 'moremenubehaviour' => $moremenubehaviour, + 'cardsize' => $cardsize, + 'cardform' => $cardform, + 'cardoverflowbehaviour' => $cardoverflowbehaviour, + 'roles' => $roles, + 'rolecontext' => $rolecontext, + 'cohorts' => $cohorts, + 'operator' => $operator, + 'languages' => $languages, + 'start_date' => $startdate, + 'end_date' => $enddate, + 'visible' => $data['visible'] ?? 1, + ]; + $record->id = $DB->insert_record('theme_boost_union_menus', $record); + return $record; + } + + /** + * Generate a Smart menu item. + * + * The ID of the parent menu must be specified. All other properties are optional. + * + * @param array $data + * @return stdClass + */ + public function create_smartmenu_item(array $data): \stdClass { + global $DB; + + if (!$DB->record_exists('theme_boost_union_menus', ['id' => $data['menu']])) { + throw new Exception('Menu not found with id ' . $data['menu']); + } + + $sortorder = $data['sortorder'] ?? $DB->count_records('theme_boost_union_menus') + 1; + + $validtypes = [ + smartmenu_item::TYPESTATIC, + smartmenu_item::TYPEHEADING, + smartmenu_item::TYPEDYNAMIC, + ]; + $type = $data['type'] ?? smartmenu_item::TYPESTATIC; + if (!in_array($type, $validtypes)) { + throw new Exception('Invalid type.'); + } + + $url = null; + if ($type === smartmenu_item::TYPESTATIC) { + if (empty($data['url'])) { + throw new Exception('URL is required when type is static.'); + } + $url = $data['url']; + } + + $category = json_encode($data['category'] ?? []); + $categorysubcats = $data['category_subcats'] ?? false; + $enrolmentrole = json_encode($data['enrolmentrole'] ?? []); + $completionstatus = json_encode($data['completionstatus'] ?? []); + $daterange = json_encode($data['daterange'] ?? []); + $customfields = json_encode($data['customfields'] ?? new stdClass()); + $listsort = null; + $displayfield = null; + $textcount = null; + if ($type == smartmenu_item::TYPEDYNAMIC) { + $validsorts = [ + smartmenu_item::LISTSORT_FULLNAME_ASC, + smartmenu_item::LISTSORT_FULLNAME_DESC, + smartmenu_item::LISTSORT_SHORTNAME_ASC, + smartmenu_item::LISTSORT_SHORTNAME_DESC, + smartmenu_item::LISTSORT_COURSEID_ASC, + smartmenu_item::LISTSORT_COURSEID_DESC, + smartmenu_item::LISTSORT_COURSEIDNUMBER_ASC, + smartmenu_item::LISTSORT_COURSEIDNUMBER_DESC, + ]; + $listsort = $data['listsort'] ?? smartmenu_item::LISTSORT_FULLNAME_ASC; + if (!in_array($listsort, $validsorts)) { + throw new Exception('Invalid listsort.'); + } + $validdisplayfields = [ + smartmenu_item::FIELD_FULLNAME, + smartmenu_item::FIELD_SHORTNAME, + ]; + $displayfield = $data['displayfield'] ?? smartmenu_item::FIELD_FULLNAME; + if (!in_array($displayfield, $validdisplayfields)) { + throw new Exception('Invalid displayfield.'); + } + $textcount = $data['textcount'] ?? null; + } + + $validmodes = [ + smartmenu_item::MODE_INLINE, + smartmenu_item::MODE_SUBMENU, + ]; + $mode = $data['mode'] ?? smartmenu_item::MODE_INLINE; + if (!in_array($mode, $validmodes)) { + throw new Exception('Invalid mode.'); + } + + $validdisplays = [ + smartmenu_item::DISPLAY_SHOWTITLEICON, + smartmenu_item::DISPLAY_HIDETITLE, + smartmenu_item::DISPLAY_HIDETITLEMOBILE, + ]; + $display = $data['display'] ?? smartmenu_item::DISPLAY_SHOWTITLEICON; + if (!in_array($display, $validdisplays)) { + throw new Exception('Invalid display.'); + } + + $validtargets = [ + smartmenu_item::TARGET_SAME, + smartmenu_item::TARGET_NEW, + ]; + $target = $data['target'] ?? smartmenu_item::TARGET_SAME; + if (!in_array($target, $validtargets)) { + throw new Exception('Invalid target.'); + } + + $validtextpositions = [ + smartmenu_item::POSITION_BELOW, + smartmenu_item::POSITION_OVERLAYTOP, + smartmenu_item::POSITION_OVERLAYBOTTOM, + ]; + $textposition = $data['textposition'] ?? smartmenu_item::POSITION_BELOW; + if (!in_array($textposition, $validtextpositions)) { + throw new Exception('Invalid text position.'); + } + + [ + $roles, + $rolecontext, + $cohorts, + $operator, + $languages, + $startdate, + $enddate, + ] = $this->parse_restrictions($data); + + $record = (object)[ + 'title' => $data['title'] ?? 'Smart menu item ' . random_string(), + 'menu' => $data['menu'], + 'type' => $type, + 'sortorder' => $sortorder, + 'url' => $url, + 'category' => $category, + 'category_subcats' => $categorysubcats, + 'enrolmentrole' => $enrolmentrole, + 'completionstatus' => $completionstatus, + 'daterange' => $daterange, + 'customfields' => $customfields, + 'listsort' => $listsort, + 'displayfield' => $displayfield, + 'textcount' => $textcount, + 'mode' => $mode, + 'menuicon' => $data['menuicon'] ?? null, + 'display' => $display, + 'tooltip' => $data['tooltip'] ?? null, + 'target' => $target, + 'cssclass' => $data['cssclass'] ?? null, + 'textposition' => $textposition, + 'textcolor' => $data['textcolor'] ?? null, + 'backgroundcolor' => $data['backgroundcolor'] ?? null, + 'desktop' => $data['desktop'] ?? 0, + 'tablet' => $data['tablet'] ?? 0, + 'mobile' => $data['mobile'] ?? 0, + 'roles' => $roles, + 'rolecontext' => $rolecontext, + 'cohorts' => $cohorts, + 'operator' => $operator, + 'languages' => $languages, + 'start_date' => $startdate, + 'end_date' => $enddate, + 'visible' => $data['visible'] ?? 1, + ]; + $record->id = $DB->insert_record('theme_boost_union_menuitems', $record); + return $record; + } + + /** + * Parse the provided restriction fields to ensure they are valid and appropriately encoded. + * + * @param array $data + * @return array + * @throws Exception + */ + protected function parse_restrictions(array $data): array { + $roles = $data['roles'] ?? []; + $rolecontext = null; + if (!empty($roles)) { + $validcontexts = array_keys(smartmenu::get_rolecontext_options()); + $rolecontext = $data['rolecontext'] ?? smartmenu::ANYCONTEXT; + if (!in_array($rolecontext, $validcontexts)) { + throw new Exception('Invalid rolecontext.'); + } + } + $cohorts = $data['cohorts'] ?? []; + $operator = null; + if (!empty($cohorts)) { + $validoperators = array_keys(smartmenu::get_operator_options()); + $operator = $data['operator'] ?? smartmenu::ANY; + if (!in_array($operator, $validoperators)) { + throw new Exception('Invalid operator.'); + } + } + $languages = $data['languages'] ?? []; + $startdate = $data['start_date'] ?? 0; + $enddate = $data['end_date'] ?? 0; + return [ + json_encode($roles), + $rolecontext, + json_encode($cohorts), + $operator, + json_encode($languages), + $startdate, + $enddate, + ]; + } +}