/** * Observe the events, and dispatch them if necessary. * * @param \core\event\base $event The event. * @return void */ public static function observer(\core\event\base $event) { if ($event->component === 'block_xp') { // Skip own events. } else { if (!$event->userid || isguestuser($event->userid) || is_siteadmin($event->userid)) { // Skip non-logged in users and guests. } else { if ($event->contextlevel !== CONTEXT_COURSE && $event->contextlevel !== CONTEXT_MODULE) { // Ignore events outside a course. } else { if ($event->edulevel !== \core\event\base::LEVEL_PARTICIPATING) { // Ignore events that are not participating. } else { if (!has_capability('block/xp:earnxp', $event->get_context(), $event->userid)) { // Skip the events if the user does not have the capability to earn XP, or if it is the admin. } else { // Keep the event, and proceed. $manager = block_xp_manager::get($event->courseid); $manager->capture_event($event); } } } } } }
public function test_custom_filters() { $this->resetAfterTest(true); $course = $this->getDataGenerator()->create_course(); $manager = block_xp_manager::get($course->id); $fm = $manager->get_filter_manager(); // Define some custom rules, the sortorder and IDs are mixed here. $rule = new block_xp_rule_property(block_xp_rule_base::EQ, 'c', 'crud'); $data = array('courseid' => $course->id, 'sortorder' => 1, 'points' => 100, 'rule' => $rule); block_xp_filter::load_from_data($data)->save(); $fm->invalidate_filters_cache(); $rule = new block_xp_ruleset(array(new block_xp_rule_property(block_xp_rule_base::EQ, 2, 'objectid'), new block_xp_rule_property(block_xp_rule_base::EQ, 'u', 'crud')), block_xp_ruleset::ANY); $data = array('courseid' => $course->id, 'sortorder' => 2, 'points' => 120, 'rule' => $rule); block_xp_filter::load_from_data($data)->save(); $rule = new block_xp_ruleset(array(new block_xp_rule_property(block_xp_rule_base::GTE, 100, 'objectid'), new block_xp_rule_property(block_xp_rule_base::EQ, 'r', 'crud')), block_xp_ruleset::ALL); $data = array('courseid' => $course->id, 'sortorder' => 0, 'points' => 130, 'rule' => $rule); block_xp_filter::load_from_data($data)->save(); // We can override default filters. $e = \block_xp\event\something_happened::mock(array('crud' => 'c', 'objectid' => 2)); $this->assertSame(100, $fm->get_points_for_event($e)); // We can still fallback on default filters. $e = \block_xp\event\something_happened::mock(array('crud' => 'd')); $this->assertSame(0, $fm->get_points_for_event($e)); // Sort order is respected. $e = \block_xp\event\something_happened::mock(array('crud' => 'u', 'objectid' => 2)); $this->assertSame(120, $fm->get_points_for_event($e)); $e = \block_xp\event\something_happened::mock(array('crud' => 'r')); $this->assertSame(9, $fm->get_points_for_event($e)); $e = \block_xp\event\something_happened::mock(array('crud' => 'r', 'objectid' => 100)); $this->assertSame(130, $fm->get_points_for_event($e)); // This filter will catch everything before the default rules. $rule = new block_xp_rule_property(block_xp_rule_base::CT, 'something', 'eventname'); $data = array('courseid' => $course->id, 'sortorder' => 3, 'points' => 110, 'rule' => $rule); block_xp_filter::load_from_data($data)->save(); $fm->invalidate_filters_cache(); $e = \block_xp\event\something_happened::mock(array('crud' => 'd')); $this->assertSame(110, $fm->get_points_for_event($e)); $e = \block_xp\event\something_happened::mock(array('crud' => 'r')); $this->assertSame(110, $fm->get_points_for_event($e)); // This filter will catch everything. $rule = new block_xp_rule_property(block_xp_rule_base::CT, 'something', 'eventname'); $data = array('courseid' => $course->id, 'sortorder' => -1, 'points' => 1, 'rule' => $rule); block_xp_filter::load_from_data($data)->save(); $fm->invalidate_filters_cache(); $e = \block_xp\event\something_happened::mock(array('crud' => 'u', 'objectid' => 2)); $this->assertSame(1, $fm->get_points_for_event($e)); $e = \block_xp\event\something_happened::mock(array('crud' => 'r', 'objectid' => 100)); $this->assertSame(1, $fm->get_points_for_event($e)); $e = \block_xp\event\something_happened::mock(array('crud' => 'd')); $this->assertSame(1, $fm->get_points_for_event($e)); $e = \block_xp\event\something_happened::mock(array('crud' => 'r')); $this->assertSame(1, $fm->get_points_for_event($e)); }
/** * Constructor. * * @param string $uniqueid Unique ID. * @param int $courseid Course ID. * @param int $groupid Group ID. */ public function __construct($uniqueid, $courseid, $groupid) { global $DB, $PAGE; parent::__construct($uniqueid); // Block XP stuff. $this->xpmanager = block_xp_manager::get($courseid); $this->xpoutput = $PAGE->get_renderer('block_xp'); $context = context_course::instance($courseid); // Define columns. $this->define_columns(array('userpic', 'fullname', 'lvl', 'xp', 'progress', 'actions')); $this->define_headers(array('', get_string('fullname'), get_string('level', 'block_xp'), get_string('xp', 'block_xp'), get_string('progress', 'block_xp'), '')); // Get all the users that are enrolled and can earn XP. $ids = array(); $users = get_enrolled_users($context, 'block/xp:earnxp', $groupid); foreach ($users as $user) { $ids[$user->id] = $user->id; } unset($users); // Get the users which might not be enrolled or are revoked the permission, but still should // be displayed in the report for the teachers' benefit. We need to filter out the users which // are not a member of the group though. if (empty($groupid)) { $sql = 'SELECT userid FROM {block_xp} WHERE courseid = :courseid'; $params = array('courseid' => $courseid); } else { $sql = 'SELECT b.userid FROM {block_xp} b JOIN {groups_members} gm ON b.userid = gm.userid AND gm.groupid = :groupid WHERE courseid = :courseid'; $params = array('courseid' => $courseid, 'groupid' => $groupid); } $entries = $DB->get_recordset_sql($sql, $params); foreach ($entries as $entry) { $ids[$entry->userid] = $entry->userid; } $entries->close(); list($insql, $inparams) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED, 'param', true, null); // Define SQL. $this->sql = new stdClass(); $this->sql->fields = user_picture::fields('u') . ', COALESCE(x.lvl, 1) AS lvl, x.xp, ' . context_helper::get_preload_record_columns_sql('ctx'); $this->sql->from = "{user} u\n JOIN {context} ctx\n ON ctx.instanceid = u.id\n AND ctx.contextlevel = :contextlevel\n LEFT JOIN {block_xp} x\n ON (x.userid = u.id AND x.courseid = :courseid)"; $this->sql->where = "u.id {$insql}"; $this->sql->params = array_merge($inparams, array('courseid' => $courseid, 'contextlevel' => CONTEXT_USER)); // Define various table settings. $this->sortable(true, 'lvl', SORT_DESC); $this->no_sorting('userpic'); $this->no_sorting('progress'); $this->collapsible(false); }
/** * Constructor. * * @param string $uniqueid Unique ID. */ public function __construct($uniqueid, $courseid) { global $PAGE; parent::__construct($uniqueid); // Block XP stuff. $this->xpmanager = block_xp_manager::get($courseid); $this->xpoutput = $PAGE->get_renderer('block_xp'); // Define columns. $this->define_columns(array('rank', 'userpic', 'fullname', 'lvl', 'xp', 'progress')); $this->define_headers(array(get_string('rank', 'block_xp'), '', get_string('fullname'), get_string('level', 'block_xp'), get_string('xp', 'block_xp'), get_string('progress', 'block_xp'))); // Define SQL. $this->sql = new stdClass(); $this->sql->fields = 'x.*, ' . user_picture::fields('u'); $this->sql->from = '{block_xp} x LEFT JOIN {user} u ON x.userid = u.id'; $this->sql->where = 'courseid = :courseid'; $this->sql->params = array('courseid' => $courseid); // Define various table settings. $this->sortable(false); $this->no_sorting('userpic'); $this->no_sorting('progress'); $this->collapsible(false); }
/** * Constructor. * * @param string $uniqueid Unique ID. */ public function __construct($uniqueid, $courseid) { global $DB, $PAGE; parent::__construct($uniqueid); // Block XP stuff. $this->xpmanager = block_xp_manager::get($courseid); $this->xpoutput = $PAGE->get_renderer('block_xp'); $context = context_course::instance($courseid); // Define columns. $this->define_columns(array('userpic', 'fullname', 'lvl', 'xp', 'progress', 'actions')); $this->define_headers(array('', get_string('fullname'), get_string('level', 'block_xp'), get_string('xp', 'block_xp'), get_string('progress', 'block_xp'), '')); // Get the relevant user IDs, users enrolled or users with XP. // This might be a performance issue at some point. $ids = array(); $users = get_enrolled_users($context, 'block/xp:earnxp'); foreach ($users as $user) { $ids[$user->id] = $user->id; } unset($users); $entries = $DB->get_recordset_sql('SELECT userid FROM {block_xp} WHERE courseid = :courseid', array('courseid' => $courseid)); foreach ($entries as $entry) { $ids[$entry->userid] = $entry->userid; } $entries->close(); list($insql, $inparams) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED, 'param', true, null); // Define SQL. $this->sql = new stdClass(); $this->sql->fields = user_picture::fields('u') . ', x.lvl, x.xp'; $this->sql->from = "{user} u LEFT JOIN {block_xp} x ON (x.userid = u.id AND x.courseid = :courseid)"; $this->sql->where = "u.id {$insql}"; $this->sql->params = array_merge($inparams, array('courseid' => $courseid)); // Define various table settings. $this->sortable(true, 'lvl', SORT_DESC); $this->no_sorting('userpic'); $this->no_sorting('progress'); $this->collapsible(false); }
/** * Constructor. * * @param string $uniqueid Unique ID. * @param int $courseid Course ID. * @param int $groupid Group ID. */ public function __construct($uniqueid, $courseid, $groupid, array $options = array(), $userid = null) { global $PAGE, $USER; parent::__construct($uniqueid); if (isset($options['rankmode'])) { $this->rankmode = $options['rankmode']; } if (isset($options['neighboursonly'])) { $this->neighboursonly = $options['neighboursonly']; } if (isset($options['neighboursabove'])) { $this->neighboursabove = $options['neighboursabove']; } if (isset($options['neighboursbelow'])) { $this->neighboursbelow = $options['neighboursbelow']; } if (isset($options['identitymode'])) { $this->identitymode = $options['identitymode']; } // The user ID we're viewing the ladder for. if ($userid === null) { $userid = $USER->id; } $this->userid = $userid; // Block XP stuff. $this->xpmanager = block_xp_manager::get($courseid); $this->xpoutput = $PAGE->get_renderer('block_xp'); // Define columns, and headers. $columns = array(); $headers = array(); if ($this->rankmode != block_xp_manager::RANK_OFF) { $columns += array('rank'); if ($this->rankmode == block_xp_manager::RANK_REL) { $headers += array(get_string('difference', 'block_xp')); } else { $headers += array(get_string('rank', 'block_xp')); } } $columns = array_merge($columns, array('userpic', 'fullname', 'lvl', 'xp', 'progress')); $headers = array_merge($headers, array('', get_string('fullname'), get_string('level', 'block_xp'), get_string('xp', 'block_xp'), get_string('progress', 'block_xp'))); $this->define_columns($columns); $this->define_headers($headers); // Define SQL. $sqlfrom = ''; $sqlparams = array(); if ($groupid) { $sqlfrom = '{block_xp} x JOIN {groups_members} gm ON gm.groupid = :groupid AND gm.userid = x.userid JOIN {user} u ON x.userid = u.id'; $sqlparams = array('groupid' => $groupid); } else { $sqlfrom = '{block_xp} x JOIN {user} u ON x.userid = u.id'; } $sqlfrom .= " JOIN {context} ctx\n ON ctx.instanceid = u.id\n AND ctx.contextlevel = :contextlevel"; $sqlparams += array('contextlevel' => CONTEXT_USER); $this->sql = new stdClass(); $this->sql->fields = 'x.*, ' . user_picture::fields('u', null, 'userid') . ', ' . context_helper::get_preload_record_columns_sql('ctx'); $this->sql->from = $sqlfrom; $this->sql->where = 'courseid = :courseid'; $this->sql->params = array_merge(array('courseid' => $courseid), $sqlparams); // Define various table settings. $this->sortable(false); $this->no_sorting('userpic'); $this->no_sorting('progress'); $this->collapsible(false); }
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require __DIR__ . '/../../config.php'; $courseid = required_param('courseid', PARAM_INT); require_login($courseid); $manager = block_xp_manager::get($courseid); $context = $manager->get_context(); if (!$manager->can_view_ladder_page()) { throw new moodle_exception('nopermissions', '', '', 'view_ladder_page'); } // Some stuff. $url = new moodle_url('/blocks/xp/ladder.php', array('courseid' => $courseid)); $strladder = get_string('ladder', 'block_xp'); // Page info. $PAGE->set_context($context); $PAGE->set_pagelayout('course'); $PAGE->set_title($strladder); $PAGE->set_heading($COURSE->fullname); $PAGE->set_url($url); $manager = block_xp_manager::get($courseid); $group = groups_get_course_group($manager->get_course(), true); $renderer = $PAGE->get_renderer('block_xp'); echo $OUTPUT->header(); echo $OUTPUT->heading($strladder); echo $renderer->navigation($manager, 'ladder'); echo $renderer->notices($manager); groups_print_course_menu($manager->get_course(), $url); $table = new block_xp_ladder_table('block_xp_ladder', $courseid, $group, array('identitymode' => $manager->get_config('identitymode'), 'rankmode' => $manager->get_config('rankmode'), 'neighboursonly' => $manager->get_config('neighbours') > 0, 'neighboursabove' => $manager->get_config('neighbours'), 'neighboursbelow' => $manager->get_config('neighbours'))); $table->define_baseurl($url); echo $table->out(20, false); echo $OUTPUT->footer();
/** * Return the current level of the user. * * @param int $courseid The course ID. * @param int $userid The user ID. * @return int The user level. */ protected function get_user_level($courseid, $userid) { // Basic static cache to improve performance. $cachekey = $courseid . ':' . $userid; if (!isset(self::$lvlcache[$cachekey])) { $xpman = \block_xp_manager::get($courseid); $progress = $xpman->get_progress_for_user($userid); self::$lvlcache[$cachekey] = $progress->level; } return self::$lvlcache[$cachekey]; }
public function test_reset_data_with_groups() { global $DB; $c1 = $this->getDataGenerator()->create_course(); $c2 = $this->getDataGenerator()->create_course(); $u1 = $this->getDataGenerator()->create_user(); $u2 = $this->getDataGenerator()->create_user(); $g1 = $this->getDataGenerator()->create_group(array('courseid' => $c1->id)); $this->getDataGenerator()->enrol_user($u1->id, $c1->id); $this->getDataGenerator()->enrol_user($u2->id, $c1->id); $this->getDataGenerator()->enrol_user($u1->id, $c2->id); $this->getDataGenerator()->create_group_member(array('groupid' => $g1->id, 'userid' => $u1->id)); $manager = block_xp_manager::get($c1->id); $manager->update_config(array('enabled' => true, 'timebetweensameactions' => 0)); $e = \block_xp\event\something_happened::mock(array('crud' => 'c', 'userid' => $u1->id, 'courseid' => $c1->id)); $manager->capture_event($e); $manager->capture_event($e); $e = \block_xp\event\something_happened::mock(array('crud' => 'c', 'userid' => $u2->id, 'courseid' => $c1->id)); $manager->capture_event($e); $manager->capture_event($e); $manager = block_xp_manager::get($c2->id); $manager->update_config(array('enabled' => true, 'timebetweensameactions' => 0)); $e = \block_xp\event\something_happened::mock(array('crud' => 'c', 'userid' => $u1->id, 'courseid' => $c2->id)); $manager->capture_event($e); $this->assertEquals(1, $DB->count_records('block_xp', array('courseid' => $c1->id, 'userid' => $u1->id))); $this->assertEquals(1, $DB->count_records('block_xp', array('courseid' => $c1->id, 'userid' => $u2->id))); $this->assertEquals(2, $DB->count_records('block_xp_log', array('courseid' => $c1->id, 'userid' => $u1->id))); $this->assertEquals(2, $DB->count_records('block_xp_log', array('courseid' => $c1->id, 'userid' => $u2->id))); $this->assertEquals(1, $DB->count_records('block_xp', array('courseid' => $c2->id))); $this->assertEquals(1, $DB->count_records('block_xp_log', array('courseid' => $c2->id))); $manager = block_xp_manager::get($c1->id); $manager->reset_data($g1->id); $this->assertEquals(0, $DB->count_records('block_xp', array('courseid' => $c1->id, 'userid' => $u1->id))); $this->assertEquals(1, $DB->count_records('block_xp', array('courseid' => $c1->id, 'userid' => $u2->id))); $this->assertEquals(0, $DB->count_records('block_xp_log', array('courseid' => $c1->id, 'userid' => $u1->id))); $this->assertEquals(2, $DB->count_records('block_xp_log', array('courseid' => $c1->id, 'userid' => $u2->id))); $this->assertEquals(1, $DB->count_records('block_xp', array('courseid' => $c2->id))); $this->assertEquals(1, $DB->count_records('block_xp_log', array('courseid' => $c2->id))); }
$PAGE->set_pagelayout('course'); $PAGE->set_title($strlevels); $PAGE->set_heading($COURSE->fullname); $PAGE->set_url($url); $renderer = $PAGE->get_renderer('block_xp'); $manager = block_xp_manager::get($courseid); // Set form and its default (existing) values. $form = new block_xp_levels_form($url, array('defaultconfig' => block_xp_manager::get_default_config(), 'manager' => $manager)); $form->set_data(array('levels' => $manager->get_level_count(), 'levelsdata' => $manager->get_levels_data())); if ($data = $form->get_data()) { if ($manager->get_level_count() != $data['levels']) { // The number of levels have changed, we need to disable the custom badges. $data['enablecustomlevelbadges'] = false; } $manager->update_config($data); $manager = block_xp_manager::get($courseid, true); // Force reload. $manager->recalculate_levels(); redirect(new moodle_url('/blocks/xp/infos.php', array('courseid' => $courseid)), get_string('valuessaved', 'block_xp')); die; } else { if ($form->is_cancelled()) { redirect(new moodle_url('/blocks/xp/infos.php', array('courseid' => $courseid))); die; } } echo $OUTPUT->header(); echo $OUTPUT->heading($strlevels); echo $renderer->navigation($manager, 'levels'); if ($form->is_submitted() && !$form->is_validated() && !$form->no_submit_button_pressed()) { echo $OUTPUT->notification(get_string('errorformvalues', 'block_xp'));
/** * Get content. * * @return stdClass */ public function get_content() { global $DB, $PAGE, $USER; if (isset($this->content)) { return $this->content; } $this->content = new stdClass(); $this->content->text = ''; $this->content->footer = ''; $context = $this->page->context; $canearnxp = has_capability('block/xp:earnxp', $context); $canedit = has_capability('block/xp:addinstance', $context); // Hide the block to non-logged in users, guests and those who cannot earn XP or edit the block. if (!$USER->id || isguestuser() || !$canearnxp && !$canedit) { return $this->content; } $manager = block_xp_manager::get($this->page->course->id); $progress = $manager->get_progress_for_user($USER->id); $renderer = $this->page->get_renderer('block_xp'); $currentlevelmethod = $manager->get_config('enablecustomlevelbadges') ? 'custom_current_level' : 'current_level'; $this->content->text = $renderer->{$currentlevelmethod}($progress); $this->content->text .= $renderer->progress_bar($progress); if (isset($this->config->description)) { $this->content->text .= $renderer->description($this->config->description); } else { $this->content->text .= $renderer->description(get_string('participatetolevelup', 'block_xp')); } $this->content->footer .= $renderer->student_links($this->page->course->id, $manager->get_config('enableladder'), $manager->get_config('enableinfos')); if (has_capability('block/xp:addinstance', $context)) { $this->content->footer .= $renderer->admin_links($this->page->course->id); if (!$manager->get_config('enabled')) { $this->content->footer .= html_writer::tag('p', html_writer::tag('small', get_string('xpgaindisabled', 'block_xp')), array('class' => 'alert alert-warning')); } } // We should be congratulating the user because they leveled up! if (get_user_preferences($manager::USERPREF_NOTIFY, false)) { $args = array('badge' => $renderer->{$currentlevelmethod}($progress), 'headline' => get_string('youreachedlevela', 'block_xp', $progress->level), 'level' => $progress->level); $PAGE->requires->yui_module('moodle-block_xp-notification', 'Y.M.block_xp.Notification.init', array($args)); $PAGE->requires->strings_for_js(array('coolthanks', 'congratulationsyouleveledup'), 'block_xp'); // Reset the value of the user preference. We could potentially do that from JS so that if // the user does not stay on the page long enough they'd be notified the next time they access // the course page, but that's probably an overkill for now. unset_user_preference($manager::USERPREF_NOTIFY); } return $this->content; }
/** * Observe the events, and dispatch them if necessary. * * @param \core\event\base $event The event. * @return void */ public static function observer(\core\event\base $event) { global $CFG; static $allowedcontexts = null; if ($allowedcontexts === null) { $allowedcontexts = array(CONTEXT_COURSE, CONTEXT_MODULE); if (isset($CFG->block_xp_context) && $CFG->block_xp_context == CONTEXT_SYSTEM) { $allowedcontexts[] = CONTEXT_SYSTEM; } } if ($event->component === 'block_xp') { // Skip own events. } else { if (!$event->userid || isguestuser($event->userid) || is_siteadmin($event->userid)) { // Skip non-logged in users and guests. } else { if ($event->anonymous) { // Skip all the events marked as anonymous. } else { if (!in_array($event->contextlevel, $allowedcontexts)) { // Ignore events that are not in the right context. } else { if ($event->edulevel !== \core\event\base::LEVEL_PARTICIPATING) { // Ignore events that are not participating. } else { if (!has_capability('block/xp:earnxp', $event->get_context(), $event->userid)) { // Skip the events if the user does not have the capability to earn XP. } else { // Keep the event, and proceed. $manager = block_xp_manager::get($event->courseid); $manager->capture_event($event); } } } } } } }
/** * Get content. * * @return stdClass */ public function get_content() { global $DB, $PAGE, $USER; if (isset($this->content)) { return $this->content; } $this->content = new stdClass(); $this->content->text = ''; $this->content->footer = ''; $manager = block_xp_manager::get($this->page->course->id); $canview = $manager->can_view(); $canedit = $manager->can_manage(); // Hide the block to non-logged in users, guests and those who cannot view the block. if (!$USER->id || isguestuser() || !$canview) { return $this->content; } $progress = $manager->get_progress_for_user($USER->id); $renderer = $this->page->get_renderer('block_xp'); $currentlevelmethod = $manager->get_config('enablecustomlevelbadges') ? 'custom_current_level' : 'current_level'; $this->content->text = $renderer->{$currentlevelmethod}($progress); $this->content->text .= $renderer->progress_bar($progress); if (isset($this->config->description)) { $this->content->text .= $renderer->description($this->config->description); } else { $this->content->text .= $renderer->description(get_string('participatetolevelup', 'block_xp')); } $this->content->footer .= $renderer->student_links($manager->get_courseid(), $manager->can_view_ladder_page(), $manager->can_view_infos_page()); if ($canedit) { $this->content->footer .= $renderer->admin_links($manager->get_courseid()); if (!$manager->get_config('enabled')) { $this->content->footer .= html_writer::tag('p', html_writer::tag('small', get_string('xpgaindisabled', 'block_xp')), array('class' => 'alert alert-warning')); } } // We should be congratulating the user because they leveled up! // Also resets the flag. We could potentially do that from JS so that if the user does not // stay on the page long enough they'd be notified the next time they access the course page, // but that's probably an overkill for now. if ($manager->has_levelled_up($USER->id)) { $args = array('badge' => $renderer->{$currentlevelmethod}($progress), 'headline' => get_string('youreachedlevela', 'block_xp', $progress->level), 'level' => $progress->level); $PAGE->requires->yui_module('moodle-block_xp-notification', 'Y.M.block_xp.Notification.init', array($args)); $PAGE->requires->strings_for_js(array('coolthanks', 'congratulationsyouleveledup'), 'block_xp'); } return $this->content; }
/** * Gets additional parameters for the plugin's initInner function. * * @param stdClass $course Course object * @param \cm_info $cm Course-module currently being edited (null if none) * @param \section_info $section Section currently being edited (null if none) * @return array Array of parameters for the JavaScript function */ protected function get_javascript_init_params($course, \cm_info $cm = null, \section_info $section = null) { $xpman = \block_xp_manager::get($course->id); return array((object) array('levels' => (int) $xpman->get_level_count())); }