--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Lets users manage data formats
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net)
+ * @package core
+ * @subpackage dataformat
+ */
+
+require_once('../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+
+$action = required_param('action', PARAM_ALPHANUMEXT);
+$name = required_param('name', PARAM_PLUGIN);
+
+$syscontext = context_system::instance();
+$PAGE->set_url('/admin/dataformats.php');
+$PAGE->set_context($syscontext);
+
+require_login();
+require_capability('moodle/site:config', $syscontext);
+require_sesskey();
+
+$return = new moodle_url('/admin/settings.php', array('section' => 'managedataformats'));
+
+$plugins = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
+$sortorder = array_flip(array_keys($plugins));
+
+if (!isset($plugins[$name])) {
+ print_error('courseformatnotfound', 'error', $return, $name);
+}
+
+switch ($action) {
+ case 'disable':
+ if ($plugins[$name]->is_enabled()) {
+ set_config('disabled', 1, 'dataformat_'. $name);
+ core_plugin_manager::reset_caches();
+ }
+ break;
+ case 'enable':
+ if (!$plugins[$name]->is_enabled()) {
+ unset_config('disabled', 'dataformat_'. $name);
+ core_plugin_manager::reset_caches();
+ }
+ break;
+ case 'up':
+ if ($sortorder[$name]) {
+ $currentindex = $sortorder[$name];
+ $seq = array_keys($plugins);
+ $seq[$currentindex] = $seq[$currentindex - 1];
+ $seq[$currentindex - 1] = $name;
+ set_config('dataformat_plugins_sortorder', implode(',', $seq));
+ }
+ break;
+ case 'down':
+ if ($sortorder[$name] < count($sortorder) - 1) {
+ $currentindex = $sortorder[$name];
+ $seq = array_keys($plugins);
+ $seq[$currentindex] = $seq[$currentindex + 1];
+ $seq[$currentindex + 1] = $name;
+ set_config('dataformat_plugins_sortorder', implode(',', $seq));
+ }
+ break;
+}
+redirect($return);
+
</PHP_EXTENSION>
<PHP_EXTENSION name="xml" level="required">
</PHP_EXTENSION>
+ <PHP_EXTENSION name="xmlreader" level="required">
+ </PHP_EXTENSION>
<PHP_EXTENSION name="intl" level="optional">
<FEEDBACK>
<ON_CHECK message="intlrecommended" />
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * File.
+ *
+ * @package core_competency
+ * @copyright 2016 Frédéric Massart - FMCorz.net
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+// Save processing when the user will not be able to access anything.
+if (has_capability('moodle/site:config', $systemcontext)) {
+
+ $parentname = 'competencies';
+
+ // Settings page.
+ $settings = new admin_settingpage('competencysettings', new lang_string('competenciessettings', 'core_competency'),
+ 'moodle/site:config', false);
+ $ADMIN->add($parentname, $settings);
+
+ // Load the full tree of settings.
+ if ($ADMIN->fulltree) {
+ $setting = new admin_setting_configcheckbox('core_competency/enabled',
+ new lang_string('enablecompetencies', 'core_competency'),
+ new lang_string('enablecompetencies_desc', 'core_competency'), 1);
+ $settings->add($setting);
+
+ $setting = new admin_setting_configcheckbox('core_competency/pushcourseratingstouserplans',
+ new lang_string('pushcourseratingstouserplans', 'core_competency'),
+ new lang_string('pushcourseratingstouserplans_desc', 'core_competency'), 1);
+ $settings->add($setting);
+ }
+
+}
$plugin->load_settings($ADMIN, 'filtersettings', $hassiteconfig);
}
+ // Data format settings.
+ $ADMIN->add('modules', new admin_category('dataformatsettings', new lang_string('dataformats')));
+ $temp = new admin_settingpage('managedataformats', new lang_string('managedataformats'));
+ $temp->add(new admin_setting_managedataformats());
+ $ADMIN->add('dataformatsettings', $temp);
//== Portfolio settings ==
require_once($CFG->libdir. '/portfoliolib.php');
$ADMIN->add('root', new admin_category('users', new lang_string('users','admin')));
$ADMIN->add('root', new admin_category('courses', new lang_string('courses','admin')));
$ADMIN->add('root', new admin_category('grades', new lang_string('grades')));
+$ADMIN->add('root', new admin_category('competencies', new lang_string('competencies', 'core_competency')));
$ADMIN->add('root', new admin_category('badges', new lang_string('badges'), empty($CFG->enablebadges)));
$ADMIN->add('root', new admin_category('location', new lang_string('location','admin')));
$ADMIN->add('root', new admin_category('language', new lang_string('language')));
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Class exposing the api for the cohortroles tool.
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_cohortroles;
+
+use stdClass;
+use context_system;
+use core_competency\invalid_persistent_exception;
+
+/**
+ * Class for doing things with cohort roles.
+ *
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class api {
+
+ /**
+ * Create a cohort role assignment from a record containing all the data for the class.
+ *
+ * Requires moodle/role:manage capability at the system context.
+ *
+ * @param stdClass $record Record containing all the data for an instance of the class.
+ * @return competency
+ */
+ public static function create_cohort_role_assignment(stdClass $record) {
+ $cohortroleassignment = new cohort_role_assignment(0, $record);
+ $context = context_system::instance();
+
+ // First we do a permissions check.
+ require_capability('moodle/role:manage', $context);
+
+ // Validate before we check for existing records.
+ if (!$cohortroleassignment->is_valid()) {
+ throw new invalid_persistent_exception($cohortroleassignment->get_errors());
+ }
+
+ $existing = cohort_role_assignment::get_record((array) $record);
+ if (!empty($existing)) {
+ return false;
+ } else {
+ // OK - all set.
+ $cohortroleassignment->create();
+ }
+ return $cohortroleassignment;
+ }
+
+ /**
+ * Delete a cohort role assignment by id.
+ *
+ * Requires moodle/role:manage capability at the system context.
+ *
+ * @param int $id The record to delete. This will also remove this role from the user for all users in the system.
+ * @return boolean
+ */
+ public static function delete_cohort_role_assignment($id) {
+ $cohortroleassignment = new cohort_role_assignment($id);
+ $context = context_system::instance();
+
+ // First we do a permissions check.
+ require_capability('moodle/role:manage', $context);
+
+ // OK - all set.
+ return $cohortroleassignment->delete();
+ }
+
+ /**
+ * Perform a search based on the provided filters and return a paginated list of records.
+ *
+ * Requires moodle/role:manage capability at the system context.
+ *
+ * @param string $sort The column to sort on
+ * @param string $order ('ASC' or 'DESC')
+ * @param int $skip Number of records to skip (pagination)
+ * @param int $limit Max of records to return (pagination)
+ * @return array of cohort_role_assignment
+ */
+ public static function list_cohort_role_assignments($sort = '', $order = 'ASC', $skip = 0, $limit = 0) {
+ $context = context_system::instance();
+
+ // First we do a permissions check.
+ require_capability('moodle/role:manage', $context);
+
+ // OK - all set.
+ return cohort_role_assignment::get_records(array(), $sort, $order, $skip, $limit);
+ }
+
+ /**
+ * Perform a search based on the provided filters and return a paginated list of records.
+ *
+ * Requires moodle/role:manage capability at system context.
+ *
+ * @return int
+ */
+ public static function count_cohort_role_assignments() {
+ $context = context_system::instance();
+
+ // First we do a permissions check.
+ require_capability('moodle/role:manage', $context);
+
+ // OK - all set.
+ return cohort_role_assignment::count_records();
+ }
+
+ /**
+ * Sync all roles - adding and deleting role assignments as required.
+ *
+ * Slow. Should only be called from a background task.
+ *
+ * Requires moodle/role:manage capability at the system context.
+ *
+ * @return array('rolesadded' => array of (useridassignedto, useridassignedover, roleid),
+ * 'rolesremoved' => array of (useridassignedto, useridassignedover, roleid))
+ */
+ public static function sync_all_cohort_roles() {
+ global $DB;
+
+ $context = context_system::instance();
+
+ // First we do a permissions check.
+ require_capability('moodle/role:manage', $context);
+
+ // Ok ready to go.
+ $rolesadded = array();
+ $rolesremoved = array();
+
+ // Get all cohort role assignments and group them by user and role.
+ $all = cohort_role_assignment::get_records(array(), 'userid, roleid');
+ // We build an better structure to loop on.
+ $info = array();
+ foreach ($all as $cra) {
+ if (!isset($info[$cra->get_userid()])) {
+ $info[$cra->get_userid()] = array();
+ }
+ if (!isset($info[$cra->get_userid()][$cra->get_roleid()])) {
+ $info[$cra->get_userid()][$cra->get_roleid()] = array();
+ }
+ array_push($info[$cra->get_userid()][$cra->get_roleid()], $cra->get_cohortid());
+ }
+ // Then for each user+role combo - find user context in the cohort without a role assigned.
+
+ foreach ($info as $userid => $roles) {
+ foreach ($roles as $roleid => $cohorts) {
+ list($cohortsql, $params) = $DB->get_in_or_equal($cohorts, SQL_PARAMS_NAMED);
+
+ $params['usercontext'] = CONTEXT_USER;
+ $params['roleid'] = $roleid;
+ $params['userid'] = $userid;
+
+ $sql = 'SELECT u.id AS userid, ra.id, ctx.id AS contextid
+ FROM {user} u
+ JOIN {cohort_members} cm ON u.id = cm.userid
+ JOIN {context} ctx ON u.id = ctx.instanceid AND ctx.contextlevel = :usercontext
+ LEFT JOIN {role_assignments} ra ON ra.contextid = ctx.id
+ AND ra.roleid = :roleid
+ AND ra.userid = :userid
+ WHERE cm.cohortid ' . $cohortsql . '
+ AND ra.id IS NULL';
+
+ $toadd = $DB->get_records_sql($sql, $params);
+
+ foreach ($toadd as $add) {
+ role_assign($roleid, $userid, $add->contextid, 'tool_cohortroles');
+ $rolesadded[] = array(
+ 'useridassignedto' => $userid,
+ 'useridassignedover' => $add->userid,
+ 'roleid' => $roleid
+ );
+ }
+ }
+ }
+
+ // And for each user+role combo - find user context not in the cohort with a role assigned.
+ // If the role was assigned by this component, unassign the role.
+ foreach ($info as $userid => $roles) {
+ foreach ($roles as $roleid => $cohorts) {
+ // Now we are looking for entries NOT in the cohort.
+ list($cohortsql, $params) = $DB->get_in_or_equal($cohorts, SQL_PARAMS_NAMED);
+
+ $params['usercontext'] = CONTEXT_USER;
+ $params['roleid'] = $roleid;
+ $params['userid'] = $userid;
+ $params['component'] = 'tool_cohortroles';
+
+ $sql = 'SELECT u.id as userid, ra.id, ctx.id AS contextid
+ FROM {user} u
+ JOIN {context} ctx ON u.id = ctx.instanceid AND ctx.contextlevel = :usercontext
+ JOIN {role_assignments} ra ON ra.contextid = ctx.id AND ra.roleid = :roleid AND ra.userid = :userid
+ LEFT JOIN {cohort_members} cm ON u.id = cm.userid
+ AND cm.cohortid ' . $cohortsql . '
+ WHERE ra.component = :component AND cm.cohortid IS NULL';
+
+ $toremove = $DB->get_records_sql($sql, $params);
+ foreach ($toremove as $remove) {
+ role_unassign($roleid, $userid, $remove->contextid, 'tool_cohortroles');
+ $rolesremoved[] = array(
+ 'useridassignedto' => $userid,
+ 'useridassignedover' => $remove->userid,
+ 'roleid' => $roleid
+ );
+ }
+ }
+ }
+
+ return array('rolesadded' => $rolesadded, 'rolesremoved' => $rolesremoved);
+ }
+
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Class for cohort_role_assignment persistence.
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_cohortroles;
+
+use lang_string;
+use core_competency\persistent;
+
+/**
+ * Class for loading/storing cohort_role_assignments from the DB.
+ *
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cohort_role_assignment extends persistent {
+
+ /** Table name for user_competency persistency */
+ const TABLE = 'tool_cohortroles';
+
+ /**
+ * Return the definition of the properties of this model.
+ *
+ * @return array
+ */
+ protected static function define_properties() {
+ return array(
+ 'userid' => array(
+ 'type' => PARAM_INT,
+ ),
+ 'roleid' => array(
+ 'type' => PARAM_INT,
+ ),
+ 'cohortid' => array(
+ 'type' => PARAM_INT,
+ )
+ );
+ }
+
+ /**
+ * Validate the user ID.
+ *
+ * @param int $value The value.
+ * @return true|lang_string
+ */
+ protected function validate_userid($value) {
+ global $DB;
+
+ if (!$DB->record_exists('user', array('id' => $value))) {
+ return new lang_string('invaliduserid', 'error');
+ }
+
+ return true;
+ }
+
+ /**
+ * Validate the role ID.
+ *
+ * @param int $value The value.
+ * @return true|lang_string
+ */
+ protected function validate_roleid($value) {
+ global $DB;
+
+ if (!$DB->record_exists('role', array('id' => $value))) {
+ return new lang_string('invalidroleid', 'error');
+ }
+
+ return true;
+ }
+
+ /**
+ * Validate the cohort ID.
+ *
+ * @param int $value The value.
+ * @return true|lang_string
+ */
+ protected function validate_cohortid($value) {
+ global $DB;
+
+ if (!$DB->record_exists('cohort', array('id' => $value))) {
+ return new lang_string('invalidcohortid', 'error');
+ }
+
+ return true;
+ }
+
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Assign role to cohort form.
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_cohortroles\form;
+defined('MOODLE_INTERNAL') || die();
+
+use moodleform;
+use context_system;
+
+require_once($CFG->libdir . '/formslib.php');
+
+/**
+ * Assign role to cohort form.
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class assign_role_cohort extends moodleform {
+
+ /**
+ * Form definition.
+ */
+ public function definition() {
+ global $PAGE;
+
+ $mform = $this->_form;
+ $roles = get_roles_for_contextlevels(CONTEXT_USER);
+
+ if (empty($roles)) {
+ $output = $PAGE->get_renderer('tool_cohortroles');
+ $warning = $output->notify_problem(get_string('noassignableroles', 'tool_cohortroles'));
+ $mform->addElement('html', $warning);
+ return;
+ }
+
+ $options = array(
+ 'ajax' => 'tool_lp/form-user-selector',
+ 'multiple' => true
+ );
+ $mform->addElement('autocomplete', 'userids', get_string('selectusers', 'tool_cohortroles'), array(), $options);
+ $mform->addRule('userids', null, 'required');
+
+ $names = role_get_names();
+ $options = array();
+ foreach ($roles as $idx => $roleid) {
+ $options[$roleid] = $names[$roleid]->localname;
+ }
+
+ $mform->addElement('select', 'roleid', get_string('selectrole', 'tool_cohortroles'), $options);
+ $mform->addRule('roleid', null, 'required');
+
+ $context = context_system::instance();
+ $options = array(
+ 'ajax' => 'tool_lp/form-cohort-selector',
+ 'multiple' => true,
+ 'data-contextid' => $context->id,
+ 'data-includes' => 'all'
+ );
+ $mform->addElement('autocomplete', 'cohortids', get_string('selectcohorts', 'tool_cohortroles'), array(), $options);
+ $mform->addRule('cohortids', null, 'required');
+ $mform->addElement('submit', 'submit', get_string('assign', 'tool_cohortroles'));
+ }
+
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Cohort role assignments table
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_cohortroles\output;
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir . '/tablelib.php');
+
+use context_helper;
+use context_system;
+use html_writer;
+use moodle_url;
+use table_sql;
+
+/**
+ * Cohort role assignments table.
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cohort_role_assignments_table extends table_sql {
+
+ /**
+ * Sets up the table.
+ *
+ * @param string $uniqueid Unique id of table.
+ * @param moodle_url $url The base URL.
+ */
+ public function __construct($uniqueid, $url) {
+ global $CFG;
+ parent::__construct($uniqueid);
+ $context = context_system::instance();
+
+ $this->context = $context;
+
+ $this->rolenames = role_get_names();
+
+ // This object should not be used without the right permissions.
+ require_capability('moodle/role:manage', $context);
+
+ $this->useridfield = 'userid';
+
+ // Define columns in the table.
+ $this->define_table_columns();
+
+ $this->define_baseurl($url);
+ // Define configs.
+ $this->define_table_configs();
+ }
+
+ /**
+ * Role name column.
+ *
+ * @param array $data Row data.
+ * @return string
+ */
+ protected function col_rolename($data) {
+ return $this->rolenames[$data->roleid]->localname;
+ }
+
+ /**
+ * Cohort name column.
+ *
+ * @param array $data Row data.
+ * @return string
+ */
+ protected function col_cohortname($data) {
+ global $OUTPUT;
+
+ $record = (object) array(
+ 'id' => $data->cohortid,
+ 'idnumber' => $data->cohortidnumber,
+ 'description' => $data->cohortdescription,
+ 'visible' => $data->cohortvisible,
+ 'name' => $data->cohortname
+ );
+ $context = context_helper::instance_by_id($data->cohortcontextid);
+
+ $exporter = new \tool_lp\external\cohort_summary_exporter($record, array('context' => $context));
+ $cohort = $exporter->export($OUTPUT);
+
+ $html = $OUTPUT->render_from_template('tool_cohortroles/cohort-in-list', $cohort);
+ return $html;
+ }
+
+ /**
+ * Actions column.
+ *
+ * @param array $data Row data.
+ * @return string
+ */
+ protected function col_actions($data) {
+ global $OUTPUT;
+
+ $action = new \confirm_action(get_string('removecohortroleassignmentconfirm', 'tool_cohortroles'));
+ $url = new moodle_url($this->baseurl);
+ $url->params(array('removecohortroleassignment' => $data->id, 'sesskey' => sesskey()));
+ $pix = new \pix_icon('t/delete', get_string('removecohortroleassignment', 'tool_cohortroles'));
+ return $OUTPUT->action_link($url, '', $action, null, $pix);
+ }
+
+ /**
+ * Setup the headers for the table.
+ */
+ protected function define_table_columns() {
+ $extrafields = get_extra_user_fields($this->context);
+
+ // Define headers and columns.
+ $cols = array(
+ 'cohortname' => get_string('cohort', 'cohort'),
+ 'rolename' => get_string('role'),
+ 'fullname' => get_string('name'),
+ );
+
+ // Add headers for extra user fields.
+ foreach ($extrafields as $field) {
+ if (get_string_manager()->string_exists($field, 'moodle')) {
+ $cols[$field] = get_string($field);
+ } else {
+ $cols[$field] = $field;
+ }
+ }
+
+ // Add remaining headers.
+ $cols = array_merge($cols, array('actions' => get_string('actions')));
+
+ $this->define_columns(array_keys($cols));
+ $this->define_headers(array_values($cols));
+ }
+
+ /**
+ * Define table configs.
+ */
+ protected function define_table_configs() {
+ $this->collapsible(false);
+ $this->sortable(true, 'lastname', SORT_ASC);
+ $this->pageable(true);
+ $this->no_sorting('actions');
+ }
+
+ /**
+ * Builds the SQL query.
+ *
+ * @param bool $count When true, return the count SQL.
+ * @return array containing sql to use and an array of params.
+ */
+ protected function get_sql_and_params($count = false) {
+ $fields = 'uca.id, uca.cohortid, uca.userid, uca.roleid, ';
+ $fields .= 'c.name as cohortname, c.idnumber as cohortidnumber, c.contextid as cohortcontextid, ';
+ $fields .= 'c.visible as cohortvisible, c.description as cohortdescription, ';
+
+ // Add extra user fields that we need for the graded user.
+ $extrafields = get_extra_user_fields($this->context);
+ foreach ($extrafields as $field) {
+ $fields .= 'u.' . $field . ', ';
+ }
+ $fields .= get_all_user_name_fields(true, 'u');
+
+ if ($count) {
+ $select = "COUNT(1)";
+ } else {
+ $select = "$fields";
+ }
+
+ $sql = "SELECT $select
+ FROM {tool_cohortroles} uca
+ JOIN {user} u ON u.id = uca.userid
+ JOIN {cohort} c ON c.id = uca.cohortid";
+ $params = array();
+
+ // Add order by if needed.
+ if (!$count && $sqlsort = $this->get_sql_sort()) {
+ $sql .= " ORDER BY " . $sqlsort;
+ }
+
+ return array($sql, $params);
+ }
+
+ /**
+ * Override the default implementation to set a decent heading level.
+ */
+ public function print_nothing_to_display() {
+ global $OUTPUT;
+ echo $this->render_reset_button();
+ $this->print_initials_bar();
+ echo $OUTPUT->heading(get_string('nothingtodisplay'), 4);
+ }
+
+ /**
+ * Query the DB.
+ *
+ * @param int $pagesize size of page for paginated displayed table.
+ * @param bool $useinitialsbar do you want to use the initials bar.
+ */
+ public function query_db($pagesize, $useinitialsbar = true) {
+ global $DB;
+
+ list($countsql, $countparams) = $this->get_sql_and_params(true);
+ list($sql, $params) = $this->get_sql_and_params();
+ $total = $DB->count_records_sql($countsql, $countparams);
+ $this->pagesize($pagesize, $total);
+ $this->rawdata = $DB->get_records_sql($sql, $params, $this->get_page_start(), $this->get_page_size());
+
+ // Set initial bars.
+ if ($useinitialsbar) {
+ $this->initialbars($total > $pagesize);
+ }
+ }
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Renderer class for cohort roles
+ *
+ * @package tool_cohortroles
+ * @copyright 2016 Issam Taboubi <issam.taboubi@umontreal.ca>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_cohortroles\output;
+
+defined('MOODLE_INTERNAL') || die();
+
+use plugin_renderer_base;
+use renderable;
+
+/**
+ * Renderer class for cohort roles
+ *
+ * @package tool_cohortroles
+ * @copyright 2016 Issam Taboubi <issam.taboubi@umontreal.ca>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class renderer extends plugin_renderer_base {
+
+ /**
+ * Output a nofication.
+ *
+ * @param string $message the message to print out
+ * @return string HTML fragment.
+ * @see \core\output\notification
+ */
+ public function notify_message($message) {
+ $n = new \core\output\notification($message, \core\output\notification::NOTIFY_INFO);
+ return $this->render($n);
+ }
+
+ /**
+ * Output an error notification.
+ *
+ * @param string $message the message to print out
+ * @return string HTML fragment.
+ * @see \core\output\notification
+ */
+ public function notify_problem($message) {
+ $n = new \core\output\notification($message, \core\output\notification::NOTIFY_ERROR);
+ return $this->render($n);
+ }
+
+ /**
+ * Output a success notification.
+ *
+ * @param string $message the message to print out
+ * @return string HTML fragment.
+ * @see \core\output\notification
+ */
+ public function notify_success($message) {
+ $n = new \core\output\notification($message, \core\output\notification::NOTIFY_SUCCESS);
+ return $this->render($n);
+ }
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Scheduled task for syncing cohort roles.
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_cohortroles\task;
+
+use core\task\scheduled_task;
+use tool_cohortroles\api;
+
+/**
+ * Scheduled task for syncing cohort roles.
+ *
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cohort_role_sync extends scheduled_task {
+
+ /**
+ * Get name.
+ * @return string
+ */
+ public function get_name() {
+ // Shown in admin screens.
+ return get_string('taskname', 'tool_cohortroles');
+ }
+
+ /**
+ * Execute.
+ */
+ public function execute() {
+ mtrace('Sync cohort roles...');
+ $result = api::sync_all_cohort_roles();
+
+ mtrace('Added ' . count($result['rolesadded']));
+ mtrace('Removed ' . count($result['rolesremoved']));
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<XMLDB PATH="admin/tool/cohortroles/db" VERSION="20151201" COMMENT="XMLDB file for Moodle admin/tool/cohortroles"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
+>
+ <TABLES>
+ <TABLE NAME="tool_cohortroles" COMMENT="Mapping of users to cohort role assignments.">
+ <FIELDS>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+ <FIELD NAME="cohortid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The cohort to sync"/>
+ <FIELD NAME="roleid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The role to assign"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The user to sync"/>
+ <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The time this record was created"/>
+ <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The time this record was modified."/>
+ <FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Who last modified this record?"/>
+ </FIELDS>
+ <KEYS>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ </KEYS>
+ <INDEXES>
+ <INDEX NAME="cohortuserrole" UNIQUE="true" FIELDS="cohortid, roleid, userid"/>
+ </INDEXES>
+ </TABLE>
+ </TABLES>
+</XMLDB>
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Tasks definitions.
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$tasks = array(
+ array(
+ 'classname' => 'tool_cohortroles\task\cohort_role_sync',
+ 'blocking' => 0,
+ 'minute' => 'R',
+ 'hour' => '*',
+ 'day' => '*',
+ 'dayofweek' => '*',
+ 'month' => '*'
+ ),
+);
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Assign roles for a user.
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(__FILE__) . '/../../../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+require_once($CFG->libdir.'/tablelib.php');
+
+$removeid = optional_param('removecohortroleassignment', 0, PARAM_INT);
+
+admin_externalpage_setup('toolcohortroles');
+$context = context_system::instance();
+
+$pageurl = new moodle_url('/admin/tool/cohortroles/index.php');
+
+$output = $PAGE->get_renderer('tool_cohortroles');
+
+echo $output->header();
+$title = get_string('assignroletocohort', 'tool_cohortroles');
+echo $output->heading($title);
+
+$form = new tool_cohortroles\form\assign_role_cohort();
+
+if ($removeid) {
+ require_sesskey();
+
+ $result = \tool_cohortroles\api::delete_cohort_role_assignment($removeid);
+ if ($result) {
+ $notification = get_string('cohortroleassignmentremoved', 'tool_cohortroles');
+ echo $output->notify_success($notification);
+ } else {
+ $notification = get_string('cohortroleassignmentnotremoved', 'tool_cohortroles');
+ echo $output->notify_problem($notification);
+ }
+ echo $output->continue_button(new moodle_url($pageurl));
+} else if ($data = $form->get_data()) {
+ require_sesskey();
+ // We must create them all or none.
+ $saved = 0;
+ foreach ($data->userids as $userid) {
+ if (empty($data->cohortids)) {
+ $data->cohortids = array();
+ }
+ foreach ($data->cohortids as $cohortid) {
+ $params = (object) array('userid' => $userid, 'cohortid' => $cohortid, 'roleid' => $data->roleid);
+ $result = \tool_cohortroles\api::create_cohort_role_assignment($params);
+ if ($result) {
+ $saved++;
+ }
+ }
+ }
+ if ($saved == 0) {
+ $notification = get_string('nocohortroleassignmentssaved', 'tool_cohortroles');
+ echo $output->notify_problem($notification);
+ } else if ($saved == 1) {
+ $notification = get_string('onecohortroleassignmentsaved', 'tool_cohortroles');
+ echo $output->notify_success($notification);
+ } else {
+ $notification = get_string('acohortroleassignmentssaved', 'tool_cohortroles', $saved);
+ echo $output->notify_success($notification);
+ }
+
+ echo $output->continue_button(new moodle_url($pageurl));
+} else {
+ $form->display();
+
+ $title = get_string('existingcohortroles', 'tool_cohortroles');
+ echo $output->heading($title);
+ $url = new moodle_url('/admin/tool/cohortroles/index.php');
+ $table = new tool_cohortroles\output\cohort_role_assignments_table(uniqid(), $url);
+ echo $table->out(50, true);
+
+ echo $output->spacer();
+ echo $output->notify_message(get_string('backgroundsync', 'tool_cohortroles'));
+}
+
+echo $output->footer();
+
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Strings for component 'tool_userroles', language 'en'
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['acohortroleassignmentssaved'] = '{$a} cohort role assignments were saved.';
+$string['assign'] = 'Assign';
+$string['assignroletocohort'] = 'Assign role to users in the context of all members of these cohorts';
+$string['backgroundsync'] = 'Note: Roles assigned on this page will not take affect immediately, the changes to role assignments will be made by a background task.';
+$string['cohortroleassignmentremoved'] = 'The cohort role assignment was removed.';
+$string['cohortroleassignmentnotremoved'] = 'The cohort role assignment was not removed.';
+$string['cohortroles'] = 'Cohort roles';
+$string['existingcohortroles'] = 'Existing cohort role assignments';
+$string['managecohortroles'] = 'Assign user roles for entire cohort';
+$string['noassignableroles'] = 'No roles can be assigned at a user context level. <a href="../../roles/manage.php">Manage roles</a>';
+$string['nocohortroleassignmentssaved'] = 'No cohort role assignments were saved.';
+$string['onecohortroleassignmentsaved'] = 'One cohort role assignment was saved.';
+$string['pluginname'] = 'Cohort roles management';
+$string['removecohortroleassignment'] = 'Remove cohort role assignment';
+$string['removecohortroleassignmentconfirm'] = 'Are you sure you want to remove this cohort role assignment? This role will be removed for this user in all other user contexts.';
+$string['selectcohorts'] = 'Select cohorts';
+$string['selectrole'] = 'Select role';
+$string['selectusers'] = 'Select users to assign role';
+$string['taskname'] = 'Sync cohort role assignments';
+$string['thisuserroles'] = 'Roles assigned relative to this user';
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Link to user roles management.
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+$str = get_string('managecohortroles', 'tool_cohortroles');
+$url = new moodle_url('/admin/tool/cohortroles/index.php');
+$ADMIN->add('roles', new admin_externalpage('toolcohortroles', $str, $url, 'moodle/role:manage'));
--- /dev/null
+{{!
+ This file is part of Moodle - http://moodle.org/
+
+ Moodle is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Moodle is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+ @template tool_cohortroles/cohort-in-list
+
+ Moodle template for displaying a cohort in a list.
+
+ Classes required for JS:
+ * none
+
+ Data attributes required for JS:
+ * none
+
+ Context variables required for this template:
+ * id cohort id field
+ * name cohort name field
+ * idnumber cohort idnumber field
+ * description cohort description field
+ * visible cohort visible field
+
+ Example context (json):
+ { "id": "1",
+ "name": "Cohort 1",
+ "visible": true,
+ "idnumber": "014",
+ "description": "Some users"
+ }
+}}
+{{> tool_lp/form-cohort-selector-suggestion }}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * API tests.
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+use tool_cohortroles\api;
+
+/**
+ * API tests.
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_cohortroles_api_testcase extends advanced_testcase {
+ /** @var stdClass $cohort */
+ protected $cohort = null;
+
+ /** @var stdClass $userassignto */
+ protected $userassignto = null;
+
+ /** @var stdClass $userassignover */
+ protected $userassignover = null;
+
+ /** @var stdClass $role */
+ protected $role = null;
+
+ /**
+ * Setup function- we will create a course and add an assign instance to it.
+ */
+ protected function setUp() {
+ global $DB;
+
+ $this->resetAfterTest(true);
+
+ // Create some users.
+ $this->cohort = $this->getDataGenerator()->create_cohort();
+ $this->userassignto = $this->getDataGenerator()->create_user();
+ $this->userassignover = $this->getDataGenerator()->create_user();
+ $this->roleid = create_role('Sausage Roll', 'sausageroll', 'mmmm');
+ cohort_add_member($this->cohort->id, $this->userassignover->id);
+ }
+
+
+ public function test_create_cohort_role_assignment_without_permission() {
+ $this->setExpectedException('required_capability_exception');
+ $this->setUser($this->userassignto);
+ $params = (object) array(
+ 'userid' => $this->userassignto->id,
+ 'roleid' => $this->roleid,
+ 'cohortid' => $this->cohort->id
+ );
+ api::create_cohort_role_assignment($params);
+ }
+
+ public function test_create_cohort_role_assignment_with_invalid_data() {
+ $this->setExpectedException('core_competency\invalid_persistent_exception');
+ $this->setAdminUser();
+ $params = (object) array(
+ 'userid' => $this->userassignto->id,
+ 'roleid' => -8,
+ 'cohortid' => $this->cohort->id
+ );
+ api::create_cohort_role_assignment($params);
+ }
+
+ public function test_create_cohort_role_assignment() {
+ $this->setAdminUser();
+ $params = (object) array(
+ 'userid' => $this->userassignto->id,
+ 'roleid' => $this->roleid,
+ 'cohortid' => $this->cohort->id
+ );
+ $result = api::create_cohort_role_assignment($params);
+ $this->assertNotEmpty($result->get_id());
+ $this->assertEquals($result->get_userid(), $this->userassignto->id);
+ $this->assertEquals($result->get_roleid(), $this->roleid);
+ $this->assertEquals($result->get_cohortid(), $this->cohort->id);
+ }
+
+ public function test_delete_cohort_role_assignment_without_permission() {
+ $this->setAdminUser();
+ $params = (object) array(
+ 'userid' => $this->userassignto->id,
+ 'roleid' => $this->roleid,
+ 'cohortid' => $this->cohort->id
+ );
+ $result = api::create_cohort_role_assignment($params);
+ $this->setExpectedException('required_capability_exception');
+ $this->setUser($this->userassignto);
+ api::delete_cohort_role_assignment($result->get_id());
+ }
+
+ public function test_delete_cohort_role_assignment_with_invalid_data() {
+ $this->setAdminUser();
+ $params = (object) array(
+ 'userid' => $this->userassignto->id,
+ 'roleid' => $this->roleid,
+ 'cohortid' => $this->cohort->id
+ );
+ $result = api::create_cohort_role_assignment($params);
+ $this->setExpectedException('dml_missing_record_exception');
+ api::delete_cohort_role_assignment($result->get_id() + 1);
+ }
+
+ public function test_delete_cohort_role_assignment() {
+ $this->setAdminUser();
+ $params = (object) array(
+ 'userid' => $this->userassignto->id,
+ 'roleid' => $this->roleid,
+ 'cohortid' => $this->cohort->id
+ );
+ $result = api::create_cohort_role_assignment($params);
+ $worked = api::delete_cohort_role_assignment($result->get_id());
+ $this->assertTrue($worked);
+ }
+
+ public function test_list_cohort_role_assignments() {
+ $this->setAdminUser();
+ $params = (object) array(
+ 'userid' => $this->userassignto->id,
+ 'roleid' => $this->roleid,
+ 'cohortid' => $this->cohort->id
+ );
+ $result = api::create_cohort_role_assignment($params);
+
+ $list = api::list_cohort_role_assignments();
+ $list[0]->is_valid();
+ $this->assertEquals($list[0], $result);
+ }
+
+ public function test_count_cohort_role_assignments() {
+ $this->setAdminUser();
+ $params = (object) array(
+ 'userid' => $this->userassignto->id,
+ 'roleid' => $this->roleid,
+ 'cohortid' => $this->cohort->id
+ );
+ $result = api::create_cohort_role_assignment($params);
+
+ $count = api::count_cohort_role_assignments();
+ $this->assertEquals($count, 1);
+ }
+
+ public function test_sync_all_cohort_roles() {
+ $this->setAdminUser();
+ $params = (object) array(
+ 'userid' => $this->userassignto->id,
+ 'roleid' => $this->roleid,
+ 'cohortid' => $this->cohort->id
+ );
+ $result = api::create_cohort_role_assignment($params);
+
+ // Verify roles are assigned when users enter the cohort.
+ $sync = api::sync_all_cohort_roles();
+
+ $rolesadded = array(array(
+ 'useridassignedto' => $this->userassignto->id,
+ 'useridassignedover' => $this->userassignover->id,
+ 'roleid' => $this->roleid
+ ));
+ $rolesremoved = array();
+ $expected = array('rolesadded' => $rolesadded,
+ 'rolesremoved' => $rolesremoved);
+ $this->assertEquals($sync, $expected);
+
+ // Verify roles are removed when users leave the cohort.
+ cohort_remove_member($this->cohort->id, $this->userassignover->id);
+ $sync = api::sync_all_cohort_roles();
+
+ $rolesadded = array();
+ $rolesremoved = array(array(
+ 'useridassignedto' => $this->userassignto->id,
+ 'useridassignedover' => $this->userassignover->id,
+ 'roleid' => $this->roleid
+ ));
+ $expected = array('rolesadded' => $rolesadded,
+ 'rolesremoved' => $rolesremoved);
+ $this->assertEquals($sync, $expected);
+
+ // Verify roles assigned by any other component are not removed.
+ $usercontext = context_user::instance($this->userassignover->id);
+ role_assign($this->roleid, $this->userassignto->id, $usercontext->id);
+ $sync = api::sync_all_cohort_roles();
+
+ $rolesadded = array();
+ $rolesremoved = array();
+ $expected = array('rolesadded' => $rolesadded,
+ 'rolesremoved' => $rolesremoved);
+ $this->assertEquals($sync, $expected);
+
+ // Remove manual role assignment.
+ role_unassign($this->roleid, $this->userassignto->id, $usercontext->id);
+ // Add someone to the cohort again...
+ cohort_add_member($this->cohort->id, $this->userassignover->id);
+ $sync = api::sync_all_cohort_roles();
+ $rolesadded = array(array(
+ 'useridassignedto' => $this->userassignto->id,
+ 'useridassignedover' => $this->userassignover->id,
+ 'roleid' => $this->roleid
+ ));
+ $rolesremoved = array();
+ $expected = array('rolesadded' => $rolesadded,
+ 'rolesremoved' => $rolesremoved);
+ $this->assertEquals($sync, $expected);
+
+ // Verify no fatal errors when a cohort is deleted.
+ cohort_delete_cohort($this->cohort);
+ $sync = api::sync_all_cohort_roles();
+
+ $rolesadded = array();
+ $rolesremoved = array(array(
+ 'useridassignedto' => $this->userassignto->id,
+ 'useridassignedover' => $this->userassignover->id,
+ 'roleid' => $this->roleid
+ ));
+ $expected = array('rolesadded' => $rolesadded,
+ 'rolesremoved' => $rolesremoved);
+ $this->assertEquals($sync, $expected);
+ }
+
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Plugin version info
+ *
+ * @package tool_cohortroles
+ * @copyright 2015 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+$plugin->version = 2015111018; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2014110400; // Requires this Moodle version.
+$plugin->component = 'tool_cohortroles'; // Full name of the plugin (used for diagnostics).
+
+$plugin->dependencies = array(
+ 'tool_lp' => ANY_VERSION
+);
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ <KEY NAME="contextid" TYPE="foreign" FIELDS="contextid" REFTABLE="context" REFFIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="timecreated" UNIQUE="false" FIELDS="timecreated"/>
defined('MOODLE_INTERNAL') || die();
function xmldb_logstore_standard_upgrade($oldversion) {
- global $CFG;
+ global $CFG, $DB;
+
+ $dbman = $DB->get_manager();
// Moodle v2.8.0 release upgrade line.
// Put any upgrade step following this.
// Moodle v3.0.0 release upgrade line.
// Put any upgrade step following this.
+ if ($oldversion < 2016041200) {
+ // This could take a long time. Unfortunately, no way to know how long, and no way to do progress, so setting for 1 hour.
+ upgrade_set_timeout(3600);
+
+ // Define key contextid (foreign) to be added to logstore_standard_log.
+ $table = new xmldb_table('logstore_standard_log');
+ $key = new xmldb_key('contextid', XMLDB_KEY_FOREIGN, array('contextid'), 'context', array('id'));
+
+ // Launch add key contextid.
+ $dbman->add_key($table, $key);
+
+ // Standard savepoint reached.
+ upgrade_plugin_savepoint(true, 2016041200, 'logstore', 'standard');
+ }
+
return true;
}
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2015111600; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version = 2016041200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2015111000; // Requires this Moodle version.
$plugin->component = 'logstore_standard'; // Full name of the plugin (used for diagnostics).
--- /dev/null
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Action selector.
+ *
+ * To handle 'save' events use: actionselector.on('save')
+ * This will receive the information to display in popup.
+ * The actions have the format [{'text': sometext, 'value' : somevalue}].
+ *
+ * @package tool_lp
+ * @copyright 2016 Serge Gauthier - <serge.gauthier.2@umontreal.ca>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery',
+ 'core/notification',
+ 'core/ajax',
+ 'core/templates',
+ 'tool_lp/dialogue',
+ 'tool_lp/event_base'],
+ function($, Notification, Ajax, Templates, Dialogue, EventBase) {
+
+ /**
+ * Action selector class.
+ * @param {String} title The title of popup.
+ * @param {String} message The message to display.
+ * @param {object} actions The actions that can be selected.
+ * @param {String} confirm Text for confirm button.
+ * @param {String} cancel Text for cancel button.
+ */
+ var ActionSelector = function(title, message, actions, confirm, cancel) {
+ var self = this;
+
+ EventBase.prototype.constructor.apply(this, []);
+ self._title = title;
+ self._message = message;
+ self._actions = actions;
+ self._confirm = confirm;
+ self._cancel = cancel;
+ self._selectedValue = null;
+ self._reset();
+ };
+
+ ActionSelector.prototype = Object.create(EventBase.prototype);
+
+ /** @type {String} The value that was selected. */
+ ActionSelector.prototype._selectedValue = null;
+ /** @type {Dialogue} The reference to the dialogue. */
+ ActionSelector.prototype._popup = null;
+ /** @type {String} The title of popup. */
+ ActionSelector.prototype._title = null;
+ /** @type {String} The message in popup. */
+ ActionSelector.prototype._message = null;
+ /** @type {object} The information for radion buttons. */
+ ActionSelector.prototype._actions = null;
+ /** @type {String} The text for confirm button. */
+ ActionSelector.prototype._confirm = null;
+ /** @type {String} The text for cancel button. */
+ ActionSelector.prototype._cancel = null;
+
+ /**
+ * Hook to executed after the view is rendered.
+ *
+ * @method _afterRender
+ */
+ ActionSelector.prototype._afterRender = function() {
+ var self = this;
+
+ // Confirm button is disabled until a choice is done.
+ self._find('[data-action="action-selector-confirm"]').attr('disabled', 'disabled');
+
+ // Add listener for radio buttons change.
+ self._find('[data-region="action-selector-radio-buttons"]').change(function() {
+ self._selectedValue = $("input[type='radio']:checked").val();
+ self._find('[data-action="action-selector-confirm"]').removeAttr('disabled');
+ self._refresh.bind(self);
+ }.bind(self));
+
+ // Add listener for cancel.
+ self._find('[data-action="action-selector-cancel"]').click(function(e) {
+ e.preventDefault();
+ self.close();
+ }.bind(self));
+
+ // Add listener for confirm.
+ self._find('[data-action="action-selector-confirm"]').click(function(e) {
+ e.preventDefault();
+ if (!self._selectedValue.length) {
+ return;
+ }
+ self._trigger('save', { action: self._selectedValue });
+ self.close();
+ }.bind(self));
+ };
+
+ /**
+ * Close the dialogue.
+ *
+ * @method close
+ */
+ ActionSelector.prototype.close = function() {
+ var self = this;
+ self._popup.close();
+ self._reset();
+ };
+
+ /**
+ * Opens the action selector.
+ *
+ * @method display
+ * @return {Promise}
+ */
+ ActionSelector.prototype.display = function() {
+ var self = this;
+ return self._render().then(function(html) {
+ self._popup = new Dialogue(
+ self._title,
+ html,
+ self._afterRender.bind(self)
+ );
+ }.bind(self)).fail(Notification.exception);
+ };
+
+ /**
+ * Find a node in the dialogue.
+ *
+ * @param {String} selector
+ * @method _find
+ */
+ ActionSelector.prototype._find = function(selector) {
+ return $(this._popup.getContent()).find(selector);
+ };
+
+ /**
+ * Refresh the view.
+ *
+ * @method _refresh
+ * @return {Promise}
+ */
+ ActionSelector.prototype._refresh = function() {
+ var self = this;
+ return self._render().then(function(html) {
+ self._find('[data-region="action-selector"]').replaceWith(html);
+ self._afterRender();
+ }.bind(self));
+ };
+
+ /**
+ * Render the dialogue.
+ *
+ * @method _render
+ * @return {Promise}
+ */
+ ActionSelector.prototype._render = function() {
+ var self = this;
+ var choices = [];
+ for (var i in self._actions) {
+ choices.push(self._actions[i]);
+ }
+ var content = {'message': self._message, 'choices' : choices,
+ 'confirm' : self._confirm, 'cancel' : self._cancel};
+
+ return Templates.render('tool_lp/action_selector', content);
+ };
+
+ /**
+ * Reset the dialogue properties.
+ *
+ * This does not reset everything, just enough to reset the UI.
+ *
+ * @method _reset
+ */
+ ActionSelector.prototype._reset = function() {
+ this._popup = null;
+ this._selectedValue = '';
+ };
+
+ return /** @alias module:tool_lp/actionselector */ ActionSelector;
+
+});
--- /dev/null
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Handle add/remove competency links.
+ *
+ * @module tool_lp/competencies
+ * @package tool_lp
+ * @copyright 2015 Damyon Wiese <damyon@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery',
+ 'core/notification',
+ 'core/ajax',
+ 'core/templates',
+ 'core/str',
+ 'tool_lp/competencypicker',
+ 'tool_lp/dragdrop-reorder'],
+ function($, notification, ajax, templates, str, Picker, dragdrop) {
+
+ /**
+ * Constructor
+ *
+ * @param {Number} itemid
+ * @param {String} itemtype
+ */
+ var competencies = function(itemid, itemtype, pagectxid) {
+ this.itemid = itemid;
+ this.itemtype = itemtype;
+ this.pageContextId = pagectxid;
+ this.pickerInstance = null;
+
+ $('[data-region="actions"] button').prop('disabled', false);
+ this.registerEvents();
+ this.registerDragDrop();
+ };
+
+ /**
+ * Initialise the drag/drop code.
+ * @method registerDragDrop
+ */
+ competencies.prototype.registerDragDrop = function() {
+ var localthis = this;
+ // Init this module.
+ str.get_string('movecompetency', 'tool_lp').done(
+ function(movestring) {
+ dragdrop.dragdrop('movecompetency',
+ movestring,
+ { identifier: 'movecompetency', component: 'tool_lp'},
+ { identifier: 'movecompetencyafter', component: 'tool_lp'},
+ 'drag-samenode',
+ 'drag-parentnode',
+ 'drag-handlecontainer',
+ function(drag, drop) {
+ localthis.handleDrop.call(localthis, drag, drop);
+ });
+ }
+ ).fail(notification.exception);
+
+ };
+
+ /**
+ * Handle a drop from a drag/drop operation.
+ *
+ * @method handleDrop
+ * @param {DOMNode} drag The dragged node.
+ * @param {DOMNode} drop The dropped on node.
+ */
+ competencies.prototype.handleDrop = function(drag, drop) {
+ var fromid = $(drag).data('id');
+ var toid = $(drop).data('id');
+ var localthis = this;
+ var requests = [];
+
+ if (localthis.itemtype == 'course') {
+ requests = ajax.call([
+ {
+ methodname: 'core_competency_reorder_course_competency',
+ args: { courseid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid }
+ }
+ ]);
+ } else if (localthis.itemtype == 'template') {
+ requests = ajax.call([
+ {
+ methodname: 'core_competency_reorder_template_competency',
+ args: { templateid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid }
+ }
+ ]);
+ } else if (localthis.itemtype == 'plan') {
+ requests = ajax.call([
+ {
+ methodname: 'core_competency_reorder_plan_competency',
+ args: { planid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid }
+ }
+ ]);
+ } else {
+ return null;
+ }
+
+ requests[0].fail(notification.exception);
+ };
+
+ /**
+ * Pick a competency
+ *
+ * @method pickCompetency
+ */
+ competencies.prototype.pickCompetency = function() {
+ var self = this;
+ var requests;
+ var pagerender;
+ var pageregion;
+ var pageContextIncludes;
+
+ if (!self.pickerInstance) {
+ if (self.itemtype === 'template' || self.itemtype === 'course') {
+ pageContextIncludes = 'parents';
+ }
+ self.pickerInstance = new Picker(self.pageContextId, false, pageContextIncludes);
+ self.pickerInstance.on('save', function(e, data) {
+ var compIds = data.competencyIds;
+
+ if (self.itemtype === "course") {
+ requests = [];
+
+ $.each(compIds, function(index, compId) {
+ requests.push({
+ methodname: 'core_competency_add_competency_to_course',
+ args: { courseid: self.itemid, competencyid: compId }
+ });
+ });
+ requests.push({
+ methodname: 'tool_lp_data_for_course_competencies_page',
+ args: { courseid: self.itemid }
+ });
+
+ pagerender = 'tool_lp/course_competencies_page';
+ pageregion = 'coursecompetenciespage';
+
+ } else if (self.itemtype === "template") {
+ requests = [];
+
+ $.each(compIds, function(index, compId) {
+ requests.push({
+ methodname: 'core_competency_add_competency_to_template',
+ args: { templateid: self.itemid, competencyid: compId }
+ });
+ });
+ requests.push({
+ methodname: 'tool_lp_data_for_template_competencies_page',
+ args: { templateid: self.itemid, pagecontext: { contextid: self.pageContextId }}
+ });
+ pagerender = 'tool_lp/template_competencies_page';
+ pageregion = 'templatecompetenciespage';
+ } else if (self.itemtype === "plan") {
+ requests = [];
+
+ $.each(compIds, function(index, compId) {
+ requests.push({
+ methodname: 'core_competency_add_competency_to_plan',
+ args: { planid: self.itemid, competencyid: compId }
+ });
+ });
+ requests.push({
+ methodname: 'tool_lp_data_for_plan_page',
+ args: { planid: self.itemid}
+ });
+ pagerender = 'tool_lp/plan_page';
+ pageregion = 'plan-page';
+ }
+
+ ajax.call(requests)[requests.length - 1].then(function(context) {
+ return templates.render(pagerender, context).done(function(html, js) {
+ $('[data-region="' + pageregion + '"]').replaceWith(html);
+ templates.runTemplateJS(js);
+ });
+ }, notification.exception);
+ });
+ }
+
+ self.pickerInstance.display();
+ };
+
+ /**
+ * Delete the link between competency and course, template or plan. Reload the page.
+ *
+ * @method doDelete
+ * @param {int} deleteid The id of record to delete.
+ */
+ competencies.prototype.doDelete = function(deleteid) {
+ var localthis = this;
+ var requests = [],
+ pagerender = '',
+ pageregion = '';
+
+ // Delete the link and reload the page template.
+ if (localthis.itemtype == 'course') {
+ requests = ajax.call([
+ { methodname: 'core_competency_remove_competency_from_course',
+ args: { courseid: localthis.itemid, competencyid: deleteid } },
+ { methodname: 'tool_lp_data_for_course_competencies_page',
+ args: { courseid: localthis.itemid } }
+ ]);
+ pagerender = 'tool_lp/course_competencies_page';
+ pageregion = 'coursecompetenciespage';
+ } else if (localthis.itemtype == 'template') {
+ requests = ajax.call([
+ { methodname: 'core_competency_remove_competency_from_template',
+ args: { templateid: localthis.itemid, competencyid: deleteid } },
+ { methodname: 'tool_lp_data_for_template_competencies_page',
+ args: { templateid: localthis.itemid, pagecontext: { contextid: localthis.pageContextId } } }
+ ]);
+ pagerender = 'tool_lp/template_competencies_page';
+ pageregion = 'templatecompetenciespage';
+ } else if (localthis.itemtype == 'plan') {
+ requests = ajax.call([
+ { methodname: 'core_competency_remove_competency_from_plan',
+ args: { planid: localthis.itemid, competencyid: deleteid } },
+ { methodname: 'tool_lp_data_for_plan_page',
+ args: { planid: localthis.itemid } }
+ ]);
+ pagerender = 'tool_lp/plan_page';
+ pageregion = 'plan-page';
+ }
+
+ requests[1].done(function(context) {
+ templates.render(pagerender, context).done(function(html, js) {
+ $('[data-region="' + pageregion + '"]').replaceWith(html);
+ templates.runTemplateJS(js);
+ }).fail(notification.exception);
+ }).fail(notification.exception);
+
+ };
+
+ /**
+ * Show a confirm dialogue before deleting a competency.
+ *
+ * @method deleteHandler
+ * @param {int} deleteid The id of record to delete.
+ */
+ competencies.prototype.deleteHandler = function(deleteid) {
+ var localthis = this;
+ var requests = [];
+ var message;
+
+ if (localthis.itemtype == 'course') {
+ message = 'unlinkcompetencycourse';
+ } else if (localthis.itemtype == 'template') {
+ message = 'unlinkcompetencytemplate';
+ } else if (localthis.itemtype == 'plan') {
+ message = 'unlinkcompetencyplan';
+ } else {
+ return;
+ }
+
+ requests = ajax.call([{
+ methodname: 'core_competency_read_competency',
+ args: { id: deleteid }
+ }]);
+
+ requests[0].done(function(competency) {
+ str.get_strings([
+ { key: 'confirm', component: 'moodle' },
+ { key: message, component: 'tool_lp', param: competency.shortname },
+ { key: 'confirm', component: 'moodle' },
+ { key: 'cancel', component: 'moodle' }
+ ]).done(function (strings) {
+ notification.confirm(
+ strings[0], // Confirm.
+ strings[1], // Unlink the competency X from the course?
+ strings[2], // Confirm.
+ strings[3], // Cancel.
+ function() {
+ localthis.doDelete(deleteid);
+ }
+ );
+ }).fail(notification.exception);
+ }).fail(notification.exception);
+ };
+
+ /**
+ * Register the javascript event handlers for this page.
+ *
+ * @method registerEvents
+ */
+ competencies.prototype.registerEvents = function() {
+ var localthis = this;
+
+ if (localthis.itemtype == 'course') {
+ // Course completion rule handling.
+ $('[data-region="coursecompetenciespage"]').on('change', 'select[data-field="ruleoutcome"]', function(e){
+ var requests = [];
+ var pagerender = 'tool_lp/course_competencies_page';
+ var pageregion = 'coursecompetenciespage';
+ var coursecompetencyid = $(e.target).data('id');
+ var ruleoutcome = $(e.target).val();
+ requests = ajax.call([
+ { methodname: 'core_competency_set_course_competency_ruleoutcome',
+ args: { coursecompetencyid: coursecompetencyid, ruleoutcome: ruleoutcome } },
+ { methodname: 'tool_lp_data_for_course_competencies_page',
+ args: { courseid: localthis.itemid } }
+ ]);
+
+ requests[1].done(function(context) {
+ templates.render(pagerender, context).done(function(html, js) {
+ $('[data-region="' + pageregion + '"]').replaceWith(html);
+ templates.runTemplateJS(js);
+ }).fail(notification.exception);
+ }).fail(notification.exception);
+ });
+ }
+
+ $('[data-region="actions"] button').click(function(e) {
+ e.preventDefault();
+ localthis.pickCompetency();
+ });
+ $('[data-action="delete-competency-link"]').click(function(e) {
+ e.preventDefault();
+
+ var deleteid = $(e.target).closest('[data-id]').data('id');
+ localthis.deleteHandler(deleteid);
+ });
+ };
+
+ return /** @alias module:tool_lp/competencies */ competencies;
+});
--- /dev/null
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Competency rule config.
+ *
+ * @package tool_lp
+ * @copyright 2015 Frédéric Massart - FMCorz.net
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery',
+ 'core/str'],
+ function($, Str) {
+
+ var OUTCOME_NONE = 0,
+ OUTCOME_EVIDENCE = 1,
+ OUTCOME_COMPLETE = 2,
+ OUTCOME_RECOMMEND = 3;
+
+ return /** @alias module:tool_lp/competency_outcomes */ {
+
+ NONE: OUTCOME_NONE,
+ EVIDENCE: OUTCOME_EVIDENCE,
+ COMPLETE: OUTCOME_COMPLETE,
+ RECOMMEND: OUTCOME_RECOMMEND,
+
+ /**
+ * Get all the outcomes.
+ *
+ * @return {Object} Indexed by outcome code, contains code and name.
+ * @method getAll
+ */
+ getAll: function() {
+ var self = this;
+ return Str.get_strings([
+ { key: 'competencyoutcome_none', component: 'tool_lp' },
+ { key: 'competencyoutcome_evidence', component: 'tool_lp' },
+ { key: 'competencyoutcome_recommend', component: 'tool_lp' },
+ { key: 'competencyoutcome_complete', component: 'tool_lp' },
+ ]).then(function(strings) {
+ var outcomes = {};
+ outcomes[self.NONE] = { code: self.NONE, name: strings[0] };
+ outcomes[self.EVIDENCE] = { code: self.EVIDENCE, name: strings[1] };
+ outcomes[self.RECOMMEND] = { code: self.RECOMMEND, name: strings[2] };
+ outcomes[self.COMPLETE] = { code: self.COMPLETE, name: strings[3] };
+ return outcomes;
+ });
+ },
+
+ /**
+ * Get the string for an outcome.
+ *
+ * @param {Number} id The outcome code.
+ * @return {Promise Resolved with the string.
+ * @method getString
+ */
+ getString: function(id) {
+ var self = this,
+ all = self.getAll();
+
+ return all.then(function(outcomes) {
+ if (typeof outcomes[id] === 'undefined') {
+ return $.Deferred().reject().promise();
+ }
+ return outcomes[id].name;
+ });
+ }
+ };
+
+});
--- /dev/null
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Event click on selecting competency in the competency autocomplete.
+ *
+ * @package tool_lp
+ * @copyright 2016 Issam Taboubi <issam.taboubi@umontreal.ca>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery'], function($) {
+
+ /**
+ * CompetencyPlanNavigation
+ *
+ * @param {String} The selector of the competency element.
+ * @param {String} The base url for the page (no params).
+ * @param {Number} The user id
+ * @param {Number} The competency id
+ * @param {Number} The plan id
+ */
+ var CompetencyPlanNavigation = function(competencySelector, baseUrl, userId, competencyId, planId) {
+ this._baseUrl = baseUrl;
+ this._userId = userId + '';
+ this._competencyId = competencyId + '';
+ this._planId = planId;
+ this._ignoreFirstCompetency = true;
+
+ $(competencySelector).on('change', this._competencyChanged.bind(this));
+ };
+
+ /**
+ * The competency was changed in the select list.
+ *
+ * @method _competencyChanged
+ * @param {Event} e
+ */
+ CompetencyPlanNavigation.prototype._competencyChanged = function(e) {
+ if (this._ignoreFirstCompetency) {
+ this._ignoreFirstCompetency = false;
+ return;
+ }
+ var newCompetencyId = $(e.target).val();
+ var queryStr = '?userid=' + this._userId + '&planid=' + this._planId + '&competencyid=' + newCompetencyId;
+ document.location = this._baseUrl + queryStr;
+ };
+
+ /** @type {Number} The id of the competency. */
+ CompetencyPlanNavigation.prototype._competencyId = null;
+ /** @type {Number} The id of the user. */
+ CompetencyPlanNavigation.prototype._userId = null;
+ /** @type {Number} The id of the plan. */
+ CompetencyPlanNavigation.prototype._planId = null;
+ /** @type {String} Plugin base url. */
+ CompetencyPlanNavigation.prototype._baseUrl = null;
+ /** @type {Boolean} Ignore the first change event for competencies. */
+ CompetencyPlanNavigation.prototype._ignoreFirstCompetency = null;
+
+ return /** @alias module:tool_lp/competency_plan_navigation */ CompetencyPlanNavigation;
+
+});
--- /dev/null
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Competency rule base module.
+ *
+ * @package tool_lp
+ * @copyright 2015 Frédéric Massart - FMCorz.net
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery'], function($) {
+
+ /**
+ * Competency rule abstract class.
+ *
+ * Any competency rule should extend this object. The event 'change' should be
+ * triggered on the instance when the configuration has changed. This will allow
+ * the components using the rule to gather the config, or check its validity.
+ *
+ * this._triggerChange();
+ *
+ * @param {Tree} tree The competency tree.
+ */
+ var Rule = function(tree) {
+ this._eventNode = $('<div>');
+ this._ready = $.Deferred();
+ this._tree = tree;
+ };
+
+ /** @type {Object} The current competency. */
+ Rule.prototype._competency = null;
+ /** @type {Node} The node we attach the events to. */
+ Rule.prototype._eventNode = null;
+ /** @type {Promise} Resolved when the object is ready. */
+ Rule.prototype._ready = null;
+ /** @type {Tree} The competency tree. */
+ Rule.prototype._tree = null;
+
+ /**
+ * Whether or not the current competency can be configured using this rule.
+ *
+ * @return {Boolean}
+ * @method canConfig
+ */
+ Rule.prototype.canConfig = function() {
+ return this._tree.hasChildren(this._competency.id);
+ };
+
+ /**
+ * The config established by this rule.
+ *
+ * To override in subclasses when relevant.
+ *
+ * @return {String|null}
+ * @method getConfig
+ */
+ Rule.prototype.getConfig = function() {
+ return null;
+ };
+
+ /**
+ * Return the type of the module.
+ *
+ * @return {String}
+ * @method getType
+ */
+ Rule.prototype.getType = function() {
+ throw new Error('Not implemented');
+ };
+
+ /**
+ * The init process.
+ *
+ * Do not override this, instead override _load.
+ *
+ * @return {Promise} Revoled when the plugin is initialised.
+ * @method init
+ */
+ Rule.prototype.init = function() {
+ return this._load();
+ };
+
+ /**
+ * Callback to inject the template.
+ *
+ * @param {Node} container Node to inject in.
+ * @return {Promise} Resolved when done.
+ * @method injectTemplate
+ */
+ Rule.prototype.injectTemplate = function() {
+ return $.Deferred().reject().promise();
+ };
+
+ /**
+ * Whether or not the current config is valid.
+ *
+ * Plugins should override this.
+ *
+ * @return {Boolean}
+ * @method _isValid
+ */
+ Rule.prototype.isValid = function() {
+ return false;
+ };
+
+ /**
+ * Load the class.
+ *
+ * @return {Promise}
+ * @method _load
+ * @protected
+ */
+ Rule.prototype._load = function() {
+ return $.when();
+ };
+
+ /**
+ * Register an event listener.
+ *
+ * @param {String} type The event type.
+ * @param {Function} handler The event listener.
+ * @method on
+ */
+ Rule.prototype.on = function(type, handler) {
+ this._eventNode.on(type, handler);
+ };
+
+ /**
+ * Sets the current competency.
+ *
+ * @param {Competency} competency
+ * @method setTargetCompetency
+ */
+ Rule.prototype.setTargetCompetency = function(competency) {
+ this._competency = competency;
+ };
+
+ /**
+ * Trigger an event.
+ *
+ * @param {String} type The type of event.
+ * @param {Object} The data to pass to the listeners.
+ * @method _trigger
+ * @protected
+ */
+ Rule.prototype._trigger = function(type, data) {
+ this._eventNode.trigger(type, [data]);
+ };
+
+ /**
+ * Trigger the change event.
+ *
+ * @method _triggerChange
+ * @protected
+ */
+ Rule.prototype._triggerChange = function() {
+ this._trigger('change', this);
+ };
+
+ return /** @alias module:tool_lp/competency_rule */ Rule;
+
+});
--- /dev/null
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Competency rule all module.
+ *
+ * @package tool_lp
+ * @copyright 2015 Frédéric Massart - FMCorz.net
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery',
+ 'core/str',
+ 'tool_lp/competency_rule',
+ ],
+ function($, Str, RuleBase) {
+
+ /**
+ * Competency rule all class.
+ */
+ var Rule = function() {
+ RuleBase.apply(this, arguments);
+ };
+ Rule.prototype = Object.create(RuleBase.prototype);
+
+ /**
+ * Return the type of the module.
+ *
+ * @return {String}
+ * @method getType
+ */
+ Rule.prototype.getType = function() {
+ return 'core_competency\\competency_rule_all';
+ };
+
+ /**
+ * Whether or not the current config is valid.
+ *
+ * @return {Boolean}
+ * @method isValid
+ */
+ Rule.prototype.isValid = function() {
+ return true;
+ };
+
+ return /** @alias module:tool_lp/competency_rule_all */ Rule;
+
+});
--- /dev/null
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Competency rule points module.
+ *
+ * @package tool_lp
+ * @copyright 2015 Frédéric Massart - FMCorz.net
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery',
+ 'core/str',
+ 'core/templates',
+ 'tool_lp/competency_rule',
+ ],
+ function($, Str, Templates, RuleBase) {
+
+ /**
+ * Competency rule points class.
+ */
+ var Rule = function() {
+ RuleBase.apply(this, arguments);
+ };
+ Rule.prototype = Object.create(RuleBase.prototype);
+
+ /** @type {Node} Reference to the container in which the template was included. */
+ Rule.prototype._container = null;
+ /** @type {Boolean} Whether or not the template was included. */
+ Rule.prototype._templateLoaded = false;
+
+ /**
+ * The config established by this rule.
+ *
+ * @return {String}
+ * @method getConfig
+ */
+ Rule.prototype.getConfig = function() {
+ return JSON.stringify({
+ base: {
+ points: this._getRequiredPoints(),
+ },
+ competencies: this._getCompetenciesConfig()
+ });
+ };
+
+ /**
+ * Gathers the input provided by the user for competencies.
+ *
+ * @return {Array} Containing id, points and required.
+ * @method _getCompetenciesConfig
+ * @protected
+ */
+ Rule.prototype._getCompetenciesConfig = function() {
+ var competencies = [];
+
+ this._container.find('[data-competency]').each(function() {
+ var node = $(this),
+ id = node.data('competency'),
+ points = parseInt(node.find('[name="points"]').val(), 10),
+ required = node.find('[name="required"]').prop('checked');
+
+ competencies.push({
+ id: id,
+ points: points,
+ required: required ? 1 : 0
+ });
+ });
+
+ return competencies;
+ };
+
+ /**
+ * Fetches the required points set by the user.
+ *
+ * @return {Number}
+ * @method _getRequiredPoints
+ * @protected
+ */
+ Rule.prototype._getRequiredPoints = function() {
+ return parseInt(this._container.find('[name="requiredpoints"]').val() || 1, 10);
+ };
+
+ /**
+ * Return the type of the module.
+ *
+ * @return {String}
+ * @method getType
+ */
+ Rule.prototype.getType = function() {
+ return 'core_competency\\competency_rule_points';
+ };
+
+ /**
+ * Callback to inject the template.
+ *
+ * @param {Node} container Node to inject in.
+ * @return {Promise} Resolved when done.
+ * @method injectTemplate
+ */
+ Rule.prototype.injectTemplate = function(container) {
+ var self = this,
+ children = this._tree.getChildren(this._competency.id),
+ context,
+ config = {
+ base: { points: 2 },
+ competencies: []
+ };
+
+ this._templateLoaded = false;
+
+ // Only pre-load the configuration when the competency is using this rule.
+ if (self._competency.ruletype == self.getType()) {
+ try {
+ config = JSON.parse(self._competency.ruleconfig);
+ } catch (e) {
+ }
+ }
+
+ context = {
+ requiredpoints: (config && config.base) ? config.base.points : 2,
+ competency: self._competency,
+ children: []
+ };
+
+ $.each(children, function(index, child) {
+ var competency = {
+ id: child.id,
+ shortname: child.shortname,
+ required: false,
+ points: 0
+ };
+
+ if (config) {
+ $.each(config.competencies, function(index, comp) {
+ if (comp.id == competency.id) {
+ competency.required = comp.required ? true : false;
+ competency.points = comp.points;
+ }
+ });
+ }
+
+ context.children.push(competency);
+ });
+
+ return Templates.render('tool_lp/competency_rule_points', context).then(function(html) {
+ self._container = container;
+ container.html(html);
+ container.find('input').change(function() {
+ self._triggerChange();
+ });
+
+ // We're done, let's trigger a change.
+ self._templateLoaded = true;
+ self._triggerChange();
+ });
+ };
+
+ /**
+ * Whether or not the current config is valid.
+ *
+ * @return {Boolean}
+ * @method isValid
+ */
+ Rule.prototype.isValid = function() {
+ if (!this._templateLoaded) {
+ return false;
+ }
+
+ var required = this._getRequiredPoints(),
+ max = 0,
+ valid = true;
+
+ $.each(this._getCompetenciesConfig(), function(index, competency) {
+ if (competency.points < 0) {
+ valid = false;
+ }
+ max += competency.points;
+ });
+
+ valid = valid && max >= required;
+ return valid;
+ };
+
+ return /** @alias module:tool_lp/competency_rule_all */ Rule;
+
+});
--- /dev/null
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Handle selection changes and actions on the competency tree.
+ *
+ * @module tool_lp/competencyactions
+ * @package tool_lp
+ * @copyright 2015 Damyon Wiese <damyon@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery',
+ 'core/url',
+ 'core/templates',
+ 'core/notification',
+ 'core/str',
+ 'core/ajax',
+ 'tool_lp/dragdrop-reorder',
+ 'tool_lp/tree',
+ 'tool_lp/dialogue',
+ 'tool_lp/menubar',
+ 'tool_lp/competencypicker',
+ 'tool_lp/competency_outcomes',
+ 'tool_lp/competencyruleconfig'],
+ function($, url, templates, notification, str, ajax, dragdrop, Ariatree, Dialogue, menubar, Picker, Outcomes, RuleConfig) {
+
+ // Private variables and functions.
+ /** @var {Object} treeModel - This is an object representing the nodes in the tree. */
+ var treeModel = null;
+ /** @var {Node} moveSource - The start of a drag operation */
+ var moveSource = null;
+ /** @var {Node} moveTarget - The end of a drag operation */
+ var moveTarget = null;
+ /** @var {Number} pageContextId The page context ID. */
+ var pageContextId;
+ /** @type {Object} Picker instance. */
+ var pickerInstance;
+ /** @type {Object} Rule config instance. */
+ var ruleConfigInstance;
+ /** @type {Object} The competency we're picking a relation to. */
+ var relatedTarget;
+ /** @type {Object} Taxonomy constants indexed per level. */
+ var taxonomiesConstants;
+ /** @type {Array} The rules modules. Values are object containing type, namd and amd. */
+ var rulesModules;
+ /** @type {Number} the selected competency ID. */
+ var selectedCompetencyId = null;
+
+ /**
+ * Respond to choosing the "Add" menu item for the selected node in the tree.
+ * @method addHandler
+ */
+ var addHandler = function() {
+ var parent = $('[data-region="competencyactions"]').data('competency');
+
+ var params = {
+ competencyframeworkid : treeModel.getCompetencyFrameworkId(),
+ pagecontextid: pageContextId
+ };
+
+ if (parent !== null) {
+ // We are adding at a sub node.
+ params.parentid = parent.id;
+ }
+
+ var relocate = function() {
+ var queryparams = $.param(params);
+ window.location = url.relativeUrl('/admin/tool/lp/editcompetency.php?' + queryparams);
+ };
+
+ if (parent !== null && treeModel.hasRule(parent.id)) {
+ str.get_strings([
+ { key: 'confirm', component: 'moodle' },
+ { key: 'addingcompetencywillresetparentrule', component: 'tool_lp', param: parent.shortname },
+ { key: 'yes', component: 'core' },
+ { key: 'no', component: 'core' }
+ ]).done(function (strings) {
+ notification.confirm(
+ strings[0],
+ strings[1],
+ strings[2],
+ strings[3],
+ relocate
+ );
+ }).fail(notification.exception);
+ } else {
+ relocate();
+ }
+ };
+
+ /**
+ * A source and destination has been chosen - so time to complete a move.
+ * @method doMove
+ */
+ var doMove = function() {
+ var frameworkid = $('[data-region="filtercompetencies"]').data('frameworkid');
+ var requests = ajax.call([{
+ methodname: 'core_competency_set_parent_competency',
+ args: { competencyid: moveSource, parentid: moveTarget }
+ }, {
+ methodname: 'tool_lp_data_for_competencies_manage_page',
+ args: { competencyframeworkid: frameworkid,
+ search: $('[data-region="filtercompetencies"] input').val() }
+ }]);
+ requests[1].done(reloadPage).fail(notification.exception);
+ };
+
+ /**
+ * Confirms a competency move.
+ *
+ * @method confirmMove
+ */
+ var confirmMove = function() {
+ moveTarget = typeof moveTarget === "undefined" ? 0 : moveTarget;
+ if (moveTarget == moveSource) {
+ // No move to do.
+ return;
+ }
+
+ var targetComp = treeModel.getCompetency(moveTarget) || {},
+ sourceComp = treeModel.getCompetency(moveSource) || {},
+ confirmMessage = 'movecompetencywillresetrules',
+ showConfirm = false;
+
+ // We shouldn't be moving the competency to the same parent.
+ if (sourceComp.parentid == moveTarget) {
+ return;
+ }
+
+ // If we are moving to a child of self.
+ if (targetComp.path && targetComp.path.indexOf('/' + sourceComp.id + '/') >= 0) {
+ confirmMessage = 'movecompetencytochildofselfwillresetrules';
+
+ // Show a confirmation if self has rules, as they'll disappear.
+ showConfirm = showConfirm || treeModel.hasRule(sourceComp.id);
+ }
+
+ // Show a confirmation if the current parent, or the destination have rules.
+ showConfirm = showConfirm || (treeModel.hasRule(targetComp.id) || treeModel.hasRule(sourceComp.parentid));
+
+ // Show confirm, and/or do the things.
+ if (showConfirm) {
+ str.get_strings([
+ { key: 'confirm', component: 'moodle' },
+ { key: confirmMessage, component: 'tool_lp' },
+ { key: 'yes', component: 'moodle' },
+ { key: 'no', component: 'moodle' }
+ ]).done(function (strings) {
+ notification.confirm(
+ strings[0], // Confirm.
+ strings[1], // Delete competency X?
+ strings[2], // Delete.
+ strings[3], // Cancel.
+ doMove
+ );
+ }).fail(notification.exception);
+
+ } else {
+ doMove();
+ }
+ };
+
+ /**
+ * A move competency popup was opened - initialise the aria tree in it.
+ * @method initMovePopup
+ * @param {dialogue} popup The tool_lp/dialogue that was created.
+ */
+ var initMovePopup = function(popup) {
+ var body = $(popup.getContent());
+ var treeRoot = body.find('[data-enhance=movetree]');
+ var tree = new Ariatree(treeRoot, false);
+ tree.on('selectionchanged', function(evt, params) {
+ var target = params.selected;
+ moveTarget = $(target).data('id');
+ });
+ treeRoot.show();
+
+ body.on('click', '[data-action="move"]', function() { popup.close(); confirmMove(); });
+ body.on('click', '[data-action="cancel"]', function() { popup.close(); });
+ };
+
+ /**
+ * Turn a flat list of competencies into a tree structure (recursive).
+ * @method addCompetencyChildren
+ * @param {Object} parent The current parent node in the tree
+ * @param {Object[]} competencies The flat list of competencies
+ */
+ var addCompetencyChildren = function(parent, competencies) {
+ var i;
+
+ for (i = 0; i < competencies.length; i++) {
+ if (competencies[i].parentid == parent.id) {
+ parent.haschildren = true;
+ competencies[i].children = [];
+ competencies[i].haschildren = false;
+ parent.children[parent.children.length] = competencies[i];
+ addCompetencyChildren(competencies[i], competencies);
+ }
+ }
+ };
+
+ /**
+ * A node was chosen and "Move" was selected from the menu. Open a popup to select the target.
+ * @method moveHandler
+ */
+ var moveHandler = function(e) {
+ e.preventDefault();
+ var competency = $('[data-region="competencyactions"]').data('competency');
+
+ // Remember what we are moving.
+ moveSource = competency.id;
+
+ // Load data for the template.
+ var requests = ajax.call([
+ {
+ methodname: 'core_competency_search_competencies',
+ args: {
+ competencyframeworkid: competency.competencyframeworkid,
+ searchtext: ''
+ }
+ },{
+ methodname: 'core_competency_read_competency_framework',
+ args: {
+ id: competency.competencyframeworkid
+ }
+ }
+ ]);
+
+ // When all data has arrived, continue.
+ $.when.apply(null, requests).done(function(competencies, framework) {
+
+ // Expand the list of competencies into a tree.
+ var i, competenciestree = [];
+ for (i = 0; i < competencies.length; i++) {
+ var onecompetency = competencies[i];
+ if (onecompetency.parentid == "0") {
+ onecompetency.children = [];
+ onecompetency.haschildren = 0;
+ competenciestree[competenciestree.length] = onecompetency;
+ addCompetencyChildren(onecompetency, competencies);
+ }
+ }
+
+ str.get_strings([
+ { key: 'movecompetency', component: 'tool_lp', param: competency.shortname },
+ { key: 'move', component: 'tool_lp' },
+ { key: 'cancel', component: 'moodle' }
+ ]).done(function (strings) {
+
+ var context = {
+ framework: framework,
+ competencies: competenciestree
+ };
+
+ templates.render('tool_lp/competencies_move_tree', context)
+ .done(function(tree) {
+ new Dialogue(
+ strings[0], // Move competency x.
+ tree, // The move tree.
+ initMovePopup
+ );
+
+ }).fail(notification.exception);
+
+ }).fail(notification.exception);
+
+ }).fail(notification.exception);
+
+ };
+
+ /**
+ * Edit the selected competency.
+ * @method editHandler
+ */
+ var editHandler = function() {
+ var competency = $('[data-region="competencyactions"]').data('competency');
+
+ var params = {
+ competencyframeworkid : treeModel.getCompetencyFrameworkId(),
+ id : competency.id,
+ parentid: competency.parentid,
+ pagecontextid: pageContextId
+ };
+
+ var queryparams = $.param(params);
+ window.location = url.relativeUrl('/admin/tool/lp/editcompetency.php?' + queryparams);
+ };
+
+ /**
+ * Re-render the page with the latest data.
+ * @method reloadPage
+ */
+ var reloadPage = function(context) {
+ templates.render('tool_lp/manage_competencies_page', context)
+ .done(function(newhtml, newjs) {
+ $('[data-region="managecompetencies"]').replaceWith(newhtml);
+ templates.runTemplateJS(newjs);
+ })
+ .fail(notification.exception);
+ };
+
+ /**
+ * Perform a search and render the page with the new search results.
+ * @method updateSearchHandler
+ */
+ var updateSearchHandler = function(e) {
+ e.preventDefault();
+
+ var frameworkid = $('[data-region="filtercompetencies"]').data('frameworkid');
+
+ var requests = ajax.call([{
+ methodname: 'tool_lp_data_for_competencies_manage_page',
+ args: { competencyframeworkid: frameworkid,
+ search: $('[data-region="filtercompetencies"] input').val() }
+ }]);
+ requests[0].done(reloadPage).fail(notification.exception);
+ };
+
+ /**
+ * Move a competency "up". This only affects the sort order within the same branch of the tree.
+ * @method moveUpHandler
+ */
+ var moveUpHandler = function() {
+ // We are chaining ajax requests here.
+ var competency = $('[data-region="competencyactions"]').data('competency');
+ var requests = ajax.call([{
+ methodname: 'core_competency_move_up_competency',
+ args: { id: competency.id }
+ }, {
+ methodname: 'tool_lp_data_for_competencies_manage_page',
+ args: { competencyframeworkid: competency.competencyframeworkid,
+ search: $('[data-region="filtercompetencies"] input').val() }
+ }]);
+ requests[1].done(reloadPage).fail(notification.exception);
+ };
+
+ /**
+ * Move a competency "down". This only affects the sort order within the same branch of the tree.
+ * @method moveDownHandler
+ */
+ var moveDownHandler = function() {
+ // We are chaining ajax requests here.
+ var competency = $('[data-region="competencyactions"]').data('competency');
+ var requests = ajax.call([{
+ methodname: 'core_competency_move_down_competency',
+ args: { id: competency.id }
+ }, {
+ methodname: 'tool_lp_data_for_competencies_manage_page',
+ args: { competencyframeworkid: competency.competencyframeworkid,
+ search: $('[data-region="filtercompetencies"] input').val() }
+ }]);
+ requests[1].done(reloadPage).fail(notification.exception);
+ };
+
+ /**
+ * Open a dialogue to show all the courses using the selected competency.
+ * @method seeCoursesHandler
+ */
+ var seeCoursesHandler = function() {
+ var competency = $('[data-region="competencyactions"]').data('competency');
+
+ var requests = ajax.call([{
+ methodname: 'tool_lp_list_courses_using_competency',
+ args: { id: competency.id }
+ }]);
+
+ requests[0].done(function(courses) {
+ var context = {
+ courses: courses
+ };
+ templates.render('tool_lp/linked_courses_summary', context).done(function(html) {
+ str.get_string('linkedcourses', 'tool_lp').done(function (linkedcourses) {
+ new Dialogue(
+ linkedcourses, // Title.
+ html, // The linked courses.
+ initMovePopup
+ );
+ }).fail(notification.exception);
+ }).fail(notification.exception);
+ }).fail(notification.exception);
+ };
+
+ /**
+ * Open a competencies popup to relate competencies.
+ *
+ * @method relateCompetenciesHandler
+ */
+ var relateCompetenciesHandler = function() {
+ relatedTarget = $('[data-region="competencyactions"]').data('competency');
+
+ if (!pickerInstance) {
+ pickerInstance = new Picker(pageContextId, relatedTarget.competencyframeworkid);
+ pickerInstance.on('save', function(e, data) {
+ var compIds = data.competencyIds;
+
+ var calls = [];
+ $.each(compIds, function(index, value) {
+ calls.push({
+ methodname: 'core_competency_add_related_competency',
+ args: { competencyid: value, relatedcompetencyid: relatedTarget.id }
+ });
+ });
+
+ calls.push( {
+ methodname: 'tool_lp_data_for_related_competencies_section',
+ args: { competencyid: relatedTarget.id }
+ });
+
+ var promises = ajax.call(calls);
+
+ promises[calls.length - 1].then(function(context) {
+ return templates.render('tool_lp/related_competencies', context).done(function(html, js) {
+ $('[data-region="relatedcompetencies"]').replaceWith(html);
+ templates.runTemplateJS(js);
+ updatedRelatedCompetencies();
+ });
+ }, notification.exception);
+ });
+ }
+
+ pickerInstance.setDisallowedCompetencyIDs([relatedTarget.id]);
+ pickerInstance.display();
+ };
+
+ var ruleConfigHandler = function(e) {
+ e.preventDefault();
+ relatedTarget = $('[data-region="competencyactions"]').data('competency');
+ ruleConfigInstance.setTargetCompetencyId(relatedTarget.id);
+ ruleConfigInstance.display();
+ };
+
+ var ruleConfigSaveHandler = function(e, config) {
+ var update = {
+ id: relatedTarget.id,
+ shortname: relatedTarget.shortname,
+ idnumber: relatedTarget.idnumber,
+ description: relatedTarget.description,
+ descriptionformat: relatedTarget.descriptionformat,
+ ruletype: config.ruletype,
+ ruleoutcome: config.ruleoutcome,
+ ruleconfig: config.ruleconfig
+ };
+ var promise = ajax.call([{
+ methodname: 'core_competency_update_competency',
+ args: { competency: update }
+ }]);
+ promise[0].then(function(result) {
+ if (result) {
+ relatedTarget.ruletype = config.ruletype;
+ relatedTarget.ruleoutcome = config.ruleoutcome;
+ relatedTarget.ruleconfig = config.ruleconfig;
+ renderCompetencySummary(relatedTarget);
+ }
+ }, notification.exception);
+ };
+
+ /**
+ * Delete a competency.
+ * @method doDelete
+ */
+ var doDelete = function() {
+ // We are chaining ajax requests here.
+ var competency = $('[data-region="competencyactions"]').data('competency');
+ var requests = ajax.call([{
+ methodname: 'core_competency_delete_competency',
+ args: { id: competency.id }
+ }, {
+ methodname: 'tool_lp_data_for_competencies_manage_page',
+ args: { competencyframeworkid: competency.competencyframeworkid,
+ search: $('[data-region="filtercompetencies"] input').val() }
+ }]);
+ requests[0].done(function(success) {
+ if (success === false) {
+ str.get_strings([
+ { key: 'competencycannotbedeleted', component: 'tool_lp', param: competency.shortname },
+ { key: 'cancel', component: 'moodle' }
+ ]).done(function (strings) {
+ notification.alert(
+ null,
+ strings[0]
+ );
+ }).fail(notification.exception);
+ }
+ }).fail(notification.exception);
+ requests[1].done(reloadPage).fail(notification.exception);
+ };
+
+ /**
+ * Show a confirm dialogue before deleting a competency.
+ * @method deleteCompetencyHandler
+ */
+ var deleteCompetencyHandler = function() {
+ var competency = $('[data-region="competencyactions"]').data('competency'),
+ confirmMessage = 'deletecompetency';
+
+ if (treeModel.hasRule(competency.parentid)) {
+ confirmMessage = 'deletecompetencyparenthasrule';
+ }
+
+ str.get_strings([
+ { key: 'confirm', component: 'moodle' },
+ { key: confirmMessage, component: 'tool_lp', param: competency.shortname },
+ { key: 'delete', component: 'moodle' },
+ { key: 'cancel', component: 'moodle' }
+ ]).done(function (strings) {
+ notification.confirm(
+ strings[0], // Confirm.
+ strings[1], // Delete competency X?
+ strings[2], // Delete.
+ strings[3], // Cancel.
+ doDelete
+ );
+ }).fail(notification.exception);
+ };
+
+ /**
+ * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).
+ * @method dragStart
+ */
+ var dragStart = function(e) {
+ e.originalEvent.dataTransfer.setData('text', $(e.target).parent().data('id'));
+ };
+
+ /**
+ * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).
+ * @method allowDrop
+ */
+ var allowDrop = function(e) {
+ e.originalEvent.dataTransfer.dropEffect = 'move';
+ e.preventDefault();
+ };
+
+ /**
+ * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).
+ * @method dragEnter
+ */
+ var dragEnter = function(e) {
+ e.preventDefault();
+ $(this).addClass('currentdragtarget');
+ };
+
+ /**
+ * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).
+ * @method dragLeave
+ */
+ var dragLeave = function(e) {
+ e.preventDefault();
+ $(this).removeClass('currentdragtarget');
+ };
+
+ /**
+ * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).
+ * @method dropOver
+ */
+ var dropOver = function(e) {
+ e.preventDefault();
+ moveSource = e.originalEvent.dataTransfer.getData('text');
+ moveTarget = $(e.target).parent().data('id');
+ $(this).removeClass('currentdragtarget');
+
+ confirmMove();
+ };
+
+ /**
+ * Deletes a related competency without confirmation.
+ *
+ * @param {Event} e The event that triggered the action.
+ * @method deleteRelatedHandler
+ */
+ var deleteRelatedHandler = function(e) {
+ e.preventDefault();
+
+ var relatedid = this.id.substr(11);
+ var competency = $('[data-region="competencyactions"]').data('competency');
+ var removeRelated = ajax.call([
+ { methodname: 'core_competency_remove_related_competency',
+ args: { relatedcompetencyid: relatedid, competencyid: competency.id } },
+ { methodname: 'tool_lp_data_for_related_competencies_section',
+ args: { competencyid: competency.id } }
+ ]);
+
+ removeRelated[1].done(function(context) {
+ templates.render('tool_lp/related_competencies', context).done(function(html) {
+ $('[data-region="relatedcompetencies"]').replaceWith(html);
+ updatedRelatedCompetencies();
+ }.bind(this)).fail(notification.exception);
+ }.bind(this)).fail(notification.exception);
+ };
+
+ /**
+ * Updates the competencies list (with relations) and add listeners.
+ *
+ * @method updatedRelatedCompetencies
+ */
+ var updatedRelatedCompetencies = function() {
+
+ // Listeners to newly loaded related competencies.
+ $('[data-action="deleterelation"]').on('click', deleteRelatedHandler);
+
+ };
+
+ /**
+ * Log the competency viewed event.
+ *
+ * @param {Object} The competency.
+ * @method triggerCompetencyViewedEvent
+ */
+ var triggerCompetencyViewedEvent = function(competency) {
+ if (competency.id !== selectedCompetencyId) {
+ // Set the selected competency id.
+ selectedCompetencyId = competency.id;
+ ajax.call([{
+ methodname: 'core_competency_competency_viewed',
+ args: { id: competency.id }
+ }]);
+ }
+ };
+
+ /**
+ * Return if the level has a sub level.
+ *
+ * @param {Number} level The level.
+ * @return {Boolean}
+ * @function hasSubLevel
+ */
+ var hasSubLevel = function(level) {
+ return typeof taxonomiesConstants[level + 1] !== 'undefined';
+ };
+
+ /**
+ * Return the taxonomy constant for a level.
+ *
+ * @param {Number} level The level.
+ * @return {String}
+ * @function getTaxonomyAtLevel
+ */
+ var getTaxonomyAtLevel = function(level) {
+ var constant = taxonomiesConstants[level];
+ if (!constant) {
+ constant = 'competency';
+ }
+ return constant;
+ };
+
+ /**
+ * Render the competency summary.
+ *
+ * @param {Object} competency The competency.
+ */
+ var renderCompetencySummary = function(competency) {
+ var promise = $.Deferred().resolve().promise(),
+ context = {};
+
+ context.competency = competency;
+ context.showdeleterelatedaction = true;
+ context.showrelatedcompetencies = true;
+ context.showrule = false;
+
+ if (competency.ruleoutcome != Outcomes.NONE) {
+ // Get the outcome and rule name.
+ promise = Outcomes.getString(competency.ruleoutcome).then(function(str) {
+ var name;
+ $.each(rulesModules, function(index, modInfo) {
+ if (modInfo.type == competency.ruletype) {
+ name = modInfo.name;
+ }
+ });
+ return [str, name];
+ });
+ }
+
+ promise.then(function(strs) {
+ if (typeof strs !== 'undefined') {
+ context.showrule = true;
+ context.rule = {
+ outcome: strs[0],
+ type: strs[1]
+ };
+ }
+ }).then(function() {
+ return templates.render('tool_lp/competency_summary', context).then(function(html) {
+ $('[data-region="competencyinfo"]').html(html);
+ $('[data-action="deleterelation"]').on('click', deleteRelatedHandler);
+ });
+ }).then(function() {
+ return templates.render('tool_lp/loading', {});
+ }).then(function(html, js) {
+ templates.replaceNodeContents('[data-region="relatedcompetencies"]', html, js);
+ }).done(function() {
+ ajax.call([{
+ methodname: 'tool_lp_data_for_related_competencies_section',
+ args: { competencyid: competency.id },
+ done: function(context) {
+ return templates.render('tool_lp/related_competencies', context).done(function(html, js) {
+ $('[data-region="relatedcompetencies"]').replaceWith(html);
+ templates.runTemplateJS(js);
+ updatedRelatedCompetencies();
+ });
+ }
+ }]);
+ }).fail(notification.exception);
+ };
+
+ /**
+ * Return the string "Add <taxonomy>".
+ *
+ * @param {Number} level The level.
+ * @return {String}
+ * @function strAddTaxonomy
+ */
+ var strAddTaxonomy = function(level) {
+ return str.get_string('taxonomy_add_' + getTaxonomyAtLevel(level), 'tool_lp');
+ };
+
+ /**
+ * Return the string "Selected <taxonomy>".
+ *
+ * @param {Number} level The level.
+ * @return {String}
+ * @function strSelectedTaxonomy
+ */
+ var strSelectedTaxonomy = function(level) {
+ return str.get_string('taxonomy_selected_' + getTaxonomyAtLevel(level), 'tool_lp');
+ };
+
+ /**
+ * Handler when a node in the aria tree is selected.
+ * @method selectionChanged
+ * @param {Event} evt The event that triggered the selection change.
+ * @param {Object} params The parameters for the event. Contains a list of selected nodes.