Skip to content

Commit

Permalink
Improvement: Configurable sort order in menu items of smart menu, sol…
Browse files Browse the repository at this point in the history
  • Loading branch information
abias authored and lucaboesch committed Dec 20, 2023
1 parent d116787 commit 781853a
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Changes

### Unreleased

* 2023-12-11 - Improvement: Configurable sort order in menu items of smart menu, solves #403.
* 2023-12-10 - Feature: Allow the admin to hide the manual login form and the IDP login intro, solves #490.
* 2023-12-10 - Improvement: Allow the admin to change the look of the course overview block, solves #204

Expand Down
27 changes: 27 additions & 0 deletions classes/form/smartmenu_item_edit_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,33 @@ public function definition() {
$mform->setType('cssclass', PARAM_TEXT);
$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'),
];
$mform->addElement('select', 'listsort',
get_string('smartmenusmenuitemtypedynamiccourses', 'theme_boost_union').': '.
get_string('smartmenusmenuitemlistsort', 'theme_boost_union'), $listsortoptions);
$mform->setDefault('listsort', smartmenu_item::LISTSORT_FULLNAME_ASC);
$mform->setType('listsort', PARAM_INT);
$mform->hideIf('listsort', 'type', 'neq', smartmenu_item::TYPEDYNAMIC);
$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'),
Expand Down
99 changes: 95 additions & 4 deletions classes/smartmenu_item.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,54 @@ class smartmenu_item {
*/
const FIELD_FULLNAME = 0;

/**
* Sort the course list alphabetically by fullname ascending for dynamic menu item.
* @var int
*/
const LISTSORT_FULLNAME_ASC = 0;

/**
* Sort the course list alphabetically by fullname descending for dynamic menu item.
* @var int
*/
const LISTSORT_FULLNAME_DESC = 1;

/**
* Sort the course list alphabetically by shortname ascending for dynamic menu item.
* @var int
*/
const LISTSORT_SHORTNAME_ASC = 2;

/**
* Sort the course list alphabetically by shortname descending for dynamic menu item.
* @var int
*/
const LISTSORT_SHORTNAME_DESC = 3;

/**
* Sort the course list numerically by course-id ascending for dynamic menu item.
* @var int
*/
const LISTSORT_COURSEID_ASC = 4;

/**
* Sort the course list numerically by course-id descending for dynamic menu item.
* @var int
*/
const LISTSORT_COURSEID_DESC = 5;

/**
* Sort the course list alphabetically by course idnumber ascending for dynamic menu item.
* @var int
*/
const LISTSORT_COURSEIDNUMBER_ASC = 6;

/**
* Sort the course list alphabetically by course idnumber descending for dynamic menu item.
* @var int
*/
const LISTSORT_COURSEIDNUMBER_DESC = 7;

/**
* The ID of the menu item.
* @var int
Expand Down Expand Up @@ -641,8 +689,11 @@ protected function generate_dynamic_item() {
$sql = " SELECT $select FROM {course} c $join";
$sql .= $where ? " WHERE $where " : '';

// Sort the courses in ascending order by its display field.
$sql .= ($this->item->displayfield == self::FIELD_SHORTNAME) ? " ORDER BY c.shortname ASC " : " ORDER BY c.fullname ASC ";
// Sort the courses in ascending order by its ID.
// The real list sorting is done later as we have to handle multilanguage strings
// which is not possible in SQL.
// Sorting by ID here is just to make cases where two courses have exactly identical names deterministic.
$sql .= " ORDER BY c.id ASC ";

// Fetch the course records based on the sql.
$records = $DB->get_records_sql($sql, $params);
Expand All @@ -662,10 +713,48 @@ protected function generate_dynamic_item() {
$coursename = ($this->item->displayfield == self::FIELD_SHORTNAME) ? $record->shortname : $record->fullname;
// Short the course text name. used custom end (2) dots instead of three dots to display more words from coursenames.
$coursename = ($this->item->textcount) ? $this->shorten_words($coursename, $this->item->textcount) : $coursename;
// Store the string which should be used for sorting within the item.
switch ($this->item->listsort) {
case self::LISTSORT_FULLNAME_ASC:
case self::LISTSORT_FULLNAME_DESC:
default:
$sortstring = $record->fullname;
break;
case self::LISTSORT_SHORTNAME_ASC:
case self::LISTSORT_SHORTNAME_DESC:
$sortstring = $record->shortname;
break;
case self::LISTSORT_COURSEID_ASC:
case self::LISTSORT_COURSEID_DESC:
$sortstring = $record->id;
break;
case self::LISTSORT_COURSEIDNUMBER_ASC:
case self::LISTSORT_COURSEIDNUMBER_DESC:
$sortstring = $record->idnumber;
break;
}

$items[] = $this->generate_node_data($coursename, $url, $rkey, null, 'link', false, [], $itemimage);
$items[] = $this->generate_node_data($coursename, $url, $rkey, null, 'link', false, [], $itemimage, $sortstring);
}

// Sort the courses based on the configured setting.
$listsort = $this->item->listsort;
usort($items, function($course1, $course2) use ($listsort) {
switch ($listsort) {
case self::LISTSORT_FULLNAME_ASC:
case self::LISTSORT_SHORTNAME_ASC:
case self::LISTSORT_COURSEID_ASC:
case self::LISTSORT_COURSEIDNUMBER_ASC:
default:
return strnatcasecmp($course1['sortstring'], $course2['sortstring']);
case self::LISTSORT_FULLNAME_DESC:
case self::LISTSORT_SHORTNAME_DESC:
case self::LISTSORT_COURSEID_DESC:
case self::LISTSORT_COURSEIDNUMBER_DESC:
return strnatcasecmp($course2['sortstring'], $course1['sortstring']);
}
});

// Submenu only contains the title as separate node.
if ($this->item->mode == self::MODE_SUBMENU) {
$haschildren = (count($items) > 0 ) ? true : false;
Expand Down Expand Up @@ -997,11 +1086,12 @@ public function build() {
* @param int $haschildren Whether the item has children or not, defaults to 0.
* @param array $children An array of child nodes, defaults to an empty array.
* @param string $itemimage Card image url for item.
* @param string $sortstring The string to be used for sorting the items.
*
* @return array An associative array of node data for the item.
*/
public function generate_node_data($title, $url, $key=null, $tooltip=null,
$itemtype='link', $haschildren=0, $children=[], $itemimage='') {
$itemtype='link', $haschildren=0, $children=[], $itemimage='', $sortstring='') {

global $OUTPUT;

Expand Down Expand Up @@ -1048,6 +1138,7 @@ public function generate_node_data($title, $url, $key=null, $tooltip=null,
'itemtype' => 'link',
'link' => 1,
'sort' => uniqid(), // Support third level menu.
'sortstring' => format_string($sortstring),
];

if ($haschildren && !empty($children)) {
Expand Down
1 change: 1 addition & 0 deletions db/install.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
<FIELD NAME="completionstatus" TYPE="char" LENGTH="20" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="daterange" TYPE="char" LENGTH="20" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="customfields" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="listsort" TYPE="int" LENGTH="9" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="displayfield" TYPE="int" LENGTH="9" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="textcount" TYPE="int" LENGTH="9" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="mode" TYPE="int" LENGTH="9" NOTNULL="true" SEQUENCE="false"/>
Expand Down
16 changes: 16 additions & 0 deletions db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,5 +262,21 @@ function xmldb_theme_boost_union_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2023090100, 'theme', 'boost_union');
}

if ($oldversion < 2023102008) {
// Define table theme_boost_union_menus to be altered.
$table = new xmldb_table('theme_boost_union_menuitems');

// Define field listsort to be added to theme_boost_union_menuitems.
$field = new xmldb_field('listsort', XMLDB_TYPE_INTEGER, '9', null, null, null, null, 'customfields');

// Conditionally launch add field listsort.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

// Boost_union savepoint reached.
upgrade_plugin_savepoint(true, 2023102008, 'theme', 'boost_union');
}

return true;
}
10 changes: 10 additions & 0 deletions lang/en/theme_boost_union.php
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,16 @@
$string['smartmenusmenuitemcssclass_help'] = 'Enter a CSS class for the menu item. This can be used to apply custom styling to the menu item.';
$string['smartmenusmenuitemdeleteconfirm'] = 'Are you sure you want to delete this menu item from the smart menu?';
$string['smartmenusmenuitemdeletesuccess'] = 'Smart menu item deleted successfully';
$string['smartmenusmenuitemlistsort'] = 'Course list sorting';
$string['smartmenusmenuitemlistsort_help'] = 'The course list will be sorted by the selected criteria and sort order. Choose between fullname, shortname, course ID and course ID number as criteria in combination with ascending and descending sort order.';
$string['smartmenusmenuitemlistsortfullnameasc'] = 'Course fullname ascending';
$string['smartmenusmenuitemlistsortfullnamedesc'] = 'Course fullname descending';
$string['smartmenusmenuitemlistsortshortnameasc'] = 'Course shortname ascending';
$string['smartmenusmenuitemlistsortshortnamedesc'] = 'Course shortname descending';
$string['smartmenusmenuitemlistsortcourseidasc'] = 'Course ID ascending';
$string['smartmenusmenuitemlistsortcourseiddesc'] = 'Course ID descending';
$string['smartmenusmenuitemlistsortcourseidnumberasc'] = 'Course ID number ascending';
$string['smartmenusmenuitemlistsortcourseidnumberdesc'] = 'Course ID number descending';
$string['smartmenusmenuitemdisplayfield'] = 'Course name presentation';
$string['smartmenusmenuitemdisplayfield_help'] = 'The course name which will be used as the title of the dynamic courses menu items. Choose between course full name and course short name';
$string['smartmenusmenuitemdisplayfieldcoursefullname'] = 'Course full name';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Feature: Configuring the theme_boost_union plugin on the "Smart menus" page, usi
And I create smart menu with the following fields to these values:
| Title | List menu |
| Menu location(s) | Main, Menu, User, Bottom |
| CSS class | dynamiccoursetest |
And I set "List menu" smart menu items with the following fields to these values:
| Title | Dynamic courses |
| Menu item type | Dynamic courses |
Expand Down Expand Up @@ -225,3 +226,40 @@ Feature: Configuring the theme_boost_union plugin on the "Smart menus" page, usi
| value | user | course1 | course2 | course3 | course4 |
| value1 | student1 | should | should | should not | should not |
| value2 | student1 | should not | should not | should | should not |

@javascript
Scenario Outline: Smartmenus: Menu items: Dynamic courses - Sort the course list based on the given setting
Given the following "courses" exist:
| fullname | shortname | category | idnumber |
| AAA Course | BBB | CAT1 | CCC |
| BBB Course | AAA | CAT1 | BBB |
| CCC Course | CCC | CAT1 | AAA |
When I log in as "admin"
And I navigate to smart menu "List menu" items
And I click on ".action-edit" "css_element" in the "Dynamic courses" "table_row"
And I set the field "Dynamic courses: Course list sorting" to "<sorting>"
And I press "Save changes"
And I log out
When I log in as "student1"
And I click on ".dynamiccoursetest" "css_element"
Then "<thisbeforethat1>" "text" should appear before "<thisbeforethat2>" "text" in the ".dynamiccoursetest .dropdown-menu" "css_element"
And "<thisbeforethat2>" "text" should appear before "<thisbeforethat3>" "text" in the ".dynamiccoursetest .dropdown-menu" "css_element"

Examples:
| sorting | thisbeforethat1 | thisbeforethat2 | thisbeforethat3 |
# Option: Course fullname ascending
| 0 | AAA Course | BBB Course | CCC Course |
# Option: Course fullname descending
| 1 | CCC Course | BBB Course | AAA Course |
# Option: Course shortname ascending
| 2 | BBB Course | AAA Course | CCC Course |
# Option: Course shortname descending
| 3 | CCC Course | AAA Course | BBB Course |
# Option: Course ID ascending
| 4 | AAA Course | BBB Course | CCC Course |
# Option: Course ID descending
| 5 | CCC Course | BBB Course | AAA Course |
# Option: Course ID number ascending
| 6 | CCC Course | BBB Course | AAA Course |
# Option: Course ID number descending
| 7 | AAA Course | BBB Course | CCC Course |
2 changes: 1 addition & 1 deletion version.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
defined('MOODLE_INTERNAL') || die();

$plugin->component = 'theme_boost_union';
$plugin->version = 2023102007;
$plugin->version = 2023102008;
$plugin->release = 'v4.3-r3';
$plugin->requires = 2023100900;
$plugin->supported = [403, 403];
Expand Down

0 comments on commit 781853a

Please sign in to comment.