mirror of
https://github.com/moodle/moodle.git
synced 2025-04-13 12:32:08 +02:00
MDL-66017 block_myoverview: filter by custom course field
This commit is contained in:
parent
a31719f91a
commit
8d166d7745
2
blocks/myoverview/amd/build/view.min.js
vendored
2
blocks/myoverview/amd/build/view.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
blocks/myoverview/amd/build/view_nav.min.js
vendored
2
blocks/myoverview/amd/build/view_nav.min.js
vendored
@ -1,2 +1,2 @@
|
||||
define ("block_myoverview/view_nav",["jquery","core/custom_interaction_events","block_myoverview/repository","block_myoverview/view","block_myoverview/selectors"],function(a,b,c,d,f){var g={FILTERS:"[data-region=\"filter\"]",FILTER_OPTION:"[data-filter]",DISPLAY_OPTION:"[data-display-option]"},h=function(a,b){var d=null;if("display"==a){d="block_myoverview_user_view_preference"}else if("sort"==a){d="block_myoverview_user_sort_preference"}else{d="block_myoverview_user_grouping_preference"}c.updateUserPreferences({preferences:[{type:d,value:b}]})},i=function(c){var e=c.find(g.FILTERS);b.define(e,[b.events.activate]);e.on(b.events.activate,g.FILTER_OPTION,function(b,e){var g=a(b.target);if(g.hasClass("active")){return}var i=g.attr("data-filter"),j=g.attr("data-pref");c.find(f.courseView.region).attr("data-"+i,g.attr("data-value"));h(i,j);d.init(c);e.originalEvent.preventDefault()});b.define(e,[b.events.activate]);e.on(b.events.activate,g.DISPLAY_OPTION,function(b,e){var g=a(b.target);if(g.hasClass("active")){return}var i=g.attr("data-display-option"),j=g.attr("data-pref");c.find(f.courseView.region).attr("data-display",g.attr("data-value"));h(i,j);d.reset(c);e.originalEvent.preventDefault()})};return{init:function init(b){b=a(b);i(b)}}});
|
||||
define ("block_myoverview/view_nav",["jquery","core/custom_interaction_events","block_myoverview/repository","block_myoverview/view","block_myoverview/selectors"],function(a,b,c,d,f){var g={FILTERS:"[data-region=\"filter\"]",FILTER_OPTION:"[data-filter]",DISPLAY_OPTION:"[data-display-option]"},h=function(a,b){var d=null;if("display"==a){d="block_myoverview_user_view_preference"}else if("sort"==a){d="block_myoverview_user_sort_preference"}else if("customfieldvalue"==a){d="block_myoverview_user_grouping_customfieldvalue_preference"}else{d="block_myoverview_user_grouping_preference"}c.updateUserPreferences({preferences:[{type:d,value:b}]})},i=function(c){var e=c.find(g.FILTERS);b.define(e,[b.events.activate]);e.on(b.events.activate,g.FILTER_OPTION,function(b,e){var g=a(b.target);if(g.hasClass("active")){return}var i=g.attr("data-filter"),j=g.attr("data-pref"),k=g.attr("data-customfieldvalue");c.find(f.courseView.region).attr("data-"+i,g.attr("data-value"));h(i,j);if(k){c.find(f.courseView.region).attr("data-customfieldvalue",k);h("customfieldvalue",k)}d.init(c);e.originalEvent.preventDefault()});b.define(e,[b.events.activate]);e.on(b.events.activate,g.DISPLAY_OPTION,function(b,e){var g=a(b.target);if(g.hasClass("active")){return}var i=g.attr("data-display-option"),j=g.attr("data-pref");c.find(f.courseView.region).attr("data-display",g.attr("data-value"));h(i,j);d.reset(c);e.originalEvent.preventDefault()})};return{init:function init(b){b=a(b);i(b)}}});
|
||||
//# sourceMappingURL=view_nav.min.js.map
|
||||
|
File diff suppressed because one or more lines are too long
@ -102,6 +102,8 @@ function(
|
||||
grouping: courseRegion.attr('data-grouping'),
|
||||
sort: courseRegion.attr('data-sort'),
|
||||
displaycategories: courseRegion.attr('data-displaycategories'),
|
||||
customfieldname: courseRegion.attr('data-customfieldname'),
|
||||
customfieldvalue: courseRegion.attr('data-customfieldvalue'),
|
||||
};
|
||||
};
|
||||
|
||||
@ -126,7 +128,9 @@ function(
|
||||
offset: courseOffset,
|
||||
limit: limit,
|
||||
classification: filters.grouping,
|
||||
sort: filters.sort
|
||||
sort: filters.sort,
|
||||
customfieldname: filters.customfieldname,
|
||||
customfieldvalue: filters.customfieldvalue
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -55,6 +55,8 @@ function(
|
||||
type = 'block_myoverview_user_view_preference';
|
||||
} else if (filter == 'sort') {
|
||||
type = 'block_myoverview_user_sort_preference';
|
||||
} else if (filter == 'customfieldvalue') {
|
||||
type = 'block_myoverview_user_grouping_customfieldvalue_preference';
|
||||
} else {
|
||||
type = 'block_myoverview_user_grouping_preference';
|
||||
}
|
||||
@ -92,10 +94,16 @@ function(
|
||||
|
||||
var filter = option.attr('data-filter');
|
||||
var pref = option.attr('data-pref');
|
||||
var customfieldvalue = option.attr('data-customfieldvalue');
|
||||
|
||||
root.find(Selectors.courseView.region).attr('data-' + filter, option.attr('data-value'));
|
||||
updatePreferences(filter, pref);
|
||||
|
||||
if (customfieldvalue) {
|
||||
root.find(Selectors.courseView.region).attr('data-customfieldvalue', customfieldvalue);
|
||||
updatePreferences('customfieldvalue', customfieldvalue);
|
||||
}
|
||||
|
||||
// Reset the views.
|
||||
View.init(root);
|
||||
|
||||
|
@ -53,8 +53,9 @@ class block_myoverview extends block_base {
|
||||
$sort = get_user_preferences('block_myoverview_user_sort_preference');
|
||||
$view = get_user_preferences('block_myoverview_user_view_preference');
|
||||
$paging = get_user_preferences('block_myoverview_user_paging_preference');
|
||||
$customfieldvalue = get_user_preferences('block_myoverview_user_grouping_customfieldvalue_preference');
|
||||
|
||||
$renderable = new \block_myoverview\output\main($group, $sort, $view, $paging);
|
||||
$renderable = new \block_myoverview\output\main($group, $sort, $view, $paging, $customfieldvalue);
|
||||
$renderer = $this->page->get_renderer('block_myoverview');
|
||||
|
||||
$this->content = new stdClass();
|
||||
|
@ -130,6 +130,27 @@ class main implements renderable, templatable {
|
||||
*/
|
||||
private $displaygroupinghidden;
|
||||
|
||||
/**
|
||||
* Store a course grouping option setting.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $displaygroupingcustomfield;
|
||||
|
||||
/**
|
||||
* Store the custom field used by customfield grouping.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $customfiltergrouping;
|
||||
|
||||
/**
|
||||
* Store the selected custom field value to group by.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $customfieldvalue;
|
||||
|
||||
/**
|
||||
* main constructor.
|
||||
* Initialize the user preferences
|
||||
@ -137,10 +158,12 @@ class main implements renderable, templatable {
|
||||
* @param string $grouping Grouping user preference
|
||||
* @param string $sort Sort user preference
|
||||
* @param string $view Display user preference
|
||||
* @param int $paging
|
||||
* @param string $customfieldvalue
|
||||
*
|
||||
* @throws \dml_exception
|
||||
*/
|
||||
public function __construct($grouping, $sort, $view, $paging) {
|
||||
public function __construct($grouping, $sort, $view, $paging, $customfieldvalue = null) {
|
||||
// Get plugin config.
|
||||
$config = get_config('block_myoverview');
|
||||
|
||||
@ -153,28 +176,14 @@ class main implements renderable, templatable {
|
||||
// Otherwise fall back to another grouping in a reasonable order.
|
||||
// This is done to prevent one-time UI glitches in the case when a user has chosen a grouping option previously which
|
||||
// was then disabled by the admin in the meantime.
|
||||
} else if ($config->displaygroupingall == true) {
|
||||
$this->grouping = BLOCK_MYOVERVIEW_GROUPING_ALL;
|
||||
} else if ($config->displaygroupingallincludinghidden == true) {
|
||||
$this->grouping = BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
|
||||
} else if ($config->displaygroupinginprogress == true) {
|
||||
$this->grouping = BLOCK_MYOVERVIEW_GROUPING_INPROGRESS;
|
||||
} else if ($config->displaygroupingfuture == true) {
|
||||
$this->grouping = BLOCK_MYOVERVIEW_GROUPING_FUTURE;
|
||||
} else if ($config->displaygroupingpast == true) {
|
||||
$this->grouping = BLOCK_MYOVERVIEW_GROUPING_PAST;
|
||||
} else if ($config->displaygroupingstarred == true) {
|
||||
$this->grouping = BLOCK_MYOVERVIEW_GROUPING_FAVOURITES;
|
||||
} else if ($config->displaygroupinghidden == true) {
|
||||
$this->grouping = BLOCK_MYOVERVIEW_GROUPING_HIDDEN;
|
||||
|
||||
// In this case, no grouping option is enabled and the grouping is not needed at all.
|
||||
// But it's better not to leave $this->grouping unset for any unexpected case.
|
||||
} else {
|
||||
$this->grouping = BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
|
||||
$this->grouping = $this->get_fallback_grouping($config);
|
||||
}
|
||||
unset ($groupingconfigname);
|
||||
|
||||
// Remember which custom field value we were using, if grouping by custom field.
|
||||
$this->customfieldvalue = $customfieldvalue;
|
||||
|
||||
// Check and remember the given sorting.
|
||||
$this->sort = $sort ? $sort : BLOCK_MYOVERVIEW_SORTING_TITLE;
|
||||
|
||||
@ -207,6 +216,8 @@ class main implements renderable, templatable {
|
||||
$this->displaygroupingpast = $config->displaygroupingpast;
|
||||
$this->displaygroupingstarred = $config->displaygroupingstarred;
|
||||
$this->displaygroupinghidden = $config->displaygroupinghidden;
|
||||
$this->displaygroupingcustomfield = ($config->displaygroupingcustomfield && $config->customfiltergrouping);
|
||||
$this->customfiltergrouping = $config->customfiltergrouping;
|
||||
|
||||
// Check and remember if the grouping selector should be shown at all or not.
|
||||
// It will be shown if more than 1 grouping option is enabled.
|
||||
@ -218,7 +229,7 @@ class main implements renderable, templatable {
|
||||
$this->displaygroupingstarred,
|
||||
$this->displaygroupinghidden);
|
||||
$displaygroupingselectorscount = count(array_filter($displaygroupingselectors));
|
||||
if ($displaygroupingselectorscount > 1) {
|
||||
if ($displaygroupingselectorscount > 1 || $this->displaygroupingcustomfield) {
|
||||
$this->displaygroupingselector = true;
|
||||
} else {
|
||||
$this->displaygroupingselector = false;
|
||||
@ -226,6 +237,41 @@ class main implements renderable, templatable {
|
||||
unset ($displaygroupingselectors, $displaygroupingselectorscount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the most sensible fallback grouping to use (in cases where the stored selection
|
||||
* is no longer available).
|
||||
* @param object $config
|
||||
* @return string
|
||||
*/
|
||||
private function get_fallback_grouping($config) {
|
||||
if ($config->displaygroupingall == true) {
|
||||
return BLOCK_MYOVERVIEW_GROUPING_ALL;
|
||||
}
|
||||
if ($config->displaygroupingallincludinghidden == true) {
|
||||
return BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
|
||||
}
|
||||
if ($config->displaygroupinginprogress == true) {
|
||||
return BLOCK_MYOVERVIEW_GROUPING_INPROGRESS;
|
||||
}
|
||||
if ($config->displaygroupingfuture == true) {
|
||||
return BLOCK_MYOVERVIEW_GROUPING_FUTURE;
|
||||
}
|
||||
if ($config->displaygroupingpast == true) {
|
||||
return BLOCK_MYOVERVIEW_GROUPING_PAST;
|
||||
}
|
||||
if ($config->displaygroupingstarred == true) {
|
||||
return BLOCK_MYOVERVIEW_GROUPING_FAVOURITES;
|
||||
}
|
||||
if ($config->displaygroupinghidden == true) {
|
||||
return BLOCK_MYOVERVIEW_GROUPING_HIDDEN;
|
||||
}
|
||||
if ($config->displaygroupingcustomfield == true) {
|
||||
return BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD;
|
||||
}
|
||||
// In this case, no grouping option is enabled and the grouping is not needed at all.
|
||||
// But it's better not to leave $this->grouping unset for any unexpected case.
|
||||
return BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the available layouts based on the config table settings,
|
||||
@ -292,6 +338,49 @@ class main implements renderable, templatable {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of values to add to the grouping dropdown
|
||||
* @return object[] containing name, value and active fields
|
||||
*/
|
||||
public function get_customfield_values_for_export() {
|
||||
global $DB, $USER;
|
||||
if (!$this->displaygroupingcustomfield) {
|
||||
return [];
|
||||
}
|
||||
$fieldid = $DB->get_field('customfield_field', 'id', ['shortname' => $this->customfiltergrouping]);
|
||||
if (!$fieldid) {
|
||||
return [];
|
||||
}
|
||||
$courses = enrol_get_all_users_courses($USER->id, true);
|
||||
if (!$courses) {
|
||||
return [];
|
||||
}
|
||||
list($csql, $params) = $DB->get_in_or_equal(array_keys($courses), SQL_PARAMS_NAMED);
|
||||
$select = "instanceid $csql AND fieldid = :fieldid";
|
||||
$params['fieldid'] = $fieldid;
|
||||
$values = $DB->get_records_select_menu('customfield_data', $select, $params, 'value',
|
||||
'DISTINCT value, value AS value2');
|
||||
$values = array_filter($values);
|
||||
if (!$values) {
|
||||
return [];
|
||||
}
|
||||
$field = \core_customfield\field_controller::create($fieldid);
|
||||
if (!$field->supports_course_grouping()) {
|
||||
return []; // The field shouldn't have been selectable in the global settings, but just skip it now.
|
||||
}
|
||||
$values = $field->course_grouping_format_values($values);
|
||||
$customfieldactive = ($this->grouping === BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD);
|
||||
$ret = [];
|
||||
foreach ($values as $value => $name) {
|
||||
$ret[] = (object)[
|
||||
'name' => $name,
|
||||
'value' => $value,
|
||||
'active' => ($customfieldactive && ($this->customfieldvalue == $value)),
|
||||
];
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
@ -305,6 +394,29 @@ class main implements renderable, templatable {
|
||||
|
||||
$nocoursesurl = $output->image_url('courses', 'block_myoverview')->out();
|
||||
|
||||
$customfieldvalues = $this->get_customfield_values_for_export();
|
||||
$selectedcustomfield = '';
|
||||
if ($this->grouping == BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD) {
|
||||
foreach ($customfieldvalues as $field) {
|
||||
if ($field->value == $this->customfieldvalue) {
|
||||
$selectedcustomfield = $field->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If the selected custom field value has not been found (possibly because the field has
|
||||
// been changed in the settings) find a suitable fallback.
|
||||
if (!$selectedcustomfield) {
|
||||
$this->grouping = $this->get_fallback_grouping(get_config('block_myoverview'));
|
||||
if ($this->grouping == BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD) {
|
||||
// If the fallback grouping is still customfield, then select the first field.
|
||||
$firstfield = reset($customfieldvalues);
|
||||
if ($firstfield) {
|
||||
$selectedcustomfield = $firstfield->name;
|
||||
$this->customfieldvalue = $firstfield->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$preferences = $this->get_preferences_as_booleans();
|
||||
$availablelayouts = $this->get_formatted_available_layouts_for_export();
|
||||
|
||||
@ -327,8 +439,13 @@ class main implements renderable, templatable {
|
||||
'displaygroupingstarred' => $this->displaygroupingstarred,
|
||||
'displaygroupinghidden' => $this->displaygroupinghidden,
|
||||
'displaygroupingselector' => $this->displaygroupingselector,
|
||||
'displaygroupingcustomfield' => $this->displaygroupingcustomfield && $customfieldvalues,
|
||||
'customfieldname' => $this->customfiltergrouping,
|
||||
'customfieldvalue' => $this->customfieldvalue,
|
||||
'customfieldvalues' => $customfieldvalues,
|
||||
'selectedcustomfield' => $selectedcustomfield,
|
||||
];
|
||||
return array_merge($defaultvariables, $preferences);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ $string['aria:controls'] = 'Course overview controls';
|
||||
$string['aria:courseactions'] = 'Actions for current course';
|
||||
$string['aria:coursesummary'] = 'Course summary text:';
|
||||
$string['aria:courseprogress'] = 'Course progress:';
|
||||
$string['aria:customfield'] = 'Show {$a} courses';
|
||||
$string['aria:displaydropdown'] = 'Display drop-down menu';
|
||||
$string['aria:favourites'] = 'Show starred courses';
|
||||
$string['aria:future'] = 'Show future courses';
|
||||
@ -51,6 +52,9 @@ $string['card'] = 'Card';
|
||||
$string['cards'] = 'Cards';
|
||||
$string['courseprogress'] = 'Course progress:';
|
||||
$string['completepercent'] = '{$a}% complete';
|
||||
$string['customfield'] = 'Custom field';
|
||||
$string['customfiltergrouping'] = 'Field to use';
|
||||
$string['customfiltergrouping_nofields'] = 'You need to add at least one course custom field in order to use this setting';
|
||||
$string['displaycategories'] = 'Display categories';
|
||||
$string['displaycategories_help'] = 'Display the course category on dashboard course items including cards, list items and summary items.';
|
||||
$string['favourites'] = 'Starred';
|
||||
@ -61,6 +65,7 @@ $string['layouts'] = 'Available layouts';
|
||||
$string['layouts_help'] = 'Course overview layouts which are available for selection by users. If none are selected, the card layout will be used.';
|
||||
$string['list'] = 'List';
|
||||
$string['myoverview:myaddinstance'] = 'Add a new course overview block to Dashboard';
|
||||
$string['nocustomvalue'] = 'No {$a}';
|
||||
$string['past'] = 'Past';
|
||||
$string['pluginname'] = 'Course overview';
|
||||
$string['privacy:metadata:overviewsortpreference'] = 'The Course overview block sort preference.';
|
||||
|
@ -34,6 +34,12 @@ define('BLOCK_MYOVERVIEW_GROUPING_FUTURE', 'future');
|
||||
define('BLOCK_MYOVERVIEW_GROUPING_PAST', 'past');
|
||||
define('BLOCK_MYOVERVIEW_GROUPING_FAVOURITES', 'favourites');
|
||||
define('BLOCK_MYOVERVIEW_GROUPING_HIDDEN', 'hidden');
|
||||
define('BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD', 'customfield');
|
||||
|
||||
/**
|
||||
* Allows selection of all courses without a value for the custom field.
|
||||
*/
|
||||
define('BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY', -1);
|
||||
|
||||
/**
|
||||
* Constants for the user preferences sorting options
|
||||
@ -81,9 +87,17 @@ function block_myoverview_user_preferences() {
|
||||
BLOCK_MYOVERVIEW_GROUPING_FUTURE,
|
||||
BLOCK_MYOVERVIEW_GROUPING_PAST,
|
||||
BLOCK_MYOVERVIEW_GROUPING_FAVOURITES,
|
||||
BLOCK_MYOVERVIEW_GROUPING_HIDDEN
|
||||
BLOCK_MYOVERVIEW_GROUPING_HIDDEN,
|
||||
BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD,
|
||||
)
|
||||
);
|
||||
|
||||
$preferences['block_myoverview_user_grouping_customfieldvalue_preference'] = [
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => null,
|
||||
'type' => PARAM_RAW,
|
||||
];
|
||||
|
||||
$preferences['block_myoverview_user_sort_preference'] = array(
|
||||
'null' => NULL_NOT_ALLOWED,
|
||||
'default' => BLOCK_MYOVERVIEW_SORTING_TITLE,
|
||||
|
@ -86,6 +86,29 @@ if ($ADMIN->fulltree) {
|
||||
'',
|
||||
1));
|
||||
|
||||
$settings->add(new admin_setting_configcheckbox(
|
||||
'block_myoverview/displaygroupingcustomfield',
|
||||
get_string('customfield', 'block_myoverview'),
|
||||
'',
|
||||
0));
|
||||
|
||||
$choices = \core_customfield\api::get_fields_supporting_course_grouping();
|
||||
if ($choices) {
|
||||
$choices = ['' => get_string('choosedots')] + $choices;
|
||||
$settings->add(new admin_setting_configselect(
|
||||
'block_myoverview/customfiltergrouping',
|
||||
get_string('customfiltergrouping', 'block_myoverview'),
|
||||
'',
|
||||
'',
|
||||
$choices));
|
||||
} else {
|
||||
$settings->add(new admin_setting_configempty(
|
||||
'block_myoverview/customfiltergrouping',
|
||||
get_string('customfiltergrouping', 'block_myoverview'),
|
||||
get_string('customfiltergrouping_nofields', 'block_myoverview')));
|
||||
}
|
||||
$settings->hide_if('block_myoverview/customfiltergrouping', 'block_myoverview/displaygroupingcustomfield');
|
||||
|
||||
$settings->add(new admin_setting_configcheckbox(
|
||||
'block_myoverview/displaygroupingstarred',
|
||||
get_string('favourites', 'block_myoverview'),
|
||||
|
@ -31,6 +31,8 @@
|
||||
data-region="courses-view"
|
||||
data-display="{{view}}"
|
||||
data-grouping="{{grouping}}"
|
||||
data-customfieldname="{{customfieldname}}"
|
||||
data-customfieldvalue="{{customfieldvalue}}"
|
||||
data-sort="{{sort}}"
|
||||
data-prev-display="{{view}}"
|
||||
data-paging="{{paging}}"
|
||||
@ -40,4 +42,4 @@
|
||||
<div data-region="course-view-content">
|
||||
{{> block_myoverview/placeholders }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -50,6 +50,7 @@
|
||||
{{#past}}{{#str}} past, block_myoverview {{/str}}{{/past}}
|
||||
{{#favourites}}{{#str}} favourites, block_myoverview {{/str}}{{/favourites}}
|
||||
{{#hidden}}{{#str}} hiddencourses, block_myoverview {{/str}}{{/hidden}}
|
||||
{{selectedcustomfield}}
|
||||
</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" data-show-active-item data-active-item-text aria-labelledby="groupingdropdown">
|
||||
@ -106,6 +107,21 @@
|
||||
</a>
|
||||
</li>
|
||||
{{/displaygroupingpast}}
|
||||
{{#displaygroupingcustomfield}}
|
||||
<li class="dropdown-divider" role="presentation">
|
||||
<span class="filler"> </span>
|
||||
</li>
|
||||
{{#customfieldvalues}}
|
||||
<li>
|
||||
<a class="dropdown-item {{#active}}active{{/active}}" href="#" data-filter="grouping"
|
||||
data-value="customfield" data-pref="customfield" data-customfieldvalue="{{value}}"
|
||||
aria-label="{{#str}}aria:customfield, block_myoverview, {{name}}{{/str}}"
|
||||
aria-controls="courses-view-{{uniqid}}">
|
||||
{{name}}
|
||||
</a>
|
||||
</li>
|
||||
{{/customfieldvalues}}
|
||||
{{/displaygroupingcustomfield}}
|
||||
{{#displaygroupingstarred}}
|
||||
<li class="dropdown-divider" role="presentation">
|
||||
<span class="filler"> </span>
|
||||
|
@ -0,0 +1,174 @@
|
||||
@block @block_myoverview @javascript
|
||||
Feature: The my overview block allows users to group courses by custom fields
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email | idnumber |
|
||||
| student1 | Student | X | student1@example.com | S1 |
|
||||
And the following "custom field categories" exist:
|
||||
| name | component | area | itemid |
|
||||
| Course fields | core_course | course | 0 |
|
||||
And the following "custom fields" exist:
|
||||
| name | category | type | shortname | configdata |
|
||||
| Checkbox field | Course fields | checkbox | checkboxfield | |
|
||||
| Date field | Course fields | date | datefield | {"mindate":0, "maxdate":0} |
|
||||
| Select field | Course fields | select | selectfield | {"options":"Option 1\nOption 2\nOption 3\nOption 4"} |
|
||||
| Text field | Course fields | text | textfield | |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category | customfield_checkboxfield | customfield_datefield | customfield_selectfield | customfield_textfield |
|
||||
| Course 1 | C1 | 0 | 1 | 981028800 | 1 | fish |
|
||||
| Course 2 | C2 | 0 | 0 | 334324800 | | |
|
||||
| Course 3 | C3 | 0 | 0 | 981028800 | 2 | dog |
|
||||
| Course 4 | C4 | 0 | 1 | | 3 | cat |
|
||||
| Course 5 | C5 | 0 | | 334411200 | 2 | fish |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student1 | C2 | student |
|
||||
| student1 | C3 | student |
|
||||
| student1 | C4 | student |
|
||||
| student1 | C5 | student |
|
||||
|
||||
Scenario: Group courses by checkbox: Yes
|
||||
Given the following config values are set as admin:
|
||||
| displaygroupingcustomfield | 1 | block_myoverview |
|
||||
| customfiltergrouping | checkboxfield | block_myoverview |
|
||||
And I log in as "student1"
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
When I click on "Checkbox field: Yes" "link" in the "Course overview" "block"
|
||||
Then I should see "Course 1" in the "Course overview" "block"
|
||||
And I should not see "Course 2" in the "Course overview" "block"
|
||||
And I should not see "Course 3" in the "Course overview" "block"
|
||||
And I should see "Course 4" in the "Course overview" "block"
|
||||
And I should not see "Course 5" in the "Course overview" "block"
|
||||
|
||||
Scenario: Group courses by checkbox: No
|
||||
Given the following config values are set as admin:
|
||||
| displaygroupingcustomfield | 1 | block_myoverview |
|
||||
| customfiltergrouping | checkboxfield | block_myoverview |
|
||||
And I log in as "student1"
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
When I click on "Checkbox field: No" "link" in the "Course overview" "block"
|
||||
Then I should not see "Course 1" in the "Course overview" "block"
|
||||
And I should see "Course 2" in the "Course overview" "block"
|
||||
And I should see "Course 3" in the "Course overview" "block"
|
||||
And I should not see "Course 4" in the "Course overview" "block"
|
||||
And I should see "Course 5" in the "Course overview" "block"
|
||||
|
||||
Scenario: Group courses by date: 1 February 2001
|
||||
Given the following config values are set as admin:
|
||||
| displaygroupingcustomfield | 1 | block_myoverview |
|
||||
| customfiltergrouping | datefield | block_myoverview |
|
||||
And I log in as "student1"
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
When I click on "1 February 2001" "link" in the "Course overview" "block"
|
||||
Then I should see "Course 1" in the "Course overview" "block"
|
||||
And I should not see "Course 2" in the "Course overview" "block"
|
||||
And I should see "Course 3" in the "Course overview" "block"
|
||||
And I should not see "Course 4" in the "Course overview" "block"
|
||||
And I should not see "Course 5" in the "Course overview" "block"
|
||||
|
||||
Scenario: Group courses by date: 6 August 1980
|
||||
Given the following config values are set as admin:
|
||||
| displaygroupingcustomfield | 1 | block_myoverview |
|
||||
| customfiltergrouping | datefield | block_myoverview |
|
||||
And I log in as "student1"
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
When I click on "6 August 1980" "link" in the "Course overview" "block"
|
||||
Then I should not see "Course 1" in the "Course overview" "block"
|
||||
And I should not see "Course 2" in the "Course overview" "block"
|
||||
And I should not see "Course 3" in the "Course overview" "block"
|
||||
And I should not see "Course 4" in the "Course overview" "block"
|
||||
And I should see "Course 5" in the "Course overview" "block"
|
||||
|
||||
Scenario: Group courses by date: No Date field
|
||||
Given the following config values are set as admin:
|
||||
| displaygroupingcustomfield | 1 | block_myoverview |
|
||||
| customfiltergrouping | datefield | block_myoverview |
|
||||
And I log in as "student1"
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
When I click on "No Date field" "link" in the "Course overview" "block"
|
||||
Then I should not see "Course 1" in the "Course overview" "block"
|
||||
And I should not see "Course 2" in the "Course overview" "block"
|
||||
And I should not see "Course 3" in the "Course overview" "block"
|
||||
And I should see "Course 4" in the "Course overview" "block"
|
||||
And I should not see "Course 5" in the "Course overview" "block"
|
||||
|
||||
Scenario: Group courses by select: Option 1
|
||||
Given the following config values are set as admin:
|
||||
| displaygroupingcustomfield | 1 | block_myoverview |
|
||||
| customfiltergrouping | selectfield | block_myoverview |
|
||||
And I log in as "student1"
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
And I should not see "Option 4" in the "Course overview" "block"
|
||||
When I click on "Option 1" "link" in the "Course overview" "block"
|
||||
Then I should see "Course 1" in the "Course overview" "block"
|
||||
And I should not see "Course 2" in the "Course overview" "block"
|
||||
And I should not see "Course 3" in the "Course overview" "block"
|
||||
And I should not see "Course 4" in the "Course overview" "block"
|
||||
And I should not see "Course 5" in the "Course overview" "block"
|
||||
|
||||
Scenario: Group courses by select: Option 2
|
||||
Given the following config values are set as admin:
|
||||
| displaygroupingcustomfield | 1 | block_myoverview |
|
||||
| customfiltergrouping | selectfield | block_myoverview |
|
||||
And I log in as "student1"
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
When I click on "Option 2" "link" in the "Course overview" "block"
|
||||
Then I should not see "Course 1" in the "Course overview" "block"
|
||||
And I should not see "Course 2" in the "Course overview" "block"
|
||||
And I should see "Course 3" in the "Course overview" "block"
|
||||
And I should not see "Course 4" in the "Course overview" "block"
|
||||
And I should see "Course 5" in the "Course overview" "block"
|
||||
|
||||
Scenario: Group courses by select: No Select field
|
||||
Given the following config values are set as admin:
|
||||
| displaygroupingcustomfield | 1 | block_myoverview |
|
||||
| customfiltergrouping | selectfield | block_myoverview |
|
||||
And I log in as "student1"
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
When I click on "No Select field" "link" in the "Course overview" "block"
|
||||
Then I should not see "Course 1" in the "Course overview" "block"
|
||||
And I should see "Course 2" in the "Course overview" "block"
|
||||
And I should not see "Course 3" in the "Course overview" "block"
|
||||
And I should not see "Course 4" in the "Course overview" "block"
|
||||
And I should not see "Course 5" in the "Course overview" "block"
|
||||
|
||||
Scenario: Group courses by text: fish
|
||||
Given the following config values are set as admin:
|
||||
| displaygroupingcustomfield | 1 | block_myoverview |
|
||||
| customfiltergrouping | textfield | block_myoverview |
|
||||
And I log in as "student1"
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
When I click on "fish" "link" in the "Course overview" "block"
|
||||
Then I should see "Course 1" in the "Course overview" "block"
|
||||
And I should not see "Course 2" in the "Course overview" "block"
|
||||
And I should not see "Course 3" in the "Course overview" "block"
|
||||
And I should not see "Course 4" in the "Course overview" "block"
|
||||
And I should see "Course 5" in the "Course overview" "block"
|
||||
|
||||
Scenario: Group courses by text: dog
|
||||
Given the following config values are set as admin:
|
||||
| displaygroupingcustomfield | 1 | block_myoverview |
|
||||
| customfiltergrouping | textfield | block_myoverview |
|
||||
And I log in as "student1"
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
When I click on "dog" "link" in the "Course overview" "block"
|
||||
Then I should not see "Course 1" in the "Course overview" "block"
|
||||
And I should not see "Course 2" in the "Course overview" "block"
|
||||
And I should see "Course 3" in the "Course overview" "block"
|
||||
And I should not see "Course 4" in the "Course overview" "block"
|
||||
And I should not see "Course 5" in the "Course overview" "block"
|
||||
|
||||
Scenario: Group courses by text: No Text field
|
||||
Given the following config values are set as admin:
|
||||
| displaygroupingcustomfield | 1 | block_myoverview |
|
||||
| customfiltergrouping | textfield | block_myoverview |
|
||||
And I log in as "student1"
|
||||
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
|
||||
When I click on "No Text field" "link" in the "Course overview" "block"
|
||||
Then I should not see "Course 1" in the "Course overview" "block"
|
||||
And I should see "Course 2" in the "Course overview" "block"
|
||||
And I should not see "Course 3" in the "Course overview" "block"
|
||||
And I should not see "Course 4" in the "Course overview" "block"
|
||||
And I should not see "Course 5" in the "Course overview" "block"
|
@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2019091800; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2019100900; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2019051100; // Requires this Moodle version.
|
||||
$plugin->component = 'block_myoverview'; // Full name of the plugin (used for diagnostics).
|
||||
|
@ -3694,7 +3694,11 @@ class core_course_external extends external_api {
|
||||
'classification' => new external_value(PARAM_ALPHA, 'future, inprogress, or past'),
|
||||
'limit' => new external_value(PARAM_INT, 'Result set limit', VALUE_DEFAULT, 0),
|
||||
'offset' => new external_value(PARAM_INT, 'Result set offset', VALUE_DEFAULT, 0),
|
||||
'sort' => new external_value(PARAM_TEXT, 'Sort string', VALUE_DEFAULT, null)
|
||||
'sort' => new external_value(PARAM_TEXT, 'Sort string', VALUE_DEFAULT, null),
|
||||
'customfieldname' => new external_value(PARAM_ALPHANUMEXT, 'Used when classification = customfield',
|
||||
VALUE_DEFAULT, null),
|
||||
'customfieldvalue' => new external_value(PARAM_RAW, 'Used when classification = customfield',
|
||||
VALUE_DEFAULT, null),
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -3717,6 +3721,8 @@ class core_course_external extends external_api {
|
||||
* @param int $limit Result set limit
|
||||
* @param int $offset Offset the full course set before timeline classification is applied
|
||||
* @param string $sort SQL sort string for results
|
||||
* @param string $customfieldname
|
||||
* @param string $customfieldvalue
|
||||
* @return array list of courses and warnings
|
||||
* @throws invalid_parameter_exception
|
||||
*/
|
||||
@ -3724,7 +3730,9 @@ class core_course_external extends external_api {
|
||||
string $classification,
|
||||
int $limit = 0,
|
||||
int $offset = 0,
|
||||
string $sort = null
|
||||
string $sort = null,
|
||||
string $customfieldname = null,
|
||||
string $customfieldvalue = null
|
||||
) {
|
||||
global $CFG, $PAGE, $USER;
|
||||
require_once($CFG->dirroot . '/course/lib.php');
|
||||
@ -3735,6 +3743,7 @@ class core_course_external extends external_api {
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'sort' => $sort,
|
||||
'customfieldvalue' => $customfieldvalue,
|
||||
)
|
||||
);
|
||||
|
||||
@ -3742,6 +3751,7 @@ class core_course_external extends external_api {
|
||||
$limit = $params['limit'];
|
||||
$offset = $params['offset'];
|
||||
$sort = $params['sort'];
|
||||
$customfieldvalue = $params['customfieldvalue'];
|
||||
|
||||
switch($classification) {
|
||||
case COURSE_TIMELINE_ALLINCLUDINGHIDDEN:
|
||||
@ -3758,6 +3768,8 @@ class core_course_external extends external_api {
|
||||
break;
|
||||
case COURSE_TIMELINE_HIDDEN:
|
||||
break;
|
||||
case COURSE_CUSTOMFIELD:
|
||||
break;
|
||||
default:
|
||||
throw new invalid_parameter_exception('Invalid classification');
|
||||
}
|
||||
@ -3801,6 +3813,13 @@ class core_course_external extends external_api {
|
||||
$favouritecourseids,
|
||||
$limit
|
||||
);
|
||||
} else if ($classification == COURSE_CUSTOMFIELD) {
|
||||
list($filteredcourses, $processedcount) = course_filter_courses_by_customfield(
|
||||
$courses,
|
||||
$customfieldname,
|
||||
$customfieldvalue,
|
||||
$limit
|
||||
);
|
||||
} else {
|
||||
list($filteredcourses, $processedcount) = course_filter_courses_by_timeline_classification(
|
||||
$courses,
|
||||
|
@ -63,7 +63,10 @@ define('COURSE_TIMELINE_INPROGRESS', 'inprogress');
|
||||
define('COURSE_TIMELINE_FUTURE', 'future');
|
||||
define('COURSE_FAVOURITES', 'favourites');
|
||||
define('COURSE_TIMELINE_HIDDEN', 'hidden');
|
||||
define('COURSE_CUSTOMFIELD', 'customfield');
|
||||
define('COURSE_DB_QUERY_LIMIT', 1000);
|
||||
/** Searching for all courses that have no value for the specified custom field. */
|
||||
define('COURSE_CUSTOMFIELD_EMPTY', -1);
|
||||
|
||||
function make_log_url($module, $url) {
|
||||
switch ($module) {
|
||||
@ -4397,6 +4400,101 @@ function course_filter_courses_by_favourites(
|
||||
return [$filteredcourses, $numberofcoursesprocessed];
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the given $courses for any that have a $customfieldname value that matches the given
|
||||
* $customfieldvalue, up to the specified $limit.
|
||||
*
|
||||
* This function will return the subset of courses that matches the value as well as the
|
||||
* number of courses it had to process to build that subset.
|
||||
*
|
||||
* It is recommended that for larger sets of courses this function is given a Generator that loads
|
||||
* the courses from the database in chunks.
|
||||
*
|
||||
* @param array|Traversable $courses List of courses to process
|
||||
* @param string $customfieldname the shortname of the custom field to match against
|
||||
* @param string $customfieldvalue the value this custom field needs to match
|
||||
* @param int $limit Limit the number of results to this amount
|
||||
* @return array First value is the filtered courses, second value is the number of courses processed
|
||||
*/
|
||||
function course_filter_courses_by_customfield(
|
||||
$courses,
|
||||
$customfieldname,
|
||||
$customfieldvalue,
|
||||
int $limit = 0
|
||||
) : array {
|
||||
global $DB;
|
||||
|
||||
if (!$courses) {
|
||||
return [[], 0];
|
||||
}
|
||||
|
||||
// Prepare the list of courses to search through.
|
||||
$coursesbyid = [];
|
||||
foreach ($courses as $course) {
|
||||
$coursesbyid[$course->id] = $course;
|
||||
}
|
||||
if (!$coursesbyid) {
|
||||
return [[], 0];
|
||||
}
|
||||
list($csql, $params) = $DB->get_in_or_equal(array_keys($coursesbyid), SQL_PARAMS_NAMED);
|
||||
|
||||
// Get the id of the custom field.
|
||||
$sql = "
|
||||
SELECT f.id
|
||||
FROM {customfield_field} f
|
||||
JOIN {customfield_category} cat ON cat.id = f.categoryid
|
||||
WHERE f.shortname = ?
|
||||
AND cat.component = 'core_course'
|
||||
AND cat.area = 'course'
|
||||
";
|
||||
$fieldid = $DB->get_field_sql($sql, [$customfieldname]);
|
||||
if (!$fieldid) {
|
||||
return [[], 0];
|
||||
}
|
||||
|
||||
// Get a list of courseids that match that custom field value.
|
||||
if ($customfieldvalue == COURSE_CUSTOMFIELD_EMPTY) {
|
||||
$sql = "
|
||||
SELECT c.id
|
||||
FROM {course} c
|
||||
LEFT JOIN {customfield_data} cd ON cd.instanceid = c.id AND cd.fieldid = :fieldid
|
||||
WHERE c.id $csql
|
||||
AND (cd.value IS NULL OR cd.value = '' OR cd.value = '0')
|
||||
";
|
||||
$params['fieldid'] = $fieldid;
|
||||
$matchcourseids = $DB->get_fieldset_sql($sql, $params);
|
||||
} else {
|
||||
$select = "fieldid = :fieldid AND value = :customfieldvalue AND instanceid $csql";
|
||||
$params['fieldid'] = $fieldid;
|
||||
$params['customfieldvalue'] = $customfieldvalue;
|
||||
$matchcourseids = $DB->get_fieldset_select('customfield_data', 'instanceid', $select, $params);
|
||||
}
|
||||
|
||||
// Prepare the list of courses to return.
|
||||
$filteredcourses = [];
|
||||
$numberofcoursesprocessed = 0;
|
||||
$filtermatches = 0;
|
||||
|
||||
foreach ($coursesbyid as $course) {
|
||||
$numberofcoursesprocessed++;
|
||||
|
||||
if (in_array($course->id, $matchcourseids)) {
|
||||
$filteredcourses[] = $course;
|
||||
$filtermatches++;
|
||||
}
|
||||
|
||||
if ($limit && $filtermatches >= $limit) {
|
||||
// We've found the number of requested courses. No need to continue searching.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the number of filtered courses as well as the number of courses that were searched
|
||||
// in order to find the matching courses. This allows the calling code to do some kind of
|
||||
// pagination.
|
||||
return [$filteredcourses, $numberofcoursesprocessed];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check module updates since a given time.
|
||||
* This function checks for updates in the module config, file areas, completion, grades, comments and ratings.
|
||||
|
@ -4866,6 +4866,262 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
$this->assertEquals($expectedprocessedcount, $processedcount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cases for the course_filter_courses_by_timeline_classification tests.
|
||||
*/
|
||||
public function get_course_filter_courses_by_customfield_test_cases() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot.'/blocks/myoverview/lib.php');
|
||||
$coursedata = [
|
||||
[
|
||||
'shortname' => 'C1',
|
||||
'customfield_checkboxfield' => 1,
|
||||
'customfield_datefield' => strtotime('2001-02-01T12:00:00Z'),
|
||||
'customfield_selectfield' => 1,
|
||||
'customfield_textfield' => 'fish',
|
||||
],
|
||||
[
|
||||
'shortname' => 'C2',
|
||||
'customfield_checkboxfield' => 0,
|
||||
'customfield_datefield' => strtotime('1980-08-05T13:00:00Z'),
|
||||
],
|
||||
[
|
||||
'shortname' => 'C3',
|
||||
'customfield_checkboxfield' => 0,
|
||||
'customfield_datefield' => strtotime('2001-02-01T12:00:00Z'),
|
||||
'customfield_selectfield' => 2,
|
||||
'customfield_textfield' => 'dog',
|
||||
],
|
||||
[
|
||||
'shortname' => 'C4',
|
||||
'customfield_checkboxfield' => 1,
|
||||
'customfield_selectfield' => 3,
|
||||
'customfield_textfield' => 'cat',
|
||||
],
|
||||
[
|
||||
'shortname' => 'C5',
|
||||
'customfield_datefield' => strtotime('1980-08-06T13:00:00Z'),
|
||||
'customfield_selectfield' => 2,
|
||||
'customfield_textfield' => 'fish',
|
||||
],
|
||||
];
|
||||
|
||||
return [
|
||||
'empty set' => [
|
||||
'coursedata' => [],
|
||||
'customfield' => 'checkboxfield',
|
||||
'customfieldvalue' => 1,
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => [],
|
||||
'expectedprocessedcount' => 0
|
||||
],
|
||||
'checkbox yes' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'checkboxfield',
|
||||
'customfieldvalue' => 1,
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C1', 'C4'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'checkbox no' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'checkboxfield',
|
||||
'customfieldvalue' => BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY,
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C2', 'C3', 'C5'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'date 1 Feb 2001' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'datefield',
|
||||
'customfieldvalue' => strtotime('2001-02-01T12:00:00Z'),
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C1', 'C3'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'date 6 Aug 1980' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'datefield',
|
||||
'customfieldvalue' => strtotime('1980-08-06T13:00:00Z'),
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C5'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'date no date' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'datefield',
|
||||
'customfieldvalue' => BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY,
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C4'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'select Option 1' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'selectfield',
|
||||
'customfieldvalue' => 1,
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C1'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'select Option 2' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'selectfield',
|
||||
'customfieldvalue' => 2,
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C3', 'C5'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'select no select' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'selectfield',
|
||||
'customfieldvalue' => BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY,
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C2'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'text fish' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'textfield',
|
||||
'customfieldvalue' => 'fish',
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C1', 'C5'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'text dog' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'textfield',
|
||||
'customfieldvalue' => 'dog',
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C3'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'text no text' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'textfield',
|
||||
'customfieldvalue' => BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY,
|
||||
'limit' => 10,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C2'],
|
||||
'expectedprocessedcount' => 5
|
||||
],
|
||||
'checkbox limit no' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'checkboxfield',
|
||||
'customfieldvalue' => BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY,
|
||||
'limit' => 2,
|
||||
'offset' => 0,
|
||||
'expectedcourses' => ['C2', 'C3'],
|
||||
'expectedprocessedcount' => 3
|
||||
],
|
||||
'checkbox limit offset no' => [
|
||||
'coursedata' => $coursedata,
|
||||
'customfield' => 'checkboxfield',
|
||||
'customfieldvalue' => BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY,
|
||||
'limit' => 2,
|
||||
'offset' => 3,
|
||||
'expectedcourses' => ['C5'],
|
||||
'expectedprocessedcount' => 2
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the course_filter_courses_by_customfield function.
|
||||
*
|
||||
* @dataProvider get_course_filter_courses_by_customfield_test_cases()
|
||||
* @param array $coursedata Course test data to create.
|
||||
* @param string $customfield Shortname of the customfield.
|
||||
* @param string $customfieldvalue the value to filter by.
|
||||
* @param int $limit Maximum number of results to return.
|
||||
* @param int $offset Results to skip at the start of the result set.
|
||||
* @param string[] $expectedcourses Expected courses in results.
|
||||
* @param int $expectedprocessedcount Expected number of course records to be processed.
|
||||
*/
|
||||
public function test_course_filter_courses_by_customfield(
|
||||
$coursedata,
|
||||
$customfield,
|
||||
$customfieldvalue,
|
||||
$limit,
|
||||
$offset,
|
||||
$expectedcourses,
|
||||
$expectedprocessedcount
|
||||
) {
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
// Create the custom fields.
|
||||
$generator->create_custom_field_category([
|
||||
'name' => 'Course fields',
|
||||
'component' => 'core_course',
|
||||
'area' => 'course',
|
||||
'itemid' => 0,
|
||||
]);
|
||||
$generator->create_custom_field([
|
||||
'name' => 'Checkbox field',
|
||||
'category' => 'Course fields',
|
||||
'type' => 'checkbox',
|
||||
'shortname' => 'checkboxfield',
|
||||
]);
|
||||
$generator->create_custom_field([
|
||||
'name' => 'Date field',
|
||||
'category' => 'Course fields',
|
||||
'type' => 'date',
|
||||
'shortname' => 'datefield',
|
||||
'configdata' => '{"mindate":0, "maxdate":0}',
|
||||
]);
|
||||
$generator->create_custom_field([
|
||||
'name' => 'Select field',
|
||||
'category' => 'Course fields',
|
||||
'type' => 'select',
|
||||
'shortname' => 'selectfield',
|
||||
'configdata' => '{"options":"Option 1\nOption 2\nOption 3\nOption 4"}',
|
||||
]);
|
||||
$generator->create_custom_field([
|
||||
'name' => 'Text field',
|
||||
'category' => 'Course fields',
|
||||
'type' => 'text',
|
||||
'shortname' => 'textfield',
|
||||
]);
|
||||
|
||||
$courses = array_map(function($coursedata) use ($generator) {
|
||||
return $generator->create_course($coursedata);
|
||||
}, $coursedata);
|
||||
|
||||
$student = $generator->create_user();
|
||||
|
||||
foreach ($courses as $course) {
|
||||
$generator->enrol_user($student->id, $course->id, 'student');
|
||||
}
|
||||
|
||||
$this->setUser($student);
|
||||
|
||||
$coursesgenerator = course_get_enrolled_courses_for_logged_in_user(0, $offset, 'shortname ASC', 'shortname');
|
||||
list($result, $processedcount) = course_filter_courses_by_customfield(
|
||||
$coursesgenerator,
|
||||
$customfield,
|
||||
$customfieldvalue,
|
||||
$limit
|
||||
);
|
||||
|
||||
$actual = array_map(function($course) {
|
||||
return $course->shortname;
|
||||
}, $result);
|
||||
|
||||
$this->assertEquals($expectedcourses, $actual);
|
||||
$this->assertEquals($expectedprocessedcount, $processedcount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cases for the course_filter_courses_by_timeline_classification w/ hidden courses tests.
|
||||
*/
|
||||
|
@ -412,4 +412,29 @@ class api {
|
||||
$field->prepare_for_config_form($formdata);
|
||||
return $formdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the course custom fields that support course grouping in
|
||||
* block_myoverview
|
||||
* @return array $shortname => $name
|
||||
*/
|
||||
public static function get_fields_supporting_course_grouping() {
|
||||
global $DB;
|
||||
$sql = "
|
||||
SELECT f.*
|
||||
FROM {customfield_field} f
|
||||
JOIN {customfield_category} cat ON cat.id = f.categoryid
|
||||
WHERE cat.component = 'core_course' AND cat.area = 'course'
|
||||
ORDER BY f.name
|
||||
";
|
||||
$ret = [];
|
||||
$fields = $DB->get_records_sql($sql);
|
||||
foreach ($fields as $field) {
|
||||
$inst = field_controller::create(0, $field);
|
||||
if ($inst->supports_course_grouping()) {
|
||||
$ret[$inst->get('shortname')] = $inst->get('name');
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
@ -249,4 +249,23 @@ abstract class field_controller {
|
||||
$context = $this->get_handler()->get_configuration_context();
|
||||
return format_string($this->get('name'), true, ['context' => $context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this custom field type support being used as part of the block_myoverview
|
||||
* custom field grouping?
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_course_grouping(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this field supports course filtering, then this function needs overriding to
|
||||
* return the formatted values for this.
|
||||
* @param array $values the used values that need grouping
|
||||
* @return array
|
||||
*/
|
||||
public function course_grouping_format_values($values): array {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -68,4 +68,27 @@ class field_controller extends \core_customfield\field_controller {
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this custom field type support being used as part of the block_myoverview
|
||||
* custom field grouping?
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_course_grouping(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this field supports course grouping, then this function needs overriding to
|
||||
* return the formatted values for this.
|
||||
* @param array $values the used values that need formatting
|
||||
* @return array
|
||||
*/
|
||||
public function course_grouping_format_values($values): array {
|
||||
$name = $this->get_formatted_name();
|
||||
return [
|
||||
1 => $name.': '.get_string('yes'),
|
||||
BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY => $name.': '.get_string('no'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -83,4 +83,35 @@ class field_controller extends \core_customfield\field_controller {
|
||||
$mform->hideIf('configdata[mindate][hour]', 'configdata[includetime]');
|
||||
$mform->hideIf('configdata[mindate][minute]', 'configdata[includetime]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this custom field type support being used as part of the block_myoverview
|
||||
* custom field grouping?
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_course_grouping(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this field supports course grouping, then this function needs overriding to
|
||||
* return the formatted values for this.
|
||||
* @param array $values the used values that need formatting
|
||||
* @return array
|
||||
*/
|
||||
public function course_grouping_format_values($values): array {
|
||||
$format = get_string('strftimedate', 'langconfig');
|
||||
$ret = [];
|
||||
foreach ($values as $value) {
|
||||
if ($value) {
|
||||
$ret[$value] = userdate($value, $format);
|
||||
}
|
||||
}
|
||||
if (!$ret) {
|
||||
return []; // If the only dates found are 0, then do not show any options.
|
||||
}
|
||||
$ret[BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY] = get_string('nocustomvalue', 'block_myoverview',
|
||||
$this->get_formatted_name());
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
@ -91,4 +91,32 @@ class field_controller extends \core_customfield\field_controller {
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this custom field type support being used as part of the block_myoverview
|
||||
* custom field grouping?
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_course_grouping(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this field supports course grouping, then this function needs overriding to
|
||||
* return the formatted values for this.
|
||||
* @param array $values the used values that need formatting
|
||||
* @return array
|
||||
*/
|
||||
public function course_grouping_format_values($values): array {
|
||||
$options = self::get_options_array($this);
|
||||
$ret = [];
|
||||
foreach ($values as $value) {
|
||||
if (isset($options[$value])) {
|
||||
$ret[$value] = format_string($options[$value]);
|
||||
}
|
||||
}
|
||||
$ret[BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY] = get_string('nocustomvalue', 'block_myoverview',
|
||||
$this->get_formatted_name());
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
@ -120,4 +120,29 @@ class field_controller extends \core_customfield\field_controller {
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this custom field type support being used as part of the block_myoverview
|
||||
* custom field grouping?
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_course_grouping(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this field supports course grouping, then this function needs overriding to
|
||||
* return the formatted values for this.
|
||||
* @param array $values the used values that need formatting
|
||||
* @return array
|
||||
*/
|
||||
public function course_grouping_format_values($values): array {
|
||||
$ret = [];
|
||||
foreach ($values as $value) {
|
||||
$ret[$value] = format_string($value);
|
||||
}
|
||||
$ret[BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY] = get_string('nocustomvalue', 'block_myoverview',
|
||||
$this->get_formatted_name());
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
5
customfield/field/upgrade.txt
Normal file
5
customfield/field/upgrade.txt
Normal file
@ -0,0 +1,5 @@
|
||||
This files describes API changes in /customfield/field/* - customfield field types,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.8 ===
|
||||
* supports_course_grouping() and course_grouping_format_values() functions added to support use of custom fields in block_myoverview
|
@ -308,6 +308,20 @@ class behat_data_generators extends behat_base {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any empty custom fields, to avoid errors when creating the course.
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
protected function preprocess_course($data) {
|
||||
foreach ($data as $fieldname => $value) {
|
||||
if ($value === '' && strpos($fieldname, 'customfield_') === 0) {
|
||||
unset($data[$fieldname]);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* If password is not set it uses the username.
|
||||
* @param array $data
|
||||
|
Loading…
x
Reference in New Issue
Block a user