$actionsmenu->add($icon);
}
+ // Enable / disable.
+ if ($model->is_enabled()) {
+ $action = 'disable';
+ $text = get_string('disable');
+ $icontype = 't/block';
+ } else {
+ $action = 'enable';
+ $text = get_string('enable');
+ $icontype = 'i/checked';
+ }
+ $url = new \moodle_url('model.php', array('action' => $action, 'id' => $model->get_id()));
+ $icon = new \action_menu_link_secondary($url, new \pix_icon($icontype, $text), $text);
+ $actionsmenu->add($icon);
+
// Evaluate machine-learning-based models.
if ($model->get_indicators() && !$model->is_static()) {
$url = new \moodle_url('model.php', array('action' => 'evaluate', 'id' => $model->get_id()));
case 'log':
$title = get_string('viewlog', 'tool_analytics');
break;
+ case 'enable':
+ $title = get_string('enable');
+ break;
+ case 'disable':
+ $title = get_string('disable');
+ break;
+
default:
throw new moodle_exception('errorunknownaction', 'analytics');
}
switch ($action) {
+ case 'enable':
+ $model->enable();
+ redirect(new \moodle_url('/admin/tool/analytics/index.php'));
+
+ case 'disable':
+ $model->update(0, false, false);
+ redirect(new \moodle_url('/admin/tool/analytics/index.php'));
+
case 'edit':
if ($model->is_static()) {
* Updates the model.
*
* @param int|bool $enabled
- * @param \core_analytics\local\indicator\base[] $indicators
- * @param string $timesplittingid
+ * @param \core_analytics\local\indicator\base[]|false $indicators False to respect current indicators
+ * @param string|false $timesplittingid False to respect current time splitting method
* @return void
*/
- public function update($enabled, $indicators, $timesplittingid = '') {
+ public function update($enabled, $indicators = false, $timesplittingid = '') {
global $USER, $DB;
\core_analytics\manager::check_can_manage_models();
$now = time();
- $indicatorclasses = self::indicator_classes($indicators);
+ if ($indicators !== false) {
+ $indicatorclasses = self::indicator_classes($indicators);
+ $indicatorsstr = json_encode($indicatorclasses);
+ } else {
+ // Respect current value.
+ $indicatorsstr = $this->model->indicators;
+ }
+
+ if ($timesplittingid === false) {
+ // Respect current value.
+ $timesplittingid = $this->model->timesplitting;
+ }
- $indicatorsstr = json_encode($indicatorclasses);
if ($this->model->timesplitting !== $timesplittingid ||
$this->model->indicators !== $indicatorsstr) {
// We update the version of the model so different time splittings are not mixed up.
/**
* Enabled the model using the provided time splitting method.
*
- * @param string $timesplittingid
+ * @param string|false $timesplittingid False to respect the current time splitting method.
* @return void
*/
public function enable($timesplittingid = false) {
$ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = bi.id AND ctx.contextlevel = :contextlevel)";
$systemcontext = context_system::instance();
+ list($bpcontext, $bpcontextidparams) = $DB->get_in_or_equal(array($context->id, $systemcontext->id),
+ SQL_PARAMS_NAMED, 'bpcontextid');
$params = array(
'contextlevel' => CONTEXT_BLOCK,
'subpage1' => $this->page->subpage,
FROM {block_instances} bi
JOIN {block} b ON bi.blockname = b.name
LEFT JOIN {block_positions} bp ON bp.blockinstanceid = bi.id
- AND bp.contextid = :contextid1
+ AND bp.contextid $bpcontext
AND bp.pagetype = :pagetype
AND bp.subpage = :subpage1
$ccjoin
COALESCE(bp.weight, bi.defaultweight),
bi.id";
- $allparams = $params + $parentcontextparams + $pagetypepatternparams + $requiredbythemeparams + $requiredbythemenotparams;
+ $allparams = $params + $parentcontextparams + $pagetypepatternparams + $requiredbythemeparams;
+ $allparams = $allparams + $requiredbythemenotparams + $bpcontextidparams;
$blockinstances = $DB->get_recordset_sql($sql, $allparams);
$this->birecordsbyregion = $this->prepare_per_region_arrays();
return array('description'=>$this->mysqli->server_info, 'version'=>$version);
}
+ protected function has_breaking_change_quoted_defaults() {
+ $version = $this->get_server_info()['version'];
+ // Breaking change since 10.2.7: MDEV-13132.
+ return version_compare($version, '10.2.7', '>=');
+ }
+
/**
* It is time to require transactions everywhere.
*
$rawcolumn->numeric_scale = null;
$rawcolumn->is_nullable = $rawcolumn->null; unset($rawcolumn->null);
$rawcolumn->column_default = $rawcolumn->default; unset($rawcolumn->default);
- $rawcolumn->column_key = $rawcolumn->key; unset($rawcolumn->default);
+ $rawcolumn->column_key = $rawcolumn->key; unset($rawcolumn->key);
if (preg_match('/(enum|varchar)\((\d+)\)/i', $rawcolumn->column_type, $matches)) {
$rawcolumn->data_type = $matches[1];
return $structure;
}
+ /**
+ * Indicates whether column information retrieved from `information_schema.columns` has default values quoted or not.
+ * @return boolean True when default values are quoted (breaking change); otherwise, false.
+ */
+ protected function has_breaking_change_quoted_defaults() {
+ return false;
+ }
+
/**
* Returns moodle column info for raw column from information schema.
* @param stdClass $rawcolumn
$info->name = $rawcolumn->column_name;
$info->type = $rawcolumn->data_type;
$info->meta_type = $this->mysqltype2moodletype($rawcolumn->data_type);
- $info->default_value = $rawcolumn->column_default;
+ if ($this->has_breaking_change_quoted_defaults()) {
+ $info->default_value = trim($rawcolumn->column_default, "'");
+ } else {
+ $info->default_value = $rawcolumn->column_default;
+ }
$info->has_default = !is_null($rawcolumn->column_default);
$info->not_null = ($rawcolumn->is_nullable === 'NO');
$info->primary_key = ($rawcolumn->column_key === 'PRI');
'null' => NULL_ALLOWED,
),
'content3' => array(
- 'type' => PARAM_BOOL,
+ 'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
'content4' => array(
- 'type' => PARAM_BOOL,
+ 'type' => PARAM_RAW,
'description' => 'Contents.',
'null' => NULL_ALLOWED,
),
require_once("../config.php");
-$formaction = required_param('formaction', PARAM_FILE);
+$formaction = required_param('formaction', PARAM_LOCALURL);
$id = required_param('id', PARAM_INT);
$PAGE->set_url('/user/action_redir.php', array('formaction' => $formaction, 'id' => $id));
+list($formaction) = explode('?', $formaction, 2);
// Add every page will be redirected by this script.
$actions = array(
'messageselect.php',
'addnote.php',
'groupaddnote.php',
+ 'bulkchange.php'
);
if (array_search($formaction, $actions) === false) {
print_error('confirmsesskeybad');
}
-require_once($formaction);
+if ($formaction == 'bulkchange.php') {
+ // Backwards compatibility for enrolment plugins bulk change functionality.
+ // This awful code is adapting from the participant page with it's param names and values
+ // to the values expected by the bulk enrolment changes forms.
+ $formaction = required_param('formaction', PARAM_URL);
+ require_once($CFG->dirroot . '/enrol/locallib.php');
+
+ $url = new moodle_url($formaction);
+ // Get the enrolment plugin type and bulk action from the url.
+ $plugin = $url->param('plugin');
+ $operationname = $url->param('operation');
+
+ $course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST);
+ $context = context_course::instance($id);
+ $PAGE->set_context($context);
+
+ $instances = enrol_get_instances($course->id, false);
+ $instance = false;
+ foreach ($instances as $oneinstance) {
+ if ($oneinstance->enrol == $plugin) {
+ $instance = $oneinstance;
+ break;
+ }
+ }
+ if (!$instance) {
+ print_error('errorwithbulkoperation', 'enrol');
+ }
+
+ $manager = new course_enrolment_manager($PAGE, $course, $instance->id);
+ $plugins = $manager->get_enrolment_plugins();
+
+ if (!isset($plugins[$plugin])) {
+ print_error('errorwithbulkoperation', 'enrol');
+ }
+
+ $plugin = $plugins[$plugin];
+
+ $operations = $plugin->get_bulk_operations($manager);
+
+ if (!isset($operations[$operationname])) {
+ print_error('errorwithbulkoperation', 'enrol');
+ }
+ $operation = $operations[$operationname];
+
+ $userids = optional_param_array('userid', array(), PARAM_INT);
+ $default = new moodle_url('/user/index.php', ['id' => $course->id]);
+ $returnurl = new moodle_url(optional_param('returnto', $default, PARAM_URL));
+
+ if (empty($userids)) {
+ $userids = optional_param_array('bulkuser', array(), PARAM_INT);
+ }
+ if (empty($userids)) {
+ // The first time list hack.
+ if (empty($userids) and $post = data_submitted()) {
+ foreach ($post as $k => $v) {
+ if (preg_match('/^user(\d+)$/', $k, $m)) {
+ $userids[] = $m[1];
+ }
+ }
+ }
+ }
+
+ if (empty($userids)) {
+ redirect($returnurl, get_string('noselectedusers', 'bulkusers'));
+ }
+
+ $users = $manager->get_users_enrolments($userids);
+
+ // We may have users from any kind of enrolment, we need to filter for the enrolment plugin matching the bulk action.
+ $matchesplugin = function($user) use ($plugin) {
+ foreach ($user->enrolments as $enrolment) {
+ if ($enrolment->enrolmentplugin->get_name() == $plugin->get_name()) {
+ return true;
+ }
+ }
+ return false;
+ };
+ $users = array_filter($users, $matchesplugin);
+
+ if (empty($users)) {
+ redirect($returnurl, get_string('noselectedusers', 'bulkusers'));
+ }
+
+ // Get the form for the bulk operation.
+ $mform = $operation->get_form($PAGE->url, array('users' => $users));
+ // If the mform is false then attempt an immediate process. This may be an immediate action that
+ // doesn't require user input OR confirmation.... who know what but maybe one day.
+ if ($mform === false) {
+ if ($operation->process($manager, $users, new stdClass)) {
+ redirect($returnurl);
+ } else {
+ print_error('errorwithbulkoperation', 'enrol');
+ }
+ }
+ // Check if the bulk operation has been cancelled.
+ if ($mform->is_cancelled()) {
+ redirect($returnurl);
+ }
+ if ($mform->is_submitted() && $mform->is_validated() && confirm_sesskey()) {
+ if ($operation->process($manager, $users, $mform->get_data())) {
+ redirect($returnurl);
+ }
+ }
+
+ $pagetitle = get_string('bulkuseroperation', 'enrol');
+
+ $PAGE->set_title($pagetitle);
+ $PAGE->set_heading($pagetitle);
+ echo $OUTPUT->header();
+ echo $OUTPUT->heading($operation->get_title());
+ $mform->display();
+ echo $OUTPUT->footer();
+ exit();
+
+} else {
+ require_once($formaction);
+}
$displaylist['groupaddnote.php'] = get_string('groupaddnewnote', 'notes');
}
+ $plugins = $manager->get_enrolment_plugins();
+ foreach ($plugins as $plugin) {
+ $bulkoperations = $plugin->get_bulk_operations($manager);
+
+ $pluginoptions = [];
+ foreach ($bulkoperations as $key => $bulkoperation) {
+ $params = ['plugin' => $plugin->get_name(), 'operation' => $key];
+ $url = new moodle_url('bulkchange.php', $params);
+ $pluginoptions[$url->out(false)] = $bulkoperation->get_title();
+ }
+ if (!empty($pluginoptions)) {
+ $name = get_string('pluginname', 'enrol_' . $plugin->get_name());
+ $displaylist[] = [$name => $pluginoptions];
+ }
+ }
+
echo $OUTPUT->help_icon('withselectedusers');
echo html_writer::tag('label', get_string("withselectedusers"), array('for' => 'formactionid'));
echo html_writer::select($displaylist, 'formaction', '', array('' => 'choosedots'), array('id' => 'formactionid'));
--- /dev/null
+@core @core_user
+Feature: Bulk enrolments
+ In order to manage a course site
+ As a teacher
+ I need to be able to bulk edit enrolments
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | student1 | Student | 1 | student1@example.com |
+ | student2 | Student | 2 | student2@example.com |
+ | teacher1 | Teacher | 1 | teacher1@example.com |
+ And the following "courses" exist:
+ | fullname | shortname | format |
+ | Course 1 | C1 | topics |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C1 | student |
+ | student2 | C1 | student |
+ | teacher1 | C1 | editingteacher |
+
+ @javascript
+ Scenario: Bulk edit enrolments
+ When I log in as "admin"
+ And I am on "Course 1" course homepage
+ And I follow "Participants"
+ And I press "Select all"
+ And I set the field "With selected users..." to "Edit selected user enrolments"
+ And I set the field "Alter status" to "Suspended"
+ And I press "Save changes"
+ Then I should see "Suspended" in the "Teacher 1" "table_row"
+ Then I should see "Suspended" in the "Student 1" "table_row"
+ And I should see "Suspended" in the "Student 2" "table_row"
+
+ @javascript
+ Scenario: Bulk delete enrolments
+ When I log in as "admin"
+ And I am on "Course 1" course homepage
+ And I follow "Participants"
+ And I press "Select all"
+ And I set the field "With selected users..." to "Delete selected user enrolments"
+ And I press "Unenrol users"
+ Then I should not see "Student 1"
+ And I should not see "Student 2"
+ And I should not see "Teacher 1"