Merge branch 'cbe' of git://github.com/FMCorz/moodle
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
<?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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,6 +30,7 @@ if ($hassiteconfig) {
|
||||
$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')));
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
<?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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?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'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?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 <[email protected]>
|
||||
* @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 <[email protected]>
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?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']));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?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' => '*'
|
||||
),
|
||||
);
|
||||
@@ -0,0 +1,98 @@
|
||||
<?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();
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?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';
|
||||
@@ -0,0 +1,27 @@
|
||||
<?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');
|
||||
$ADMIN->add('roles', new admin_externalpage('toolcohortroles', $str, '/admin/tool/cohortroles/index.php', 'moodle/role:manage'));
|
||||
@@ -0,0 +1,43 @@
|
||||
{{!
|
||||
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 }}
|
||||
@@ -0,0 +1,243 @@
|
||||
<?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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","core/ajax","core/templates","tool_lp/dialogue","tool_lp/event_base"],function(a,b,c,d,e,f){var g=function(a,b,c,d,e){var g=this;f.prototype.constructor.apply(this,[]),g._title=a,g._message=b,g._actions=c,g._confirm=d,g._cancel=e,g._selectedValue=null,g._reset()};return g.prototype=Object.create(f.prototype),g.prototype._selectedValue=null,g.prototype._popup=null,g.prototype._title=null,g.prototype._message=null,g.prototype._actions=null,g.prototype._confirm=null,g.prototype._cancel=null,g.prototype._afterRender=function(){var b=this;b._find('[data-action="action-selector-confirm"]').attr("disabled","disabled"),b._find('[data-region="action-selector-radio-buttons"]').change(function(){b._selectedValue=a("input[type='radio']:checked").val(),b._find('[data-action="action-selector-confirm"]').removeAttr("disabled"),b._refresh.bind(b)}.bind(b)),b._find('[data-action="action-selector-cancel"]').click(function(a){a.preventDefault(),b.close()}.bind(b)),b._find('[data-action="action-selector-confirm"]').click(function(a){a.preventDefault(),b._selectedValue.length&&(b._trigger("save",{action:b._selectedValue}),b.close())}.bind(b))},g.prototype.close=function(){var a=this;a._popup.close(),a._reset()},g.prototype.display=function(){var a=this;return a._render().then(function(b){a._popup=new e(a._title,b,a._afterRender.bind(a))}.bind(a)).fail(b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._refresh=function(){var a=this;return a._render().then(function(b){a._find('[data-region="action-selector"]').replaceWith(b),a._afterRender()}.bind(a))},g.prototype._render=function(){var a=this,b=[];for(var c in a._actions)b.push(a._actions[c]);var e={message:a._message,choices:b,confirm:a._confirm,cancel:a._cancel};return d.render("tool_lp/action_selector",e)},g.prototype._reset=function(){this._popup=null,this._selectedValue=""},g});
|
||||
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define(["jquery","core/str"],function(a,b){var c=0,d=1,e=2,f=3;return{NONE:c,EVIDENCE:d,COMPLETE:e,RECOMMEND:f,getAll:function(){var a=this;return b.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(b){var c={};return c[a.NONE]={code:a.NONE,name:b[0]},c[a.EVIDENCE]={code:a.EVIDENCE,name:b[1]},c[a.RECOMMEND]={code:a.RECOMMEND,name:b[2]},c[a.COMPLETE]={code:a.COMPLETE,name:b[3]},c})},getString:function(b){var c=this,d=c.getAll();return d.then(function(c){return"undefined"==typeof c[b]?a.Deferred().reject().promise():c[b].name})}}});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery"],function(a){var b=function(b){this._eventNode=a("<div>"),this._ready=a.Deferred(),this._tree=b};return b.prototype._competency=null,b.prototype._eventNode=null,b.prototype._ready=null,b.prototype._tree=null,b.prototype.canConfig=function(){return this._tree.hasChildren(this._competency.id)},b.prototype.getConfig=function(){return null},b.prototype.getType=function(){throw new Error("Not implemented")},b.prototype.init=function(){return this._load()},b.prototype.injectTemplate=function(){return a.Deferred().reject().promise()},b.prototype.isValid=function(){return!1},b.prototype._load=function(){return a.when()},b.prototype.on=function(a,b){this._eventNode.on(a,b)},b.prototype.setTargetCompetency=function(a){this._competency=a},b.prototype._trigger=function(a,b){this._eventNode.trigger(a,[b])},b.prototype._triggerChange=function(){this._trigger("change",this)},b});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/str","tool_lp/competency_rule"],function(a,b,c){var d=function(){c.apply(this,arguments)};return d.prototype=Object.create(c.prototype),d.prototype.getType=function(){return"core_competency\\competency_rule_all"},d.prototype.isValid=function(){return!0},d});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/str","core/templates","tool_lp/competency_rule"],function(a,b,c,d){var e=function(){d.apply(this,arguments)};return e.prototype=Object.create(d.prototype),e.prototype._container=null,e.prototype._templateLoaded=!1,e.prototype.getConfig=function(){return JSON.stringify({base:{points:this._getRequiredPoints()},competencies:this._getCompetenciesConfig()})},e.prototype._getCompetenciesConfig=function(){var b=[];return this._container.find("[data-competency]").each(function(){var c=a(this),d=c.data("competency"),e=parseInt(c.find('[name="points"]').val(),10),f=c.find('[name="required"]').prop("checked");b.push({id:d,points:e,required:f?1:0})}),b},e.prototype._getRequiredPoints=function(){return parseInt(this._container.find('[name="requiredpoints"]').val()||1,10)},e.prototype.getType=function(){return"core_competency\\competency_rule_points"},e.prototype.injectTemplate=function(b){var d,e=this,f=this._tree.getChildren(this._competency.id),g={base:{points:2},competencies:[]};if(this._templateLoaded=!1,e._competency.ruletype==e.getType())try{g=JSON.parse(e._competency.ruleconfig)}catch(h){}return d={requiredpoints:g&&g.base?g.base.points:2,competency:e._competency,children:[]},a.each(f,function(b,c){var e={id:c.id,shortname:c.shortname,required:!1,points:0};g&&a.each(g.competencies,function(a,b){b.id==e.id&&(e.required=b.required?!0:!1,e.points=b.points)}),d.children.push(e)}),c.render("tool_lp/competency_rule_points",d).then(function(a){e._container=b,b.html(a),b.find("input").change(function(){e._triggerChange()}),e._templateLoaded=!0,e._triggerChange()})},e.prototype.isValid=function(){if(!this._templateLoaded)return!1;var b=this._getRequiredPoints(),c=0,d=!0;return a.each(this._getCompetenciesConfig(),function(a,b){b.points<0&&(d=!1),c+=b.points}),d=d&&c>=b},e});
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","core/ajax","core/templates","core/str","tool_lp/dialogue"],function(a,b,c,d,e,f){var g=function(b){this.options={includerelated:!1,includecourses:!1},a.extend(this.options,b)};return g.prototype.triggerCompetencyViewedEvent=function(a){c.call([{methodname:"core_competency_competency_viewed",args:{id:a}}])},g.prototype.enhanceDialogue=function(a){var b=new g({includerelated:!1});b.watch(a.getContent())},g.prototype.showDialogue=function(a){var c=this.getCompetencyDataPromise(a),e=this;c.done(function(c){d.render("tool_lp/competency_summary",c).done(function(b){e.triggerCompetencyViewedEvent(a),new f(c.competency.shortname,b,e.enhanceDialogue)}).fail(b.exception)}).fail(b.exception)},g.prototype.showDialogueFromData=function(a){var c=this;d.render("tool_lp/competency_summary",a).done(function(b){c.triggerCompetencyViewedEvent(a.id),new f(a.shortname,b,c.enhanceDialogue)}).fail(b.exception)},g.prototype.clickEventHandler=function(b){var c=b.data.compdialogue,d=a(b.currentTarget).data("id");c.showDialogue(d),b.preventDefault()},g.prototype.getCompetencyDataPromise=function(a){var d=c.call([{methodname:"tool_lp_data_for_competency_summary",args:{competencyid:a,includerelated:this.options.includerelated,includecourses:this.options.includecourses}}]);return d[0].then(function(a){return a}).fail(b.exception)},g.prototype.watch=function(b){a(b).off("click",'[data-action="competency-dialogue"]',this.clickEventHandler),a(b).on("click",'[data-action="competency-dialogue"]',{compdialogue:this},this.clickEventHandler)},g});
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","core/ajax","core/templates","core/str","tool_lp/tree","tool_lp/competencypicker"],function(a,b,c,d,e,f,g){var h=function(a,b,c){g.prototype.constructor.apply(this,[1,!1,"self",c]),this._userId=a,this._plans=[],b&&(this._planId=b,this._singlePlan=!0)};return h.prototype=Object.create(g.prototype),h.prototype._plans=null,h.prototype._planId=null,h.prototype._singlePlan=!1,h.prototype._userId=null,h.prototype._afterRender=function(){var b=this;g.prototype._afterRender.apply(b,arguments),b._singlePlan||b._find('[data-action="chooseplan"]').change(function(c){b._planId=a(c.target).val(),b._loadCompetencies().then(b._refresh.bind(b))}.bind(b))},h.prototype._fetchCompetencies=function(a,d){var e=this;return c.call([{methodname:"core_competency_list_plan_competencies",args:{id:a}}])[0].done(function(a){var b,c,f=[];for(b=0;b<a.length;b++)c=a[b].competency,c.shortname.toLowerCase().indexOf(d.toLowerCase())<0||(c.children=[],c.haschildren=0,f.push(c));e._competencies=f}).fail(b.exception)},h.prototype._getPlan=function(b){var c;return a.each(this._plans,function(a,d){return d.id==b?(c=d,!1):void 0}),c},h.prototype._loadCompetencies=function(){return this._fetchCompetencies(this._planId,this._searchText)},h.prototype._loadPlans=function(){var d,e=this;return e._plans.length>0?a.when():(d=e._singlePlan?c.call([{methodname:"core_competency_read_plan",args:{id:this._planId}}])[0].then(function(a){return[a]}):c.call([{methodname:"core_competency_list_user_plans",args:{userid:e._userId}}])[0],d.done(function(a){e._plans=a}).fail(b.exception))},h.prototype._preRender=function(){var b=this;return b._loadPlans().then(function(){return!b._planId&&b._plans.length>0&&(b._planId=b._plans[0].id),b._planId?b._loadCompetencies():(b._plans=[],a.when())}.bind(b))},h.prototype._render=function(){var b=this;return b._preRender().then(function(){b._singlePlan||a.each(b._plans,function(a,c){c.id==b._planId?c.selected=!0:c.selected=!1});var c={competencies:b._competencies,plan:b._getPlan(b._planId),plans:b._plans,search:b._searchText,singlePlan:b._singlePlan};return d.render("tool_lp/competency_picker_user_plans",c)}.bind(b))},h});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","core/templates","tool_lp/dialogue","tool_lp/competency_outcomes","core/str"],function(a,b,c,d,e,f){var g=function(b,c){this._eventNode=a("<div></div>"),this._tree=b,this._rulesModules=c,this._setUp()};return g.prototype._competency=null,g.prototype._eventNode=null,g.prototype._outcomesOption=null,g.prototype._popup=null,g.prototype._ready=null,g.prototype._rules=null,g.prototype._rulesModules=null,g.prototype._tree=null,g.prototype._afterChange=function(){this._isValid()?this._find('[data-action="save"]').prop("disabled",!1):this._find('[data-action="save"]').prop("disabled",!0)},g.prototype._afterRuleConfigChange=function(a,b){b==this._getRule()&&this._afterChange()},g.prototype._afterRender=function(){var a=this;a._find('[name="outcome"]').on("change",function(){a._switchedOutcome()}).trigger("change"),a._find('[name="rule"]').on("change",function(){a._switchedRule()}).trigger("change"),a._find('[data-action="save"]').on("click",function(){a._trigger("save",a._getConfig()),a.close()}),a._find('[data-action="cancel"]').on("click",function(){a.close()})},g.prototype.canBeConfigured=function(){var b=!1;return a.each(this._rules,function(a,c){return c.canConfig()?(b=!0,!1):void 0}),b},g.prototype.close=function(){this._popup.close(),this._popup=null},g.prototype.display=function(){var a=this;if(a._competency)return a._render().then(function(b){return f.get_string("competencyrule","tool_lp").then(function(c){a._popup=new d(c,b,a._afterRender.bind(a))})}).fail(b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._getApplicableOutcomesOptions=function(){var b=this,c=[];return a.each(b._outcomesOption,function(a,d){c.push({code:d.code,name:d.name,selected:d.code==b._competency.ruleoutcome?!0:!1})}),c},g.prototype._getApplicableRulesOptions=function(){var b=this,c=[];return a.each(b._rules,function(a,d){d.canConfig()&&c.push({name:b._getRuleName(d.getType()),type:d.getType(),selected:d.getType()==b._competency.ruletype?!0:!1})}),c},g.prototype._getConfig=function(){var a=this._getRule();return{ruletype:a?a.getType():null,ruleconfig:a?a.getConfig():null,ruleoutcome:this._getOutcome()}},g.prototype._getOutcome=function(){return this._find('[name="outcome"]').val()},g.prototype._getRule=function(){var b,c=this._find('[name="rule"]').val();return a.each(this._rules,function(a,d){return d.getType()==c?(b=d,!1):void 0}),b},g.prototype._getRuleName=function(b){var c,d=this;return a.each(d._rulesModules,function(a,d){return d.type==b?(c=d.name,!1):void 0}),c},g.prototype._initOutcomes=function(){var a=this;return e.getAll().then(function(b){a._outcomesOption=b})},g.prototype._initRules=function(){var b=this,c=[];return a.each(b._rules,function(d,e){var f=e.init().then(function(){e.setTargetCompetency(b._competency),e.on("change",b._afterRuleConfigChange.bind(b))},function(){return b._rules.splice(d,1),a.when()});c.push(f)}),a.when.apply(a.when,c)},g.prototype._isValid=function(){var a=this._getOutcome(),b=this._getRule();return a==e.NONE?!0:b?b.isValid():!1},g.prototype.on=function(a,b){this._eventNode.on(a,b)},g.prototype._preRender=function(){return this.ready()},g.prototype.ready=function(){return this._ready.promise()},g.prototype._render=function(){var a=this;return this._preRender().then(function(){var b;a.canBeConfigured()?(b={},b.outcomes=a._getApplicableOutcomesOptions(),b.rules=a._getApplicableRulesOptions()):b=!1;var d={competencyshortname:a._competency.shortname,config:b};return c.render("tool_lp/competency_rule_config",d)})},g.prototype.setTargetCompetencyId=function(b){var c=this;c._competency=c._tree.getCompetency(b),a.each(c._rules,function(a,b){b.setTargetCompetency(c._competency)})},g.prototype._setUp=function(){var b=this,c=[],d=[];b._ready=a.Deferred(),b._rules=[],a.each(b._rulesModules,function(a,b){d.push(b.amd)}),require(d,function(){a.each(arguments,function(a,c){var d=new c(b._tree);b._rules.push(d)}),c.push(b._initRules()),c.push(b._initOutcomes()),a.when.apply(a.when,c).always(function(){b._ready.resolve()})})},g.prototype._switchedOutcome=function(){var a=this,b=a._getOutcome();return b==e.NONE?(a._find('[data-region="rule-type"]').hide().find('[name="rule"]').val(-1),a._find('[data-region="rule-config"]').empty().hide(),void a._afterChange()):(a._find('[data-region="rule-type"]').show(),a._find('[data-region="rule-config"]').show(),void a._afterChange())},g.prototype._switchedRule=function(){var a=this,b=a._find('[data-region="rule-config"]'),c=a._getRule();return c?void c.injectTemplate(b).then(function(){b.show()},function(){b.empty().hide()}).always(function(){a._afterChange()}):(b.empty().hide(),void a._afterChange())},g.prototype._trigger=function(a,b){this._eventNode.trigger(a,[b])},g});
|
||||
@@ -0,0 +1 @@
|
||||
define(["core/ajax","core/notification","core/templates","tool_lp/tree","tool_lp/competency_outcomes","jquery"],function(a,b,c,d,e,f){var g={},h=0,i="",j="",k="",l=!1,m=function(a,b){var c=0,d=!1;for(a.haschildren=!1,a.children=[],c=0;c<b.length;c++)d=b[c],d.parentid==a.id&&(a.haschildren=!0,a.children.push(d),m(d,b))},n=function(b){var e=f.Deferred();return c.render("tool_lp/loading",{}).done(function(n,o){c.replaceNodeContents(f(j),n,o);var p=a.call([{methodname:"core_competency_search_competencies",args:{searchtext:b,competencyframeworkid:h}}]);p[0].done(function(a){g={};var b=0;for(b=0;b<a.length;b++)g[a[b].id]=a[b];var h=[],n=!1;for(b=0;b<a.length;b++)n=a[b],0===parseInt(n.parentid,10)&&(h.push(n),m(n,a));var o={shortname:i,canmanage:l,competencies:h};c.render("tool_lp/competencies_tree_root",o).done(function(a,b){c.replaceNodeContents(f(j),f(a).html(),b);var h=new d(j,!1);if(k){var i=f(j).find("[data-id="+k+"]");i.length&&(h.selectItem(i),h.updateFocus(i))}e.resolve(g)}).fail(e.reject)}).fail(e.reject)}),e.promise()},o=function(a,b){var c=b.selected;k=c.attr("data-id")};return{init:function(a,c,d,e,f){h=a,i=c,l=f,j=e,n(d).fail(b.exception),this.on("selectionchanged",o)},on:function(a,b){f(j).on(a,b)},getChildren:function(a){var b=[];return f.each(g,function(c,d){d.parentid==a&&b.push(d)}),b},getCompetencyFrameworkId:function(){return h},getCompetency:function(a){return g[a]},getCompetencyLevel:function(a){var b=this.getCompetency(a),c=b.path.replace(/^\/|\/$/g,"").split("/").length;return c},hasChildren:function(a){return this.getChildren(a).length>0},hasRule:function(a){var b=this.getCompetency(a);return b?b.ruleoutcome!=e.OUTCOME_NONE&&b.ruletype:!1},reloadCompetencies:function(){return n("").fail(b.exception)},listCompetencies:function(){return g}}});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","tool_lp/dialogue","core/str","core/ajax","core/templates"],function(a,b,c,d,e,f){var g=function(b){a(b).on("click",this.configureSettings.bind(this))};return g.prototype._dialogue=null,g.prototype.configureSettings=function(e){var g=a(e.target).closest("a").data("courseid"),h=a(e.target).closest("a").data("pushratingstouserplans"),i={courseid:g,settings:{pushratingstouserplans:h}};e.preventDefault(),f.render("tool_lp/course_competency_settings",i).done(function(a){d.get_string("configurecoursecompetencysettings","tool_lp").done(function(b){this._dialogue=new c(b,a,this.addListeners.bind(this))}.bind(this)).fail(b.exception)}.bind(this)).fail(b.exception)},g.prototype.addListeners=function(){var a=this._find('[data-action="save"]');a.on("click",this.saveSettings.bind(this));var b=this._find('[data-action="cancel"]');b.on("click",this.cancelChanges.bind(this))},g.prototype.cancelChanges=function(a){a.preventDefault(),this._dialogue.close()},g.prototype._find=function(b){return a('[data-region="coursecompetencysettings"]').find(b)},g.prototype.saveSettings=function(a){a.preventDefault();var c=this._find('input[name="pushratingstouserplans"]:checked').val(),d=this._find('input[name="courseid"]').val(),f={pushratingstouserplans:c};e.call([{methodname:"core_competency_update_course_competency_settings",args:{courseid:d,settings:f}}])[0].done(function(){this.refreshCourseCompetenciesPage()}.bind(this)).fail(b.exception)},g.prototype.refreshCourseCompetenciesPage=function(){var c=this._find('input[name="courseid"]').val();e.call([{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:c}}])[0].done(function(c){f.render("tool_lp/course_competencies_page",c).done(function(b,c){a('[data-region="coursecompetenciespage"]').replaceWith(b),f.runTemplateJS(c),this._dialogue.close()}.bind(this)).fail(b.exception)}.bind(this)).fail(b.exception)},g});
|
||||
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
define(["core/yui"],function(a){var b=function(b,c,d,e,f){this.yuiDialogue=null;var g=this;"undefined"==typeof f&&(f=!1),a.use("moodle-core-notification","timers",function(){var h="480px";f&&(h="800px"),g.yuiDialogue=new M.core.dialogue({headerContent:b,bodyContent:c,draggable:!0,visible:!1,center:!0,modal:!0,width:h}),g.yuiDialogue.after("visibleChange",function(b){b.newVal?"undefined"!=typeof d&&a.soon(function(){d(g),g.yuiDialogue.centerDialogue()}):"undefined"!=typeof e&&a.soon(function(){e(g)})}),g.yuiDialogue.show()})};return b.prototype.close=function(){this.yuiDialogue.hide(),this.yuiDialogue.destroy()},b.prototype.getContent=function(){return this.yuiDialogue.bodyNode.getDOMNode()},b});
|
||||
@@ -0,0 +1 @@
|
||||
define(["core/str","core/yui"],function(a,b){var c=function(a){var b=a.drag.get("node"),c=a.drop.get("node");this.callback(b.getDOMNode(),c.getDOMNode())};return{dragdrop:function(d,e,f,g,h,i,j,k){a.get_strings([{key:"emptydragdropregion",component:"moodle"},{key:"movecontent",component:"moodle"},{key:"tocontent",component:"moodle"}]).done(function(){b.use("moodle-tool_lp-dragdrop-reorder",function(){var a={callback:k};M.tool_lp.dragdrop_reorder({group:d,dragHandleText:e,sameNodeText:f,parentNodeText:g,sameNodeClass:h,parentNodeClass:i,dragHandleInsertClass:j,callback:b.bind(c,a)})})})}}});
|
||||
+1
@@ -0,0 +1 @@
|
||||
define(["jquery"],function(a){var b=function(){this._eventNode=a("<div></div>")};return b.prototype._eventNode=null,b.prototype.on=function(a,b){this._eventNode.on(a,b)},b.prototype._trigger=function(a,b){this._eventNode.trigger(a,[b])},b});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","core/ajax","core/str","core/log"],function(a,b,c,d,e){var f={},g=function(g,h){"undefined"==typeof f[g]&&(f[g]=a("body").delegate(g,"click",function(f){var g=a(f.currentTarget).parents(h);if(!g.length||g.length>1)return void e.error("None or too many evidence container were found.");var i=g.data("id");return i?(f.preventDefault(),f.stopPropagation(),void d.get_strings([{key:"confirm",component:"moodle"},{key:"areyousure",component:"moodle"},{key:"delete",component:"moodle"},{key:"cancel",component:"moodle"}]).done(function(a){b.confirm(a[0],a[1],a[2],a[3],function(){var a=c.call([{methodname:"core_competency_delete_evidence",args:{id:i}}]);a[0].then(function(){g.remove()}).fail(b.exception)})}).fail(b.exception)):void e.error("Evidence ID was not found.")}))};return{register:g}});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/ajax","core/templates"],function(a,b,c){return{processResults:function(b,c){var d=[];return a.each(c,function(a,b){d.push({value:b.id,label:b._label})}),d},transport:function(d,e,f,g){var h,i=parseInt(a(d).data("contextid"),10),j=a(d).data("includes");h=b.call([{methodname:"tool_lp_search_cohorts",args:{query:e,context:{contextid:i},includes:j}}]),h[0].then(function(b){var d=[],e=0;return a.each(b.cohorts,function(a,b){d.push(c.render("tool_lp/form-cohort-selector-suggestion",b))}),a.when.apply(a.when,d).then(function(){var c=arguments;a.each(b.cohorts,function(a,b){b._label=c[e],e++}),f(b.cohorts)})},g)}}});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/ajax","core/templates"],function(a,b,c){return{processResults:function(b,c){var d=[];return a.each(c,function(a,b){d.push({value:b.id,label:b._label})}),d},transport:function(d,e,f,g){var h,i=a(d).data("capability");"undefined"==typeof i&&(i=""),h=b.call([{methodname:"tool_lp_search_users",args:{query:e,capability:i}}]),h[0].then(function(b){var d=[],e=0;return a.each(b.users,function(b,e){var f=e,g=[];a.each(["idnumber","email","phone1","phone2","department","institution"],function(a,b){"undefined"!=typeof e[b]&&""!==e[b]&&(f.hasidentity=!0,g.push(e[b]))}),f.identity=g.join(", "),d.push(c.render("tool_lp/form-user-selector-suggestion",f))}),a.when.apply(a.when,d).then(function(){var c=arguments;a.each(b.users,function(a,b){b._label=c[e],e++}),f(b.users)})},g)}}});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/templates","core/ajax","core/notification","core/str"],function(a,b,c,d,e){var f=0,g=0,h=function(c,d){a('[data-region="managecompetencies"]').replaceWith(c),b.runTemplateJS(d)},i=function(a){b.render("tool_lp/manage_competency_frameworks_page",a).done(h).fail(d.exception)},j=function(b){b.preventDefault(),g=a(this).attr("data-frameworkid");var e=c.call([{methodname:"core_competency_duplicate_competency_framework",args:{id:g}},{methodname:"tool_lp_data_for_competency_frameworks_manage_page",args:{pagecontext:{contextid:f}}}]);e[1].done(i).fail(d.exception)},k=function(){var a=c.call([{methodname:"core_competency_delete_competency_framework",args:{id:g}},{methodname:"tool_lp_data_for_competency_frameworks_manage_page",args:{pagecontext:{contextid:f}}}]);a[0].done(function(a){if(a===!1){var b=c.call([{methodname:"core_competency_read_competency_framework",args:{id:g}}]);b[0].done(function(a){e.get_strings([{key:"frameworkcannotbedeleted",component:"tool_lp",param:a.shortname},{key:"cancel",component:"moodle"}]).done(function(a){d.alert(null,a[0])}).fail(d.exception)})}}).fail(d.exception),a[1].done(i).fail(d.exception)},l=function(b){b.preventDefault();var f=a(this).attr("data-frameworkid");g=f;var h=c.call([{methodname:"core_competency_read_competency_framework",args:{id:g}}]);h[0].done(function(a){e.get_strings([{key:"confirm",component:"moodle"},{key:"deletecompetencyframework",component:"tool_lp",param:a.shortname},{key:"delete",component:"moodle"},{key:"cancel",component:"moodle"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],k)}).fail(d.exception)}).fail(d.exception)};return{deleteHandler:l,duplicateHandler:j,init:function(a){f=a}}});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/templates","core/ajax","core/notification","core/str"],function(a,b,c,d,e){var f=0,g=0,h=function(c,d){a('[data-region="managecompetencies"]').replaceWith(c),b.runTemplateJS(d)},i=function(a){b.render("tool_lp/manage_competency_frameworks_page",a).done(h).fail(d.exception)},j=function(){var a=c.call([{methodname:"core_competency_delete_competency_framework",args:{id:g}},{methodname:"tool_lp_data_for_competency_frameworks_manage_page",args:{pagecontext:{contextid:f}}}]);a[1].done(i).fail(d.exception)},k=function(b){b.preventDefault();var f=a(this).attr("data-frameworkid");g=f;var h=c.call([{methodname:"core_competency_read_competency_framework",args:{id:g}}]);h[0].done(function(a){e.get_strings([{key:"confirm",component:"moodle"},{key:"deletecompetencyframework",component:"tool_lp",param:a.shortname},{key:"delete",component:"moodle"},{key:"cancel",component:"moodle"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],j)}).fail(d.exception)}).fail(d.exception)};return{deleteHandler:k,init:function(a){f=a}}});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/ajax","core/notification"],function(a,b,c){return{list:function(d,e){var f,g={context:{contextid:d}};return a.extend(g,"undefined"==typeof e?{}:e),f=b.call([{methodname:"core_competency_list_competency_frameworks",args:g}])[0],f.fail(c.exception)},processResults:function(b,c){var d=[];return a.each(c,function(a,b){d.push({value:b.id,label:b.shortname+" "+b.idnumber})}),d},transport:function(b,c,d){var e=a(b),f=e.data("contextid"),g=e.data("onlyvisible");if(!f)throw new Error("The attribute data-contextid is required on "+b);this.list(f,{query:c,onlyvisible:g}).then(d)}}});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","core/templates","tool_lp/dialogue","tool_lp/event_base","core/str"],function(a,b,c,d,e,f){var g=function(a){e.prototype.constructor.apply(this,[]),this._ratingOptions=a};return g.prototype=Object.create(e.prototype),g.prototype._popup=null,g.prototype._ratingOptions=null,g.prototype._afterRender=function(){var b=this._find('[data-action="rate"]'),c=this._find('[name="rating"]'),d=this._find('[name="comment"]');this._find('[data-action="cancel"]').click(function(a){a.preventDefault(),this._trigger("cancelled"),this.close()}.bind(this)),c.change(function(){var c=a(this);c.val()?b.prop("disabled",!1):b.prop("disabled",!0)}).change(),b.click(function(a){a.preventDefault();var b=c.val();b&&(this._trigger("rated",{rating:b,note:d.val()}),this.close())}.bind(this))},g.prototype.close=function(){this._popup.close(),this._popup=null},g.prototype.display=function(){return this._render().then(function(a){return f.get_string("rate","tool_lp").then(function(b){this._popup=new d(b,a,this._afterRender.bind(this))}.bind(this))}.bind(this)).fail(b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._render=function(){var a={cangrade:this._canGrade,ratings:this._ratingOptions};return c.render("tool_lp/competency_grader",a)},g});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","core/ajax","core/log","tool_lp/grade_dialogue","tool_lp/event_base","tool_lp/scalevalues"],function(a,b,c,d,e,f,g){var h=function(b,c,d,e,g,h,i){f.prototype.constructor.apply(this,[]);var j=a(b);if(!j.length)throw new Error("Could not find the trigger");this._scaleId=c,this._competencyId=d,this._userId=e,this._planId=g,this._courseId=h,this._chooseStr=i,this._setUp(),j.click(function(a){a.preventDefault(),this._dialogue.display()}.bind(this)),this._planId?(this._methodName="core_competency_grade_competency_in_plan",this._args={competencyid:this._competencyId,planid:this._planId}):this._courseId?(this._methodName="core_competency_grade_competency_in_course",this._args={competencyid:this._competencyId,courseid:this._courseId,userid:this._userId}):(this._methodName="core_competency_grade_competency",this._args={userid:this._userId,competencyid:this._competencyId})};return h.prototype=Object.create(f.prototype),h.prototype._setUp=function(){var a=[],d=this,f=g.get_values(d._scaleId);f.done(function(f){a.push({value:"",name:d._chooseStr});for(var g=0;g<f.length;g++){var h=f[g];a.push({value:h.id,name:h.name})}d._dialogue=new e(a),d._dialogue.on("rated",function(a,e){var f=d._args;f.grade=e.rating,f.note=e.note,c.call([{methodname:d._methodName,args:f,done:function(a){d._trigger("competencyupdated",{args:f,evidence:a})}.bind(d),fail:b.exception}])}.bind(d))}).fail(b.exception)},h.prototype._scaleId=null,h.prototype._competencyId=null,h.prototype._userId=null,h.prototype._planId=null,h.prototype._courseId=null,h.prototype._chooseStr=null,h.prototype._dialogue=null,h});
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
define(["jquery","core/templates","core/notification","core/yui"],function(a,b,c,d){var e=function(b){var c=a(b);c.find("li a").each(function(b,c){a(c).addClass("menu-action"),a(c).attr("role","menuitem")}),a(this).replaceWith(c),d.use("moodle-core-actionmenu",function(){if(null===M.core.actionmenu.instance)M.core.actionmenu.init();else{var a=d.one(c.get(0));M.core.actionmenu.newDOMNode(a.ancestor())}})};return{menu:function(d,f){a(f).each(function(f,g){var h=[];a(g).find("li").each(function(b,c){h.push(a(c).html().trim())});var i={triggerMessage:d,links:h},j=a.proxy(e,g);b.render("core/menu",i).done(j).fail(c.exception)})}}});
|
||||
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define(["jquery","core/ajax","core/str","tool_lp/competencypicker","core/templates","core/notification"],function(a,b,c,d,e,f){var g=function(a,b,c,d,e,f){this.buttonSelector=a,this.inputHiddenSelector=b,this.staticElementSelector=c,this.frameworkId=d,this.frameworkMaxLevel=e,this.pageContextId=f,this.registerEvents()};return g.prototype.buttonSelector=null,g.prototype.inputHiddenSelector=null,g.prototype.staticElementSelector=null,g.prototype.frameworkId=null,g.prototype.frameworkMaxLevel=null,g.prototype.pageContextId=null,g.prototype.setParent=function(d){var e=this;0!==d.competencyId?b.call([{methodname:"core_competency_read_competency",args:{id:d.competencyId}}])[0].done(function(b){a(e.staticElementSelector).html(b.shortname),a(e.inputHiddenSelector).val(b.id)}).fail(f.exception):c.get_string("competencyframeworkroot","tool_lp").then(function(b){a(e.staticElementSelector).html(b),a(e.inputHiddenSelector).val(d.competencyId)}).fail(f.exception)},g.prototype.registerEvents=function(){var c=this;a(c.buttonSelector).on("click",function(a){a.preventDefault();var g=new d(c.pageContextId,c.frameworkId,"self",!1),h=c.frameworkMaxLevel;g._fetchCompetencies=function(a,c){var d=this;return b.call([{methodname:"core_competency_search_competencies",args:{searchtext:c,competencyframeworkid:a}}])[0].done(function(a){function b(a,c){for(var d=0;d<c.length;d++){var e=String(c[d].path),g=e.split("/").length-2;g>=h&&"0"!==c[d].id&&f.push(c[d].id),c[d].parentid==a.id&&(a.haschildren=!0,c[d].children=[],c[d].haschildren=!1,a.children[a.children.length]=c[d],b(c[d],c))}}var c,e,f=[],g=[];for(c=0;c<a.length;c++)e=a[c],"0"==e.parentid&&(e.children=[],e.haschildren=0,g[g.length]=e,b(e,a));d._competencies=g,d.setDisallowedCompetencyIDs(f)}.bind(d)).fail(f.exception)},g._render=function(){var a=this;return a._preRender().then(function(){var b={competencies:a._competencies,framework:a._getFramework(a._frameworkId),frameworks:a._frameworks,search:a._searchText,singleFramework:a._singleFramework};return e.render("tool_lp/competency_picker_competencyform",b)}.bind(a))},g.on("save",function(a,b){c.setParent(b)}.bind(c)),g.display()})},{init:function(a,b,c,d,e,f){new g(a,b,c,d,e,f)}}});
|
||||
+1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
define(["jquery","core/templates","core/ajax","core/notification","core/str"],function(a,b,c,d,e){var f=1,g=2,h=0,i=0,j=null,k=function(c,d){a('[data-region="plans"]').replaceWith(c),b.runTemplateJS(d)},l=function(a){b.render("tool_lp/plans_page",a).done(k).fail(d.exception)},m=function(){var a=c.call([{methodname:"core_competency_delete_plan",args:{id:h}},{methodname:"tool_lp_data_for_plans_page",args:{userid:i}}]);a[1].done(l).fail(d.exception)},n=function(b){b.preventDefault(),h=a(this).attr("data-planid"),i=a(this).attr("data-userid");var f=c.call([{methodname:"core_competency_read_plan",args:{id:h}}]);f[0].done(function(a){e.get_strings([{key:"confirm",component:"moodle"},{key:"deleteplan",component:"tool_lp",param:a.name},{key:"delete",component:"moodle"},{key:"cancel",component:"moodle"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],m)}).fail(d.exception)}).fail(d.exception)},o=function(){var a=c.call([{methodname:"core_competency_update_plan_status",args:{planid:h,status:j}},{methodname:"tool_lp_data_for_plans_page",args:{userid:i}}]);a[1].done(l).fail(d.exception)},p=function(b){b.preventDefault(),h=a(this).attr("data-planid"),i=a(this).attr("data-userid"),j=parseInt(a(this).attr("data-status"));var k="";j===f&&(k="reopenplan"),j===g&&(k="completeplan");var l=c.call([{methodname:"core_competency_read_plan",args:{id:h}}]);l[0].done(function(a){e.get_strings([{key:"confirm",component:"moodle"},{key:k+"confirm",component:"tool_lp",param:a.name},{key:k,component:"tool_lp"},{key:"cancel",component:"moodle"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],o)}).fail(d.exception)}).fail(d.exception)};return{deleteHandler:n,updateStatusHandler:p}});
|
||||
+1
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","core/templates","core/ajax","tool_lp/dialogue","tool_lp/scalevalues"],function(a,b,c,d,e,f){var g=function(b,c,d){this.selectSelector=b,this.inputSelector=c,this.triggerSelector=d,this.originalscaleid=a(b).val(),a(b).on("change",this.scaleChangeHandler.bind(this)).change(),a(d).click(this.showConfig.bind(this))};return g.prototype.selectSelector=null,g.prototype.inputSelector=null,g.prototype.triggerSelector=null,g.prototype.scalevalues=null,g.prototype.originalscaleid=0,g.prototype.scaleid=0,g.prototype.popup=null,g.prototype.showConfig=function(){var d=this;if(this.scaleid=a(this.selectSelector).val(),!(this.scaleid<=0)){var f=a(this.selectSelector).find("option:selected").text();this.getScaleValues(this.scaleid).done(function(){var a={scalename:f,scales:d.scalevalues};c.render("tool_lp/scale_configuration_page",a).done(function(a){new e(f,a,d.initScaleConfig.bind(d))}).fail(b.exception)}).fail(b.exception)}},g.prototype.retrieveOriginalScaleConfig=function(){var b=a(this.inputSelector).val();if(""!==b){var c=a.parseJSON(b),d=c.shift();if(d.scaleid===this.originalscaleid)return c}return""},g.prototype.initScaleConfig=function(b){this.popup=b;var c=a(b.getContent());if(this.originalscaleid===this.scaleid){var d=this.retrieveOriginalScaleConfig();""!==d&&d.forEach(function(a){1===a.scaledefault&&c.find('[data-field="tool_lp_scale_default_'+a.id+'"]').attr("checked",!0),1===a.proficient&&c.find('[data-field="tool_lp_scale_proficient_'+a.id+'"]').attr("checked",!0)})}c.on("click",'[data-action="close"]',function(){this.setScaleConfig(),b.close()}.bind(this)),c.on("click",'[data-action="cancel"]',function(){b.close()}.bind(this))},g.prototype.setScaleConfig=function(){var b=a(this.popup.getContent()),c=[{scaleid:this.scaleid}];this.scalevalues.forEach(function(a){var d=0,e=0;b.find('[data-field="tool_lp_scale_default_'+a.id+'"]').is(":checked")&&(d=1),b.find('[data-field="tool_lp_scale_proficient_'+a.id+'"]').is(":checked")&&(e=1),(d||e)&&c.push({id:a.id,scaledefault:d,proficient:e})});var d=JSON.stringify(c);a(this.inputSelector).val(d),this.originalscaleid=this.scaleid},g.prototype.getScaleValues=function(a){return f.get_values(a).then(function(a){return this.scalevalues=a,a}.bind(this))},g.prototype.scaleChangeHandler=function(b){a(b.target).val()<=0?a(this.triggerSelector).prop("disabled",!0):a(this.triggerSelector).prop("disabled",!1)},{init:function(a,b,c){return new g(a,b,c)}}});
|
||||
+1
@@ -0,0 +1 @@
|
||||
define(["jquery","core/ajax"],function(a,b){var c=[];return{get_values:function(d){var e=a.Deferred();return"undefined"==typeof c[d]?b.call([{methodname:"core_competency_get_scale_values",args:{scaleid:d},done:function(a){c[d]=a,e.resolve(a)},fail:e.reject}]):e.resolve(c[d]),e.promise()}}});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/templates","core/ajax","core/notification","core/str","tool_lp/actionselector"],function(a,b,c,d,e,f){var g=0,h=0,i=!0,j=function(c,d){a('[data-region="managetemplates"]').replaceWith(c),b.runTemplateJS(d)},k=function(a){b.render("tool_lp/manage_templates_page",a).done(j).fail(d.exception)},l=function(){var a=c.call([{methodname:"core_competency_delete_template",args:{id:h,deleteplans:i}},{methodname:"tool_lp_data_for_templates_manage_page",args:{pagecontext:{contextid:g}}}]);a[1].done(k).fail(d.exception)},m=function(b){b.preventDefault(),h=a(this).attr("data-templateid");var e=c.call([{methodname:"core_competency_duplicate_template",args:{id:h}},{methodname:"tool_lp_data_for_templates_manage_page",args:{pagecontext:{contextid:g}}}]);e[1].done(k).fail(d.exception)},n=function(b){b.preventDefault();var g=a(this).attr("data-templateid");h=g,i=!0;var j=c.call([{methodname:"core_competency_read_template",args:{id:h}},{methodname:"core_competency_template_has_related_data",args:{id:h}}]);j[0].done(function(a){j[1].done(function(b){b?e.get_strings([{key:"deletetemplate",component:"tool_lp",param:a.shortname},{key:"deletetemplatewithplans",component:"tool_lp"},{key:"deleteplans",component:"tool_lp"},{key:"unlinkplanstemplate",component:"tool_lp"},{key:"confirm",component:"moodle"},{key:"cancel",component:"moodle"}]).done(function(a){var b=[{text:a[2],value:"delete"},{text:a[3],value:"unlink"}],c=new f(a[0],a[1],b,a[4],a[5]);c.display(),c.on("save",function(a,b){"delete"!=b.action&&(i=!1),l()})}).fail(d.exception):e.get_strings([{key:"confirm",component:"moodle"},{key:"deletetemplate",component:"tool_lp",param:a.shortname},{key:"delete",component:"moodle"},{key:"cancel",component:"moodle"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],l)}).fail(d.exception)}).fail(d.exception)}).fail(d.exception)};return{deleteHandler:n,duplicateHandler:m,init:function(a){g=a}}});
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define(["jquery"],function(a){var b=function(b,c,d,e,f,g){this._baseUrl=d,this._userId=e+"",this._competencyId=f+"",this._courseId=g,this._ignoreFirstUser=!0,this._ignoreFirstCompetency=!0,a(b).on("change",this._userChanged.bind(this)),a(c).on("change",this._competencyChanged.bind(this))};return b.prototype._userChanged=function(b){if(this._ignoreFirstUser)return void(this._ignoreFirstUser=!1);var c=a(b.target).val(),d="?userid="+c+"&courseid="+this._courseId+"&competencyid="+this._competencyId;document.location=this._baseUrl+d},b.prototype._competencyChanged=function(b){if(this._ignoreFirstCompetency)return void(this._ignoreFirstCompetency=!1);var c=a(b.target).val(),d="?userid="+this._userId+"&courseid="+this._courseId+"&competencyid="+c;document.location=this._baseUrl+d},b.prototype._competencyId=null,b.prototype._userId=null,b.prototype._courseId=null,b.prototype._baseUrl=null,b.prototype._ignoreFirstUser=null,b.prototype._ignoreFirstCompetency=null,b});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","core/ajax","core/templates"],function(a,b,c,d){var e=function(a,b,c,d,e,f){this._rootElement=a,this._competencyId=b,this._userId=c,this._planId=d,this._courseId=e,this._valid=!0,this._displayuser="undefined"!=typeof f?f:!1,this._planId?(this._methodName="tool_lp_data_for_user_competency_summary_in_plan",this._args={competencyid:this._competencyId,planid:this._planId},this._templateName="tool_lp/user_competency_summary_in_plan"):this._courseId?(this._methodName="tool_lp_data_for_user_competency_summary_in_course",this._args={userid:this._userId,competencyid:this._competencyId,courseid:this._courseId},this._templateName="tool_lp/user_competency_summary_in_course"):(this._methodName="tool_lp_data_for_user_competency_summary",this._args={userid:this._userId,competencyid:this._competencyId},this._templateName="tool_lp/user_competency_summary")};return e.prototype.reload=function(){var a=this,e=[];this._valid&&(e=c.call([{methodname:this._methodName,args:this._args}]),e[0].done(function(c){a._displayuser&&(c.displayuser=!0),d.render(a._templateName,c).done(function(b,c){d.replaceNode(a._rootElement,b,c)}).fail(b.exception)}).fail(b.exception))},e.prototype._rootElement=null,e.prototype._courseId=null,e.prototype._valid=null,e.prototype._planId=null,e.prototype._competencyId=null,e.prototype._userId=null,e.prototype._methodName=null,e.prototype._args=null,e.prototype._templateName=null,e.prototype._displayuser=!1,e});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/templates","core/ajax","core/notification","core/str","tool_lp/menubar","tool_lp/event_base"],function(a,b,c,d,e,f,g){var h=function(){g.prototype.constructor.apply(this,[])};return h.prototype=Object.create(g.prototype),h.prototype._nodeSelector='[data-node="user-competency"]',h.prototype._cancelReviewRequest=function(a){var b={methodname:"core_competency_user_competency_cancel_review_request",args:{userid:a.userid,competencyid:a.competencyid}};c.call([b])[0].then(function(){this._trigger("review-request-cancelled",a),this._trigger("status-changed",a)}.bind(this),function(){this._trigger("error-occured",a)}.bind(this))},h.prototype.cancelReviewRequest=function(a){this._cancelReviewRequest(a)},h.prototype._cancelReviewRequestHandler=function(b){b.preventDefault();var c=this._findUserCompetencyData(a(b.target));this.cancelReviewRequest(c)},h.prototype._requestReview=function(a){var b={methodname:"core_competency_user_competency_request_review",args:{userid:a.userid,competencyid:a.competencyid}};c.call([b])[0].then(function(){this._trigger("review-requested",a),this._trigger("status-changed",a)}.bind(this),function(){this._trigger("error-occured",a)}.bind(this))},h.prototype.requestReview=function(a){this._requestReview(a)},h.prototype._requestReviewHandler=function(b){b.preventDefault();var c=this._findUserCompetencyData(a(b.target));this.requestReview(c)},h.prototype._startReview=function(a){var b={methodname:"core_competency_user_competency_start_review",args:{userid:a.userid,competencyid:a.competencyid}};c.call([b])[0].then(function(){this._trigger("review-started",a),this._trigger("status-changed",a)}.bind(this),function(){this._trigger("error-occured",a)}.bind(this))},h.prototype.startReview=function(a){this._startReview(a)},h.prototype._startReviewHandler=function(b){b.preventDefault();var c=this._findUserCompetencyData(a(b.target));this.startReview(c)},h.prototype._stopReview=function(a){var b={methodname:"core_competency_user_competency_stop_review",args:{userid:a.userid,competencyid:a.competencyid}};c.call([b])[0].then(function(){this._trigger("review-stopped",a),this._trigger("status-changed",a)}.bind(this),function(){this._trigger("error-occured",a)}.bind(this))},h.prototype.stopReview=function(a){this._stopReview(a)},h.prototype._stopReviewHandler=function(b){b.preventDefault();var c=this._findUserCompetencyData(a(b.target));this.stopReview(c)},h.prototype.enhanceMenubar=function(a){f.enhance(a,{'[data-action="request-review"]':this._requestReviewHandler.bind(this),'[data-action="cancel-review-request"]':this._cancelReviewRequestHandler.bind(this)})},h.prototype._findUserCompetencyData=function(a){var b,c=a.parents(this._nodeSelector);if(1!=c.length)throw new Error("The evidence node was not located.");if(b=c.data(),"undefined"==typeof b||"undefined"==typeof b.userid||"undefined"==typeof b.competencyid)throw new Error("User competency data could not be found.");return b},h.prototype.enhanceMenubar=function(a){f.enhance(a,{'[data-action="request-review"]':this._requestReviewHandler.bind(this),'[data-action="cancel-review-request"]':this._cancelReviewRequestHandler.bind(this),'[data-action="start-review"]':this._startReviewHandler.bind(this),'[data-action="stop-review"]':this._stopReviewHandler.bind(this)})},h.prototype.registerEvents=function(b){var c=a(b);c.find('[data-action="request-review"]').click(this._requestReviewHandler.bind(this)),c.find('[data-action="cancel-review-request"]').click(this._cancelReviewRequestHandler.bind(this)),c.find('[data-action="start-review"]').click(this._startReviewHandler.bind(this)),c.find('[data-action="stop-review"]').click(this._stopReviewHandler.bind(this))},h});
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,193 @@
|
||||
// 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 - <[email protected]>
|
||||
* @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;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,338 @@
|
||||
// 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 <[email protected]>
|
||||
* @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;
|
||||
});
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,175 @@
|
||||
// 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;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,199 @@
|
||||
// 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;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,861 @@
|
||||
// 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 <[email protected]>
|
||||
* @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.
|
||||
*/
|
||||
var selectionChanged = function(evt, params) {
|
||||
var node = params.selected,
|
||||
id = $(node).data('id'),
|
||||
btn = $('[data-region="competencyactions"] [data-action="add"]'),
|
||||
actionMenu = $('[data-region="competencyactionsmenu"]'),
|
||||
selectedTitle = $('[data-region="selected-competency"]'),
|
||||
level = 0,
|
||||
sublevel = 1;
|
||||
|
||||
menubar.closeAll();
|
||||
|
||||
if (typeof id === "undefined") {
|
||||
// Assume this is the root of the tree.
|
||||
// Here we are only getting the text from the top of the tree, to do it we clone the tree,
|
||||
// remove all children and then call text on the result.
|
||||
$('[data-region="competencyinfo"]').html(node.clone().children().remove().end().text());
|
||||
$('[data-region="competencyactions"]').data('competency', null);
|
||||
actionMenu.hide();
|
||||
|
||||
} else {
|
||||
var competency = treeModel.getCompetency(id);
|
||||
|
||||
level = treeModel.getCompetencyLevel(id);
|
||||
if (!hasSubLevel(level)) {
|
||||
sublevel = false;
|
||||
} else {
|
||||
sublevel = level + 1;
|
||||
}
|
||||
|
||||
actionMenu.show();
|
||||
$('[data-region="competencyactions"]').data('competency', competency);
|
||||
renderCompetencySummary(competency);
|
||||
// Log Competency viewed event.
|
||||
triggerCompetencyViewedEvent(competency);
|
||||
}
|
||||
|
||||
strSelectedTaxonomy(level).then(function(str) {
|
||||
selectedTitle.text(str);
|
||||
});
|
||||
|
||||
if (!sublevel) {
|
||||
btn.hide();
|
||||
} else {
|
||||
strAddTaxonomy(sublevel).then(function(str) {
|
||||
btn.show()
|
||||
.find('[data-region="term"]')
|
||||
.text(str);
|
||||
});
|
||||
}
|
||||
// We handled this event so consume it.
|
||||
evt.preventDefault();
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the string "Selected <taxonomy>".
|
||||
*
|
||||
* @function parseTaxonomies
|
||||
* @param {String} Comma separated list of taxonomies.
|
||||
* @return {Array} of level => taxonomystr
|
||||
*/
|
||||
var parseTaxonomies = function(taxonomiesstr) {
|
||||
var all = taxonomiesstr.split(',');
|
||||
all.unshift("");
|
||||
delete all[0];
|
||||
|
||||
// Note we don't need to fill holes, because other functions check for empty anyway.
|
||||
return all;
|
||||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Initialise this page (attach event handlers etc).
|
||||
*
|
||||
* @method init
|
||||
* @param {Object} model The tree model provides some useful functions for loading and searching competencies.
|
||||
* @param {Number} pagectxid The page context ID.
|
||||
* @param {Object} taxonomies Constants indexed by level.
|
||||
* @param {Object} rulesMods The modules of the rules.
|
||||
*/
|
||||
init: function(model, pagectxid, taxonomies, rulesMods) {
|
||||
treeModel = model;
|
||||
pageContextId = pagectxid;
|
||||
taxonomiesConstants = parseTaxonomies(taxonomies);
|
||||
rulesModules = rulesMods;
|
||||
|
||||
$('[data-region="competencyactions"] [data-action="add"]').on('click', addHandler);
|
||||
|
||||
menubar.enhance('.competencyactionsmenu', {
|
||||
'[data-action="edit"]': editHandler,
|
||||
'[data-action="delete"]': deleteCompetencyHandler,
|
||||
'[data-action="move"]': moveHandler,
|
||||
'[data-action="moveup"]': moveUpHandler,
|
||||
'[data-action="movedown"]': moveDownHandler,
|
||||
'[data-action="linkedcourses"]': seeCoursesHandler,
|
||||
'[data-action="relatedcompetencies"]': relateCompetenciesHandler.bind(this),
|
||||
'[data-action="competencyrules"]': ruleConfigHandler.bind(this)
|
||||
});
|
||||
$('[data-region="competencyactionsmenu"]').hide();
|
||||
$('[data-region="competencyactions"] [data-action="add"]').hide();
|
||||
|
||||
$('[data-region="filtercompetencies"]').on('submit', updateSearchHandler);
|
||||
// Simple html5 drag drop because we already added an accessible alternative.
|
||||
var top = $('[data-region="managecompetencies"] [data-enhance="tree"]');
|
||||
top.on('dragstart', 'li>span', dragStart)
|
||||
.on('dragover', 'li>span', allowDrop)
|
||||
.on('dragenter', 'li>span', dragEnter)
|
||||
.on('dragleave', 'li>span', dragLeave)
|
||||
.on('drop', 'li>span', dropOver);
|
||||
|
||||
model.on('selectionchanged', selectionChanged);
|
||||
|
||||
// Prepare the configuration tool.
|
||||
ruleConfigInstance = new RuleConfig(treeModel, rulesModules);
|
||||
ruleConfigInstance.on('save', ruleConfigSaveHandler.bind(this));
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,175 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Display Competency in dialogue box.
|
||||
*
|
||||
* @module tool_lp/Competencydialogue
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Issam Taboubi <[email protected]>
|
||||
* @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/dialogue'],
|
||||
function($, notification, ajax, templates, str, Dialogue) {
|
||||
|
||||
/**
|
||||
* Constructor for CompetencyDialogue.
|
||||
*
|
||||
* @param {Object} options
|
||||
*
|
||||
*/
|
||||
var Competencydialogue = function(options) {
|
||||
this.options = {
|
||||
includerelated: false,
|
||||
includecourses: false
|
||||
};
|
||||
$.extend(this.options, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Log the competency viewed event.
|
||||
*
|
||||
* @param {Number} The competency ID.
|
||||
* @method triggerCompetencyViewedEvent
|
||||
*/
|
||||
Competencydialogue.prototype.triggerCompetencyViewedEvent = function(competencyId) {
|
||||
ajax.call([{
|
||||
methodname: 'core_competency_competency_viewed',
|
||||
args: { id: competencyId }
|
||||
}]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback on dialogue display, it apply enhance on competencies dialogue.
|
||||
*
|
||||
* @param {Dialogue} dialogue
|
||||
* @method enhanceDialogue
|
||||
*/
|
||||
Competencydialogue.prototype.enhanceDialogue = function(dialogue) {
|
||||
//Apply watch on the related competencies and competencies in the dialogue.
|
||||
var comprelated = new Competencydialogue({includerelated : false});
|
||||
comprelated.watch(dialogue.getContent());
|
||||
};
|
||||
|
||||
/**
|
||||
* Display a dialogue box by competencyid.
|
||||
*
|
||||
* @param {Number} the competency id
|
||||
* @param {Object} Options for tool_lp_data_for_competency_summary service
|
||||
* @param {Object} dataSource data to be used to display dialogue box
|
||||
* @method showDialogue
|
||||
*/
|
||||
Competencydialogue.prototype.showDialogue = function(competencyid) {
|
||||
|
||||
var datapromise = this.getCompetencyDataPromise(competencyid);
|
||||
var localthis = this;
|
||||
datapromise.done(function(data) {
|
||||
// Inner Html in the dialogue content.
|
||||
templates.render('tool_lp/competency_summary', data)
|
||||
.done(function(html) {
|
||||
// Log competency viewed event.
|
||||
localthis.triggerCompetencyViewedEvent(competencyid);
|
||||
|
||||
// Show the dialogue.
|
||||
new Dialogue(
|
||||
data.competency.shortname,
|
||||
html,
|
||||
localthis.enhanceDialogue
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Display a dialogue box from data.
|
||||
*
|
||||
* @param {Object} dataSource data to be used to display dialogue box
|
||||
* @method showDialogueFromData
|
||||
*/
|
||||
Competencydialogue.prototype.showDialogueFromData = function(dataSource) {
|
||||
|
||||
var localthis = this;
|
||||
// Inner Html in the dialogue content.
|
||||
templates.render('tool_lp/competency_summary', dataSource)
|
||||
.done(function(html) {
|
||||
// Log competency viewed event.
|
||||
localthis.triggerCompetencyViewedEvent(dataSource.id);
|
||||
|
||||
// Show the dialogue.
|
||||
new Dialogue(
|
||||
dataSource.shortname,
|
||||
html,
|
||||
localthis.enhanceDialogue
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* The action on the click event.
|
||||
*
|
||||
* @param {Event} event click
|
||||
* @method clickEventHandler
|
||||
*/
|
||||
Competencydialogue.prototype.clickEventHandler = function(e) {
|
||||
|
||||
var compdialogue = e.data.compdialogue;
|
||||
var competencyid = $(e.currentTarget).data('id');
|
||||
|
||||
// Show the dialogue box.
|
||||
compdialogue.showDialogue(competencyid);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a promise on data competency.
|
||||
*
|
||||
* @param {Number} competencyid
|
||||
* @return {Promise} return promise on data request
|
||||
* @method getCompetencyDataPromise
|
||||
*/
|
||||
Competencydialogue.prototype.getCompetencyDataPromise = function(competencyid) {
|
||||
|
||||
var requests = ajax.call([
|
||||
{ methodname: 'tool_lp_data_for_competency_summary',
|
||||
args: { competencyid: competencyid,
|
||||
includerelated: this.options.includerelated,
|
||||
includecourses: this.options.includecourses
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
return requests[0].then(function(context) {
|
||||
return context;
|
||||
}).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Watch the competencies links in container.
|
||||
*
|
||||
* @param {String} container selector of node containing competencies links
|
||||
* @method watch
|
||||
*/
|
||||
Competencydialogue.prototype.watch = function(containerSelector) {
|
||||
$(containerSelector).off('click', '[data-action="competency-dialogue"]', this.clickEventHandler);
|
||||
$(containerSelector).on('click', '[data-action="competency-dialogue"]', { compdialogue: this }, this.clickEventHandler);
|
||||
};
|
||||
|
||||
return Competencydialogue;
|
||||
});
|
||||
@@ -0,0 +1,460 @@
|
||||
// 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 picker.
|
||||
*
|
||||
* To handle 'save' events use: picker.on('save')
|
||||
* This will receive a object with either a single 'competencyId', or an array in 'competencyIds'
|
||||
* depending on the value of multiSelect.
|
||||
*
|
||||
* @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/notification',
|
||||
'core/ajax',
|
||||
'core/templates',
|
||||
'tool_lp/dialogue',
|
||||
'core/str',
|
||||
'tool_lp/tree'],
|
||||
function($, Notification, Ajax, Templates, Dialogue, Str, Tree) {
|
||||
|
||||
/**
|
||||
* Competency picker class.
|
||||
* @param {Number} pageContextId The page context ID.
|
||||
* @param {Number|false} singleFramework The ID of the framework when limited to one.
|
||||
* @param {String} pageContextIncludes One of 'children', 'parents', 'self'.
|
||||
* @param {Boolean} multiSelect Support multi-select in the tree.
|
||||
*/
|
||||
var Picker = function(pageContextId, singleFramework, pageContextIncludes, multiSelect) {
|
||||
var self = this;
|
||||
self._eventNode = $('<div></div>');
|
||||
self._frameworks = [];
|
||||
self._reset();
|
||||
|
||||
self._pageContextId = pageContextId;
|
||||
self._pageContextIncludes = pageContextIncludes || 'children';
|
||||
self._multiSelect = (typeof multiSelect === 'undefined' || multiSelect === true);
|
||||
if (singleFramework) {
|
||||
self._frameworkId = singleFramework;
|
||||
self._singleFramework = true;
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {Array} The competencies fetched. */
|
||||
Picker.prototype._competencies = null;
|
||||
/** @type {Array} The competencies that cannot be picked. */
|
||||
Picker.prototype._disallowedCompetencyIDs = null;
|
||||
/** @type {Node} The node we attach the events to. */
|
||||
Picker.prototype._eventNode = null;
|
||||
/** @type {Array} The list of frameworks fetched. */
|
||||
Picker.prototype._frameworks = null;
|
||||
/** @type {Number} The current framework ID. */
|
||||
Picker.prototype._frameworkId = null;
|
||||
/** @type {Number} The page context ID. */
|
||||
Picker.prototype._pageContextId = null;
|
||||
/** @type {Number} Relevant contexts inclusion. */
|
||||
Picker.prototype._pageContextIncludes = null;
|
||||
/** @type {Dialogue} The reference to the dialogue. */
|
||||
Picker.prototype._popup = null;
|
||||
/** @type {String} The string we filter the competencies with. */
|
||||
Picker.prototype._searchText = '';
|
||||
/** @type {Object} The competency that was selected. */
|
||||
Picker.prototype._selectedCompetencies = null;
|
||||
/** @type {Boolean} Whether we can browse frameworks or not. */
|
||||
Picker.prototype._singleFramework = false;
|
||||
/** @type {Boolean} Do we allow multi select? */
|
||||
Picker.prototype._multiSelect = true;
|
||||
/** @type {Boolean} Do we allow to display hidden framework? */
|
||||
Picker.prototype._onlyVisible = true;
|
||||
|
||||
/**
|
||||
* Hook to executed after the view is rendered.
|
||||
*
|
||||
* @method _afterRender
|
||||
*/
|
||||
Picker.prototype._afterRender = function() {
|
||||
var self = this;
|
||||
|
||||
// Initialise the tree.
|
||||
var tree = new Tree(self._find('[data-enhance=linktree]'), self._multiSelect);
|
||||
|
||||
// To prevent jiggling we only show the tree after it is enhanced.
|
||||
self._find('[data-enhance=linktree]').show();
|
||||
|
||||
tree.on('selectionchanged', function(evt, params) {
|
||||
var selected = params.selected;
|
||||
evt.preventDefault();
|
||||
var validIds = [];
|
||||
$.each(selected, function(index, item) {
|
||||
var compId = $(item).data('id'),
|
||||
valid = true;
|
||||
|
||||
if (typeof compId === 'undefined') {
|
||||
// Do not allow picking nodes with no id.
|
||||
valid = false;
|
||||
} else {
|
||||
$.each(self._disallowedCompetencyIDs, function(i, id) {
|
||||
if (id == compId) {
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (valid) {
|
||||
validIds.push(compId);
|
||||
}
|
||||
}.bind(self));
|
||||
|
||||
self._selectedCompetencies = validIds;
|
||||
|
||||
// TODO Implement disabling of nodes in the tree module somehow.
|
||||
if (!self._selectedCompetencies.length) {
|
||||
self._find('[data-region="competencylinktree"] [data-action="add"]').attr('disabled', 'disabled');
|
||||
} else {
|
||||
self._find('[data-region="competencylinktree"] [data-action="add"]').removeAttr('disabled');
|
||||
}
|
||||
}.bind(self));
|
||||
|
||||
// Add listener for framework change.
|
||||
if (!self._singleFramework) {
|
||||
self._find('[data-action="chooseframework"]').change(function(e) {
|
||||
self._frameworkId = $(e.target).val();
|
||||
self._loadCompetencies().then(self._refresh.bind(self));
|
||||
}.bind(self));
|
||||
}
|
||||
|
||||
// Add listener for search.
|
||||
self._find('[data-region="filtercompetencies"] button').click(function(e) {
|
||||
e.preventDefault();
|
||||
$(e.target).attr('disabled', 'disabled');
|
||||
self._searchText = self._find('[data-region="filtercompetencies"] input').val() || '';
|
||||
return self._refresh().always(function() {
|
||||
$(e.target).removeAttr('disabled');
|
||||
});
|
||||
}.bind(self));
|
||||
|
||||
// Add listener for cancel.
|
||||
self._find('[data-region="competencylinktree"] [data-action="cancel"]').click(function(e) {
|
||||
e.preventDefault();
|
||||
self.close();
|
||||
}.bind(self));
|
||||
|
||||
// Add listener for add.
|
||||
self._find('[data-region="competencylinktree"] [data-action="add"]').click(function(e) {
|
||||
e.preventDefault();
|
||||
if (!self._selectedCompetencies.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self._multiSelect) {
|
||||
self._trigger('save', { competencyIds: self._selectedCompetencies });
|
||||
} else {
|
||||
// We checked above that the array has at least one value.
|
||||
self._trigger('save', { competencyId: self._selectedCompetencies[0] });
|
||||
}
|
||||
|
||||
self.close();
|
||||
}.bind(self));
|
||||
|
||||
// The list of selected competencies will be modified while looping (because of the listeners above).
|
||||
var currentItems = self._selectedCompetencies.slice(0);
|
||||
|
||||
$.each(currentItems, function(index, id) {
|
||||
var node = self._find('[data-id=' + id + ']');
|
||||
if (node.length) {
|
||||
tree.toggleItem(node);
|
||||
tree.updateFocus(node);
|
||||
}
|
||||
}.bind(self));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the dialogue.
|
||||
*
|
||||
* @method close
|
||||
*/
|
||||
Picker.prototype.close = function() {
|
||||
var self = this;
|
||||
self._popup.close();
|
||||
self._reset();
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens the picker.
|
||||
*
|
||||
* @method display
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype.display = function() {
|
||||
var self = this;
|
||||
return self._render().then(function(html) {
|
||||
return Str.get_string('competencypicker', 'tool_lp').then(function(title) {
|
||||
self._popup = new Dialogue(
|
||||
title,
|
||||
html,
|
||||
self._afterRender.bind(self)
|
||||
);
|
||||
}.bind(self));
|
||||
}.bind(self)).fail(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the competencies.
|
||||
*
|
||||
* @param {Number} frameworkId The frameworkId.
|
||||
* @param {String} searchText Limit the competencies to those matching the text.
|
||||
* @method _fetchCompetencies
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype._fetchCompetencies = function(frameworkId, searchText) {
|
||||
var self = this;
|
||||
|
||||
return Ajax.call([
|
||||
{ methodname: 'core_competency_search_competencies', args: {
|
||||
searchtext: searchText,
|
||||
competencyframeworkid: frameworkId
|
||||
}}
|
||||
])[0].done(function(competencies) {
|
||||
|
||||
function addCompetencyChildren(parent, competencies) {
|
||||
for (var 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expand the list of competencies into a tree.
|
||||
var i, tree = [], comp;
|
||||
for (i = 0; i < competencies.length; i++) {
|
||||
comp = competencies[i];
|
||||
if (comp.parentid == "0") { // Loose check for now, because WS returns a string.
|
||||
comp.children = [];
|
||||
comp.haschildren = 0;
|
||||
tree[tree.length] = comp;
|
||||
addCompetencyChildren(comp, competencies);
|
||||
}
|
||||
}
|
||||
|
||||
self._competencies = tree;
|
||||
|
||||
}.bind(self)).fail(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find a node in the dialogue.
|
||||
*
|
||||
* @param {String} selector
|
||||
* @method _find
|
||||
*/
|
||||
Picker.prototype._find = function(selector) {
|
||||
return $(this._popup.getContent()).find(selector);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience method to get a framework object.
|
||||
*
|
||||
* @param {Number} fid The framework ID.
|
||||
* @method _getFramework
|
||||
*/
|
||||
Picker.prototype._getFramework = function(fid) {
|
||||
var frm;
|
||||
$.each(this._frameworks, function(i, f) {
|
||||
if (f.id == fid) {
|
||||
frm = f;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return frm;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the competencies.
|
||||
*
|
||||
* @method _loadCompetencies
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype._loadCompetencies = function() {
|
||||
return this._fetchCompetencies(this._frameworkId, this._searchText);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the frameworks.
|
||||
*
|
||||
* @method _loadFrameworks
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype._loadFrameworks = function() {
|
||||
var promise,
|
||||
self = this;
|
||||
|
||||
// Quit early because we already have the data.
|
||||
if (self._frameworks.length > 0) {
|
||||
return $.when();
|
||||
}
|
||||
|
||||
if (self._singleFramework) {
|
||||
promise = Ajax.call([
|
||||
{ methodname: 'core_competency_read_competency_framework', args: {
|
||||
id: this._frameworkId
|
||||
}}
|
||||
])[0].then(function(framework) {
|
||||
return [framework];
|
||||
});
|
||||
} else {
|
||||
promise = Ajax.call([
|
||||
{ methodname: 'core_competency_list_competency_frameworks', args: {
|
||||
sort: 'shortname',
|
||||
context: { contextid: self._pageContextId },
|
||||
includes: self._pageContextIncludes,
|
||||
onlyvisible: self._onlyVisible
|
||||
}}
|
||||
])[0];
|
||||
}
|
||||
|
||||
return promise.done(function(frameworks) {
|
||||
self._frameworks = frameworks;
|
||||
}).fail(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register an event listener.
|
||||
*
|
||||
* @param {String} type The event type.
|
||||
* @param {Function} handler The event listener.
|
||||
* @method on
|
||||
*/
|
||||
Picker.prototype.on = function(type, handler) {
|
||||
this._eventNode.on(type, handler);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to executed before render.
|
||||
*
|
||||
* @method _preRender
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype._preRender = function() {
|
||||
var self = this;
|
||||
return self._loadFrameworks().then(function() {
|
||||
if (!self._frameworkId && self._frameworks.length > 0) {
|
||||
self._frameworkId = self._frameworks[0].id;
|
||||
}
|
||||
|
||||
// We could not set a framework ID, that probably means there are no frameworks accessible.
|
||||
if (!self._frameworkId) {
|
||||
self._frameworks = [];
|
||||
return $.when();
|
||||
}
|
||||
|
||||
return self._loadCompetencies();
|
||||
}.bind(self));
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh the view.
|
||||
*
|
||||
* @method _refresh
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype._refresh = function() {
|
||||
var self = this;
|
||||
return self._render().then(function(html) {
|
||||
self._find('[data-region="competencylinktree"]').replaceWith(html);
|
||||
self._afterRender();
|
||||
}.bind(self));
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the dialogue.
|
||||
*
|
||||
* @method _render
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype._render = function() {
|
||||
var self = this;
|
||||
return self._preRender().then(function() {
|
||||
|
||||
if (!self._singleFramework) {
|
||||
$.each(self._frameworks, function(i, framework) {
|
||||
if (framework.id == self._frameworkId) {
|
||||
framework.selected = true;
|
||||
} else {
|
||||
framework.selected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var context = {
|
||||
competencies: self._competencies,
|
||||
framework: self._getFramework(self._frameworkId),
|
||||
frameworks: self._frameworks,
|
||||
search: self._searchText,
|
||||
singleFramework: self._singleFramework,
|
||||
};
|
||||
|
||||
return Templates.render('tool_lp/competency_picker', context);
|
||||
}.bind(self));
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset the dialogue properties.
|
||||
*
|
||||
* This does not reset everything, just enough to reset the UI.
|
||||
*
|
||||
* @method _reset
|
||||
*/
|
||||
Picker.prototype._reset = function() {
|
||||
this._competencies = [];
|
||||
this._disallowedCompetencyIDs = [];
|
||||
this._popup = null;
|
||||
this._searchText = '';
|
||||
this._selectedCompetencies = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set what competencies cannot be picked.
|
||||
*
|
||||
* This needs to be set after reset/close.
|
||||
*
|
||||
* @params {Number[]} The IDs.
|
||||
* @method _setDisallowedCompetencyIDs
|
||||
*/
|
||||
Picker.prototype.setDisallowedCompetencyIDs = function(ids) {
|
||||
this._disallowedCompetencyIDs = ids;
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger an event.
|
||||
*
|
||||
* @param {String} type The type of event.
|
||||
* @param {Object} The data to pass to the listeners.
|
||||
* @method _reset
|
||||
*/
|
||||
Picker.prototype._trigger = function(type, data) {
|
||||
this._eventNode.trigger(type, [data]);
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/competencypicker */ Picker;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,240 @@
|
||||
// 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 picker from user plans.
|
||||
*
|
||||
* To handle 'save' events use: picker.on('save').
|
||||
*
|
||||
* This will receive a object with either a single 'competencyId', or an array in 'competencyIds'
|
||||
* depending on the value of multiSelect.
|
||||
*
|
||||
* @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/notification',
|
||||
'core/ajax',
|
||||
'core/templates',
|
||||
'core/str',
|
||||
'tool_lp/tree',
|
||||
'tool_lp/competencypicker'
|
||||
],
|
||||
function($, Notification, Ajax, Templates, Str, Tree, PickerBase) {
|
||||
|
||||
/**
|
||||
* Competency picker in plan class.
|
||||
*
|
||||
* @param {Number|false} singlePlan The ID of the plan when limited to one.
|
||||
* @param {String} pageContextIncludes One of 'children', 'parents', 'self'.
|
||||
* @param {Boolean} multiSelect Support multi-select in the tree.
|
||||
*/
|
||||
var Picker = function(userId, singlePlan, multiSelect) {
|
||||
PickerBase.prototype.constructor.apply(this, [1, false, 'self', multiSelect]);
|
||||
this._userId = userId;
|
||||
this._plans = [];
|
||||
|
||||
if (singlePlan) {
|
||||
this._planId = singlePlan;
|
||||
this._singlePlan = true;
|
||||
}
|
||||
};
|
||||
Picker.prototype = Object.create(PickerBase.prototype);
|
||||
|
||||
/** @type {Array} The list of plans fetched. */
|
||||
Picker.prototype._plans = null;
|
||||
/** @type {Number} The current plan ID. */
|
||||
Picker.prototype._planId = null;
|
||||
/** @type {Boolean} Whether we can browse plans or not. */
|
||||
Picker.prototype._singlePlan = false;
|
||||
/** @type {Number} The user the plans belongs to. */
|
||||
Picker.prototype._userId = null;
|
||||
|
||||
/**
|
||||
* Hook to executed after the view is rendered.
|
||||
*
|
||||
* @method _afterRender
|
||||
*/
|
||||
Picker.prototype._afterRender = function() {
|
||||
var self = this;
|
||||
PickerBase.prototype._afterRender.apply(self, arguments);
|
||||
|
||||
// Add listener for framework change.
|
||||
if (!self._singlePlan) {
|
||||
self._find('[data-action="chooseplan"]').change(function(e) {
|
||||
self._planId = $(e.target).val();
|
||||
self._loadCompetencies().then(self._refresh.bind(self));
|
||||
}.bind(self));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the competencies.
|
||||
*
|
||||
* @param {Number} planId The planId.
|
||||
* @param {String} searchText Limit the competencies to those matching the text.
|
||||
* @method _fetchCompetencies
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype._fetchCompetencies = function(planId, searchText) {
|
||||
var self = this;
|
||||
|
||||
return Ajax.call([
|
||||
{ methodname: 'core_competency_list_plan_competencies', args: {
|
||||
id: planId
|
||||
}}
|
||||
])[0].done(function(competencies) {
|
||||
|
||||
// Expand the list of competencies into a fake tree.
|
||||
var i, tree = [], comp;
|
||||
for (i = 0; i < competencies.length; i++) {
|
||||
comp = competencies[i].competency;
|
||||
if (comp.shortname.toLowerCase().indexOf(searchText.toLowerCase()) < 0) {
|
||||
continue;
|
||||
}
|
||||
comp.children = [];
|
||||
comp.haschildren = 0;
|
||||
tree.push(comp);
|
||||
}
|
||||
|
||||
self._competencies = tree;
|
||||
|
||||
}).fail(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience method to get a plan object.
|
||||
*
|
||||
* @param {Number} id The plan ID.
|
||||
* @return {Object|undefined} The plan.
|
||||
* @method _getPlan
|
||||
*/
|
||||
Picker.prototype._getPlan = function(id) {
|
||||
var plan;
|
||||
$.each(this._plans, function(i, f) {
|
||||
if (f.id == id) {
|
||||
plan = f;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return plan;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the competencies.
|
||||
*
|
||||
* @method _loadCompetencies
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype._loadCompetencies = function() {
|
||||
return this._fetchCompetencies(this._planId, this._searchText);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the plans.
|
||||
*
|
||||
* @method _loadPlans
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype._loadPlans = function() {
|
||||
var promise,
|
||||
self = this;
|
||||
|
||||
// Quit early because we already have the data.
|
||||
if (self._plans.length > 0) {
|
||||
return $.when();
|
||||
}
|
||||
|
||||
if (self._singlePlan) {
|
||||
promise = Ajax.call([
|
||||
{ methodname: 'core_competency_read_plan', args: {
|
||||
id: this._planId
|
||||
}}
|
||||
])[0].then(function(plan) {
|
||||
return [plan];
|
||||
});
|
||||
} else {
|
||||
promise = Ajax.call([
|
||||
{ methodname: 'core_competency_list_user_plans', args: {
|
||||
userid: self._userId
|
||||
}}
|
||||
])[0];
|
||||
}
|
||||
|
||||
return promise.done(function(plans) {
|
||||
self._plans = plans;
|
||||
}).fail(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to executed before render.
|
||||
*
|
||||
* @method _preRender
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype._preRender = function() {
|
||||
var self = this;
|
||||
return self._loadPlans().then(function() {
|
||||
if (!self._planId && self._plans.length > 0) {
|
||||
self._planId = self._plans[0].id;
|
||||
}
|
||||
|
||||
// We could not set a framework ID, that probably means there are no frameworks accessible.
|
||||
if (!self._planId) {
|
||||
self._plans = [];
|
||||
return $.when();
|
||||
}
|
||||
|
||||
return self._loadCompetencies();
|
||||
}.bind(self));
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the dialogue.
|
||||
*
|
||||
* @method _render
|
||||
* @return {Promise}
|
||||
*/
|
||||
Picker.prototype._render = function() {
|
||||
var self = this;
|
||||
return self._preRender().then(function() {
|
||||
|
||||
if (!self._singlePlan) {
|
||||
$.each(self._plans, function(i, plan) {
|
||||
if (plan.id == self._planId) {
|
||||
plan.selected = true;
|
||||
} else {
|
||||
plan.selected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var context = {
|
||||
competencies: self._competencies,
|
||||
plan: self._getPlan(self._planId),
|
||||
plans: self._plans,
|
||||
search: self._searchText,
|
||||
singlePlan: self._singlePlan,
|
||||
};
|
||||
|
||||
return Templates.render('tool_lp/competency_picker_user_plans', context);
|
||||
}.bind(self));
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/competencypicker_user_plans */ Picker;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,548 @@
|
||||
// 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/notification',
|
||||
'core/templates',
|
||||
'tool_lp/dialogue',
|
||||
'tool_lp/competency_outcomes',
|
||||
'core/str'],
|
||||
function($, Notification, Templates, Dialogue, Outcomes, Str) {
|
||||
|
||||
/**
|
||||
* Competency rule class.
|
||||
*
|
||||
* When implementing this you should attach a listener to the event 'save'
|
||||
* on the instance. E.g.
|
||||
*
|
||||
* var config = new RuleConfig(tree, modules);
|
||||
* config.on('save', function(e, config) { ... });
|
||||
*
|
||||
* @param {competencytree} tree The competency tree.
|
||||
* @param {Array} rulesModules The modules containing the rules: [{ typeName: { amd: amdModule, name: ruleName }}].
|
||||
*/
|
||||
var RuleConfig = function(tree, rulesModules) {
|
||||
this._eventNode = $('<div></div>');
|
||||
this._tree = tree;
|
||||
this._rulesModules = rulesModules;
|
||||
this._setUp();
|
||||
};
|
||||
|
||||
/** @type {Object} The current competency. */
|
||||
RuleConfig.prototype._competency = null;
|
||||
/** @type {Node} The node we attach the events to. */
|
||||
RuleConfig.prototype._eventNode = null;
|
||||
/** @type {Array} Outcomes options. */
|
||||
RuleConfig.prototype._outcomesOption = null;
|
||||
/** @type {Dialogue} The dialogue. */
|
||||
RuleConfig.prototype._popup = null;
|
||||
/** @type {Promise} Resolved when the module is ready. */
|
||||
RuleConfig.prototype._ready = null;
|
||||
/** @type {Array} The rules. */
|
||||
RuleConfig.prototype._rules = null;
|
||||
/** @type {Array} The rules modules. */
|
||||
RuleConfig.prototype._rulesModules = null;
|
||||
/** @type {competencytree} The competency tree. */
|
||||
RuleConfig.prototype._tree = null;
|
||||
|
||||
/**
|
||||
* After change.
|
||||
*
|
||||
* Triggered when a change occured.
|
||||
*
|
||||
* @return {Void}
|
||||
* @method _afterChange
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._afterChange = function() {
|
||||
if (!this._isValid()) {
|
||||
this._find('[data-action="save"]').prop('disabled', true);
|
||||
} else {
|
||||
this._find('[data-action="save"]').prop('disabled', false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* After change in rule's config.
|
||||
*
|
||||
* Triggered when a change occured in a specific rule config.
|
||||
*
|
||||
* @return {Void}
|
||||
* @method _afterRuleConfigChange
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._afterRuleConfigChange = function(e, rule) {
|
||||
if (rule != this._getRule()) {
|
||||
// This rule is not the current one any more, we can ignore.
|
||||
return;
|
||||
}
|
||||
this._afterChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* After render hook.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @method _afterRender
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._afterRender = function() {
|
||||
var self = this;
|
||||
|
||||
self._find('[name="outcome"]').on('change', function() {
|
||||
self._switchedOutcome();
|
||||
}).trigger('change');
|
||||
|
||||
self._find('[name="rule"]').on('change', function() {
|
||||
self._switchedRule();
|
||||
}).trigger('change');
|
||||
|
||||
self._find('[data-action="save"]').on('click', function() {
|
||||
self._trigger('save', self._getConfig());
|
||||
self.close();
|
||||
});
|
||||
|
||||
self._find('[data-action="cancel"]').on('click', function() {
|
||||
self.close();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the current competency can be configured.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method canBeConfigured
|
||||
*/
|
||||
RuleConfig.prototype.canBeConfigured = function() {
|
||||
var can = false;
|
||||
$.each(this._rules, function(index, rule) {
|
||||
if (rule.canConfig()) {
|
||||
can = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return can;
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the dialogue.
|
||||
*
|
||||
* @method close
|
||||
*/
|
||||
RuleConfig.prototype.close = function() {
|
||||
this._popup.close();
|
||||
this._popup = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens the picker.
|
||||
*
|
||||
* @param {Number} competencyId The competency ID of the competency to work on.
|
||||
* @method display
|
||||
* @return {Promise}
|
||||
*/
|
||||
RuleConfig.prototype.display = function() {
|
||||
var self = this;
|
||||
if (!self._competency) {
|
||||
return;
|
||||
}
|
||||
return self._render().then(function(html) {
|
||||
return Str.get_string('competencyrule', 'tool_lp').then(function(title) {
|
||||
self._popup = new Dialogue(
|
||||
title,
|
||||
html,
|
||||
self._afterRender.bind(self)
|
||||
);
|
||||
});
|
||||
}).fail(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find a node in the dialogue.
|
||||
*
|
||||
* @param {String} selector
|
||||
* @method _find
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._find = function(selector) {
|
||||
return $(this._popup.getContent()).find(selector);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the applicable outcome options.
|
||||
*
|
||||
* @return {Array}
|
||||
* @method _getApplicableOutcomesOptions
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getApplicableOutcomesOptions = function() {
|
||||
var self = this,
|
||||
options = [];
|
||||
|
||||
$.each(self._outcomesOption, function(index, outcome) {
|
||||
options.push({
|
||||
code: outcome.code,
|
||||
name: outcome.name,
|
||||
selected: (outcome.code == self._competency.ruleoutcome) ? true : false,
|
||||
});
|
||||
});
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the applicable rules options.
|
||||
*
|
||||
* @return {Array}
|
||||
* @method _getApplicableRulesOptions
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getApplicableRulesOptions = function() {
|
||||
var self = this,
|
||||
options = [];
|
||||
|
||||
$.each(self._rules, function(index, rule) {
|
||||
if (!rule.canConfig()) {
|
||||
return;
|
||||
}
|
||||
options.push({
|
||||
name: self._getRuleName(rule.getType()),
|
||||
type: rule.getType(),
|
||||
selected: (rule.getType() == self._competency.ruletype) ? true : false,
|
||||
});
|
||||
});
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the full config for the competency.
|
||||
*
|
||||
* @return {Object} Contains rule, ruleoutcome and ruleconfig.
|
||||
* @method _getConfig
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getConfig = function() {
|
||||
var rule = this._getRule();
|
||||
return {
|
||||
ruletype: rule ? rule.getType() : null,
|
||||
ruleconfig: rule ? rule.getConfig() : null,
|
||||
ruleoutcome: this._getOutcome()
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the selected outcome code.
|
||||
*
|
||||
* @return {String}
|
||||
* @method _getOutcome
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getOutcome = function() {
|
||||
return this._find('[name="outcome"]').val();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the selected rule.
|
||||
*
|
||||
* @return {null|Rule}
|
||||
* @method _getRule
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getRule = function() {
|
||||
var result,
|
||||
type = this._find('[name="rule"]').val();
|
||||
|
||||
$.each(this._rules, function(index, rule) {
|
||||
if (rule.getType() == type) {
|
||||
result = rule;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the name of a rule.
|
||||
*
|
||||
* @param {String} type The type of a rule.
|
||||
* @return {String}
|
||||
* @method _getRuleName
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getRuleName = function(type) {
|
||||
var self = this,
|
||||
name;
|
||||
$.each(self._rulesModules, function(index, modInfo) {
|
||||
if (modInfo.type == type) {
|
||||
name = modInfo.name;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the outcomes.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @method _initOutcomes
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._initOutcomes = function() {
|
||||
var self = this;
|
||||
|
||||
return Outcomes.getAll().then(function(outcomes) {
|
||||
self._outcomesOption = outcomes;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the rules.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @method _initRules
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._initRules = function() {
|
||||
var self = this,
|
||||
promises = [];
|
||||
|
||||
$.each(self._rules, function(index, rule) {
|
||||
var promise = rule.init().then(function() {
|
||||
rule.setTargetCompetency(self._competency);
|
||||
rule.on('change', self._afterRuleConfigChange.bind(self));
|
||||
}, function() {
|
||||
// Upon failure remove the rule, and resolve the promise.
|
||||
self._rules.splice(index, 1);
|
||||
return $.when();
|
||||
});
|
||||
promises.push(promise);
|
||||
});
|
||||
|
||||
return $.when.apply($.when, promises);
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether or not the current config is valid.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method _isValid
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._isValid = function() {
|
||||
var outcome = this._getOutcome(),
|
||||
rule = this._getRule();
|
||||
|
||||
if (outcome == Outcomes.NONE) {
|
||||
return true;
|
||||
} else if (!rule) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return rule.isValid();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register an event listener.
|
||||
*
|
||||
* @param {String} type The event type.
|
||||
* @param {Function} handler The event listener.
|
||||
* @method on
|
||||
*/
|
||||
RuleConfig.prototype.on = function(type, handler) {
|
||||
this._eventNode.on(type, handler);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to executed before render.
|
||||
*
|
||||
* @method _preRender
|
||||
* @protected
|
||||
* @return {Promise}
|
||||
*/
|
||||
RuleConfig.prototype._preRender = function() {
|
||||
// We need to have all the information about the rule plugins first.
|
||||
return this.ready();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a promise that is resolved when the module is ready.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @method ready
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype.ready = function() {
|
||||
return this._ready.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the dialogue.
|
||||
*
|
||||
* @method _render
|
||||
* @protected
|
||||
* @return {Promise}
|
||||
*/
|
||||
RuleConfig.prototype._render = function() {
|
||||
var self = this;
|
||||
return this._preRender().then(function() {
|
||||
var config;
|
||||
|
||||
if (!self.canBeConfigured()) {
|
||||
config = false;
|
||||
} else {
|
||||
config = {};
|
||||
config.outcomes = self._getApplicableOutcomesOptions();
|
||||
config.rules = self._getApplicableRulesOptions();
|
||||
}
|
||||
|
||||
var context = {
|
||||
competencyshortname: self._competency.shortname,
|
||||
config: config
|
||||
};
|
||||
|
||||
return Templates.render('tool_lp/competency_rule_config', context);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the target competency.
|
||||
*
|
||||
* @param {Number} competencyId The target competency Id.
|
||||
* @method setTargetCompetencyId
|
||||
*/
|
||||
RuleConfig.prototype.setTargetCompetencyId = function(competencyId) {
|
||||
var self = this;
|
||||
self._competency = self._tree.getCompetency(competencyId);
|
||||
$.each(self._rules, function(index, rule) {
|
||||
rule.setTargetCompetency(self._competency);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Set up the instance.
|
||||
*
|
||||
* @return {Void}
|
||||
* @method _setUp
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._setUp = function() {
|
||||
var self = this,
|
||||
promises = [],
|
||||
modules = [];
|
||||
|
||||
self._ready = $.Deferred();
|
||||
self._rules = [];
|
||||
|
||||
$.each(self._rulesModules, function(index, rule) {
|
||||
modules.push(rule.amd);
|
||||
});
|
||||
|
||||
// Load all the modules.
|
||||
require(modules, function() {
|
||||
$.each(arguments, function(index, Module) {
|
||||
// Instantiate the rule and listen to it.
|
||||
var rule = new Module(self._tree);
|
||||
self._rules.push(rule);
|
||||
});
|
||||
|
||||
// Load all the option values.
|
||||
promises.push(self._initRules());
|
||||
promises.push(self._initOutcomes());
|
||||
|
||||
// Ready when everything is done.
|
||||
$.when.apply($.when, promises).always(function() {
|
||||
self._ready.resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the user switches outcome.
|
||||
*
|
||||
* @return {Void}
|
||||
* @method _switchedOutcome
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._switchedOutcome = function() {
|
||||
var self = this,
|
||||
type = self._getOutcome();
|
||||
|
||||
if (type == Outcomes.NONE) {
|
||||
// Reset to defaults.
|
||||
self._find('[data-region="rule-type"]').hide()
|
||||
.find('[name="rule"]').val(-1);
|
||||
self._find('[data-region="rule-config"]').empty().hide();
|
||||
self._afterChange();
|
||||
return;
|
||||
}
|
||||
|
||||
self._find('[data-region="rule-type"]').show();
|
||||
self._find('[data-region="rule-config"]').show();
|
||||
self._afterChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the user switches rule.
|
||||
*
|
||||
* @return {Void}
|
||||
* @method _switchedRule
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._switchedRule = function() {
|
||||
var self = this,
|
||||
container = self._find('[data-region="rule-config"]'),
|
||||
rule = self._getRule();
|
||||
|
||||
if (!rule) {
|
||||
container.empty().hide();
|
||||
self._afterChange();
|
||||
return;
|
||||
}
|
||||
|
||||
rule.injectTemplate(container).then(function() {
|
||||
container.show();
|
||||
}, function() {
|
||||
container.empty().hide();
|
||||
}).always(function() {
|
||||
self._afterChange();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger an event.
|
||||
*
|
||||
* @param {String} type The type of event.
|
||||
* @param {Object} The data to pass to the listeners.
|
||||
* @method _trigger
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._trigger = function(type, data) {
|
||||
this._eventNode.trigger(type, [data]);
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/competencyruleconfig */ RuleConfig;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,262 @@
|
||||
// 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 on the competency tree.
|
||||
*
|
||||
* @module tool_lp/competencyselect
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['core/ajax', 'core/notification', 'core/templates', 'tool_lp/tree', 'tool_lp/competency_outcomes', 'jquery'],
|
||||
function(ajax, notification, templates, Ariatree, CompOutcomes, $) {
|
||||
|
||||
// Private variables and functions.
|
||||
/** @var {Object[]} competencies - Cached list of competencies */
|
||||
var competencies = {};
|
||||
|
||||
/** @var {Number} competencyFrameworkId - The current framework id */
|
||||
var competencyFrameworkId = 0;
|
||||
|
||||
/** @var {String} competencyFrameworkShortName - The current framework short name */
|
||||
var competencyFrameworkShortName = '';
|
||||
|
||||
/** @var {String} treeSelector - The selector for the root of the tree. */
|
||||
var treeSelector = '';
|
||||
|
||||
/** @var {String} currentNodeId - The data-id of the current node in the tree. */
|
||||
var currentNodeId = '';
|
||||
|
||||
/** @var {Boolean} competencyFramworkCanManage - Can manage the competencies framework */
|
||||
var competencyFramworkCanManage = false;
|
||||
|
||||
/**
|
||||
* Build a tree from the flat list of competencies.
|
||||
* @param {Object} parent The parent competency.
|
||||
* @param {Array} all The list of all competencies.
|
||||
*/
|
||||
var addChildren = function(parent, all) {
|
||||
var i = 0;
|
||||
var current = false;
|
||||
parent.haschildren = false;
|
||||
parent.children = [];
|
||||
for (i = 0; i < all.length; i++) {
|
||||
current = all[i];
|
||||
if (current.parentid == parent.id) {
|
||||
parent.haschildren = true;
|
||||
parent.children.push(current);
|
||||
addChildren(current, all);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the list of competencies via ajax. Competencies are filtered by the searchtext.
|
||||
* @param {String} searchtext The text to filter on.
|
||||
* @return {promise}
|
||||
*/
|
||||
var loadCompetencies = function(searchtext) {
|
||||
var deferred = $.Deferred();
|
||||
|
||||
templates.render('tool_lp/loading', {}).done(function(loadinghtml, loadingjs) {
|
||||
templates.replaceNodeContents($(treeSelector), loadinghtml, loadingjs);
|
||||
|
||||
var promises = ajax.call([{
|
||||
methodname: 'core_competency_search_competencies',
|
||||
args: {
|
||||
searchtext: searchtext,
|
||||
competencyframeworkid: competencyFrameworkId
|
||||
}
|
||||
}]);
|
||||
promises[0].done(function(result) {
|
||||
competencies = {};
|
||||
var i = 0;
|
||||
for (i = 0; i < result.length; i++) {
|
||||
competencies[result[i].id] = result[i];
|
||||
}
|
||||
|
||||
var children = [];
|
||||
var competency = false;
|
||||
for (i = 0; i < result.length; i++) {
|
||||
competency = result[i];
|
||||
if (parseInt(competency.parentid, 10) === 0) {
|
||||
children.push(competency);
|
||||
addChildren(competency, result);
|
||||
}
|
||||
}
|
||||
var context = {
|
||||
shortname: competencyFrameworkShortName,
|
||||
canmanage: competencyFramworkCanManage,
|
||||
competencies: children
|
||||
};
|
||||
templates.render('tool_lp/competencies_tree_root', context).done(function(html, js) {
|
||||
templates.replaceNodeContents($(treeSelector), $(html).html(), js);
|
||||
var tree = new Ariatree(treeSelector, false);
|
||||
|
||||
if (currentNodeId) {
|
||||
var node = $(treeSelector).find('[data-id=' + currentNodeId + ']');
|
||||
if (node.length) {
|
||||
tree.selectItem(node);
|
||||
tree.updateFocus(node);
|
||||
}
|
||||
}
|
||||
deferred.resolve(competencies);
|
||||
}).fail(deferred.reject);
|
||||
}).fail(deferred.reject);
|
||||
});
|
||||
|
||||
return deferred.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* Whenever the current item in the tree is changed - remember the "id".
|
||||
* @param {Event} evt
|
||||
* @param {Object} params The parameters for the event (This is the selected node).
|
||||
*/
|
||||
var rememberCurrent = function(evt, params) {
|
||||
var node = params.selected;
|
||||
currentNodeId = node.attr('data-id');
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/competencytree */ {
|
||||
// Public variables and functions.
|
||||
/**
|
||||
* Initialise the tree.
|
||||
*
|
||||
* @param {Number} id The competency id.
|
||||
* @param {String} shortname The framework shortname
|
||||
* @param {String} search The current search string
|
||||
* @param {String} selector The selector for the tree div
|
||||
* @param {Boolean} canmanage Can manage the competencies
|
||||
*/
|
||||
init: function(id, shortname, search, selector, canmanage) {
|
||||
competencyFrameworkId = id;
|
||||
competencyFrameworkShortName = shortname;
|
||||
competencyFramworkCanManage = canmanage;
|
||||
treeSelector = selector;
|
||||
loadCompetencies(search).fail(notification.exception);
|
||||
|
||||
this.on('selectionchanged', rememberCurrent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add an event handler for custom events emitted by the tree.
|
||||
*
|
||||
* @param {String} eventname The name of the event - only "selectionchanged" for now
|
||||
* @param {Function} handler The handler for the event.
|
||||
*/
|
||||
on: function(eventname, handler) {
|
||||
// We can't use the tree on function directly
|
||||
// because the tree gets rebuilt whenever the search string changes,
|
||||
// instead we attach the listner to the root node of the tree which never
|
||||
// gets destroyed (same as "on()" code in the tree.js).
|
||||
$(treeSelector).on(eventname, handler);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the children of a competency.
|
||||
*
|
||||
* @param {Number} id The competency ID.
|
||||
* @return {Array}
|
||||
* @method getChildren
|
||||
*/
|
||||
getChildren: function(id) {
|
||||
var children = [];
|
||||
$.each(competencies, function(index, competency) {
|
||||
if (competency.parentid == id) {
|
||||
children.push(competency);
|
||||
}
|
||||
});
|
||||
return children;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the competency framework id this model was initiliased with.
|
||||
*
|
||||
* @return {Number}
|
||||
*/
|
||||
getCompetencyFrameworkId: function() {
|
||||
return competencyFrameworkId;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a competency by id
|
||||
*
|
||||
* @param {Number} id The competency id
|
||||
* @return {Object}
|
||||
*/
|
||||
getCompetency: function(id) {
|
||||
return competencies[id];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the competency level.
|
||||
*
|
||||
* @param {Number} id The competency ID.
|
||||
* @return {Number}
|
||||
*/
|
||||
getCompetencyLevel: function(id) {
|
||||
var competency = this.getCompetency(id),
|
||||
level = competency.path.replace(/^\/|\/$/g, '').split('/').length;
|
||||
return level;
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether a competency has children.
|
||||
*
|
||||
* @param {Number} id The competency ID.
|
||||
* @return {Boolean}
|
||||
* @method hasChildren
|
||||
*/
|
||||
hasChildren: function(id) {
|
||||
return this.getChildren(id).length > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Does the competency have a rule?
|
||||
*
|
||||
* @param {Number} id The competency ID.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
hasRule: function(id) {
|
||||
var comp = this.getCompetency(id);
|
||||
if (comp) {
|
||||
return comp.ruleoutcome != CompOutcomes.OUTCOME_NONE
|
||||
&& comp.ruletype;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reload all the page competencies framework competencies.
|
||||
* @method reloadCompetencies
|
||||
* @return {Promise}
|
||||
*/
|
||||
reloadCompetencies: function() {
|
||||
return loadCompetencies('').fail(notification.exception);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all competencies for this framework.
|
||||
*
|
||||
* @return {Object[]}
|
||||
*/
|
||||
listCompetencies: function() {
|
||||
return competencies;
|
||||
},
|
||||
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,149 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Change the course competency settings in a popup.
|
||||
*
|
||||
* @module tool_lp/configurecoursecompetencysettings
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery',
|
||||
'core/notification',
|
||||
'tool_lp/dialogue',
|
||||
'core/str',
|
||||
'core/ajax',
|
||||
'core/templates'],
|
||||
function($, notification, Dialogue, str, ajax, templates) {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param {String} selector - selector for the links to open the dialogue.
|
||||
*/
|
||||
var settingsMod = function(selector) {
|
||||
$(selector).on('click', this.configureSettings.bind(this));
|
||||
};
|
||||
|
||||
/** @type {Dialogue} Reference to the dialogue that we opened. */
|
||||
settingsMod.prototype._dialogue = null;
|
||||
|
||||
/**
|
||||
* Open the configure settings dialogue.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @method configureSettings
|
||||
*/
|
||||
settingsMod.prototype.configureSettings = function(e) {
|
||||
var courseid = $(e.target).closest('a').data('courseid');
|
||||
var currentValue = $(e.target).closest('a').data('pushratingstouserplans');
|
||||
var context = {
|
||||
courseid: courseid,
|
||||
settings: { pushratingstouserplans: currentValue }
|
||||
};
|
||||
e.preventDefault();
|
||||
|
||||
templates.render('tool_lp/course_competency_settings', context).done(function(html) {
|
||||
str.get_string('configurecoursecompetencysettings', 'tool_lp').done(function (title) {
|
||||
this._dialogue = new Dialogue(
|
||||
title,
|
||||
html,
|
||||
this.addListeners.bind(this)
|
||||
);
|
||||
}.bind(this)).fail(notification.exception);
|
||||
}.bind(this)).fail(notification.exception);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the save listener to the form.
|
||||
*
|
||||
* @method addSaveListener
|
||||
*/
|
||||
settingsMod.prototype.addListeners = function() {
|
||||
var save = this._find('[data-action="save"]');
|
||||
save.on('click', this.saveSettings.bind(this));
|
||||
var cancel = this._find('[data-action="cancel"]');
|
||||
cancel.on('click', this.cancelChanges.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel the changes.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @method cancelChanges
|
||||
*/
|
||||
settingsMod.prototype.cancelChanges = function(e) {
|
||||
e.preventDefault();
|
||||
this._dialogue.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel the changes.
|
||||
*
|
||||
* @param {String} selector
|
||||
* @return {JQuery}
|
||||
*/
|
||||
settingsMod.prototype._find = function(selector) {
|
||||
return $('[data-region="coursecompetencysettings"]').find(selector);
|
||||
};
|
||||
|
||||
/**
|
||||
* Save the settings.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @method saveSettings
|
||||
*/
|
||||
settingsMod.prototype.saveSettings = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var newValue = this._find('input[name="pushratingstouserplans"]:checked').val();
|
||||
var courseId = this._find('input[name="courseid"]').val();
|
||||
var settings = { pushratingstouserplans: newValue };
|
||||
|
||||
ajax.call([
|
||||
{ methodname: 'core_competency_update_course_competency_settings',
|
||||
args: { courseid: courseId, settings: settings } }
|
||||
])[0].done(function() {
|
||||
this.refreshCourseCompetenciesPage();
|
||||
}.bind(this)).fail(notification.exception);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh the course competencies page.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @method saveSettings
|
||||
*/
|
||||
settingsMod.prototype.refreshCourseCompetenciesPage = function() {
|
||||
var courseId = this._find('input[name="courseid"]').val();
|
||||
|
||||
ajax.call([
|
||||
{ methodname: 'tool_lp_data_for_course_competencies_page',
|
||||
args: { courseid: courseId } }
|
||||
])[0].done(function(context) {
|
||||
templates.render('tool_lp/course_competencies_page', context).done(function(html, js) {
|
||||
$('[data-region="coursecompetenciespage"]').replaceWith(html);
|
||||
templates.runTemplateJS(js);
|
||||
this._dialogue.close();
|
||||
}.bind(this)).fail(notification.exception);
|
||||
}.bind(this)).fail(notification.exception);
|
||||
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/configurecoursecompetencysettings */ settingsMod;
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Wrapper for the YUI M.core.notification class. Allows us to
|
||||
* use the YUI version in AMD code until it is replaced.
|
||||
*
|
||||
* @module tool_lp/dialogue
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['core/yui'], function(Y) {
|
||||
|
||||
// Private variables and functions.
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param {String} title Title for the window.
|
||||
* @param {String} content The content for the window.
|
||||
* @param {function} afterShow Callback executed after the window is opened.
|
||||
* @param {function} afterHide Callback executed after the window is closed.
|
||||
* @param {Boolean} wide Specify we want an extra wide dialogue (the size is standard, but wider than the default).
|
||||
*/
|
||||
var dialogue = function(title, content, afterShow, afterHide, wide) {
|
||||
this.yuiDialogue = null;
|
||||
var parent = this;
|
||||
|
||||
// Default for wide is false.
|
||||
if (typeof wide == 'undefined') {
|
||||
wide = false;
|
||||
}
|
||||
|
||||
Y.use('moodle-core-notification', 'timers', function () {
|
||||
var width = '480px';
|
||||
if (wide) {
|
||||
width = '800px';
|
||||
}
|
||||
|
||||
parent.yuiDialogue = new M.core.dialogue({
|
||||
headerContent: title,
|
||||
bodyContent: content,
|
||||
draggable: true,
|
||||
visible: false,
|
||||
center: true,
|
||||
modal: true,
|
||||
width: width
|
||||
});
|
||||
|
||||
parent.yuiDialogue.after('visibleChange', function(e) {
|
||||
if (e.newVal) {
|
||||
// Delay the callback call to the next tick, otherwise it can happen that it is
|
||||
// executed before the dialogue constructor returns.
|
||||
if ((typeof afterShow !== 'undefined')) {
|
||||
Y.soon(function() {
|
||||
afterShow(parent);
|
||||
parent.yuiDialogue.centerDialogue();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if ((typeof afterHide !== 'undefined')) {
|
||||
Y.soon(function() {
|
||||
afterHide(parent);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
parent.yuiDialogue.show();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Close this window.
|
||||
*/
|
||||
dialogue.prototype.close = function() {
|
||||
this.yuiDialogue.hide();
|
||||
this.yuiDialogue.destroy();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get content.
|
||||
*/
|
||||
dialogue.prototype.getContent = function() {
|
||||
return this.yuiDialogue.bodyNode.getDOMNode();
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/dialogue */ dialogue;
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Drag and drop reorder via HTML5.
|
||||
*
|
||||
* @module tool_lp/dragdrop-reorder
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['core/str', 'core/yui'], function(str, Y) {
|
||||
|
||||
// Private variables and functions.
|
||||
|
||||
/**
|
||||
* Translate the drophit event from YUI
|
||||
* into simple drag and drop nodes.
|
||||
* @param {Y.Event} e The yui drop event.
|
||||
*/
|
||||
var proxyCallback = function(e) {
|
||||
var dragNode = e.drag.get('node');
|
||||
var dropNode = e.drop.get('node');
|
||||
this.callback(dragNode.getDOMNode(), dropNode.getDOMNode());
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/dragdrop-reorder */ {
|
||||
// Public variables and functions.
|
||||
/**
|
||||
* Create an instance of M.tool_lp.dragdrop
|
||||
*
|
||||
* @param {String} group Unique string to identify this interaction.
|
||||
* @param {String} dragHandleText Alt text for the drag handle.
|
||||
* @param {String} sameNodeText Used in keyboard drag drop for the list of items target.
|
||||
* @param {String} parentNodeText Used in keyboard drag drop for the parent target.
|
||||
* @param {String} sameNodeClass class used to find the each of the list of items.
|
||||
* @param {String} parentNodeClass class used to find the container for the list of items.
|
||||
* @param {String} dragHandleInsertClass class used to find the location to insert the drag handles.
|
||||
* @param {function} callback Drop hit handler.
|
||||
*/
|
||||
dragdrop: function(group,
|
||||
dragHandleText,
|
||||
sameNodeText,
|
||||
parentNodeText,
|
||||
sameNodeClass,
|
||||
parentNodeClass,
|
||||
dragHandleInsertClass,
|
||||
callback) {
|
||||
// Here we are wrapping YUI. This allows us to start transitioning, but
|
||||
// wait for a good alternative without having inconsistent UIs.
|
||||
str.get_strings([
|
||||
{ key: 'emptydragdropregion', component: 'moodle' },
|
||||
{ key: 'movecontent', component: 'moodle' },
|
||||
{ key: 'tocontent', component: 'moodle' },
|
||||
]).done( function () {
|
||||
Y.use('moodle-tool_lp-dragdrop-reorder', function () {
|
||||
|
||||
var context = {
|
||||
callback: callback
|
||||
};
|
||||
M.tool_lp.dragdrop_reorder({
|
||||
group: group,
|
||||
dragHandleText: dragHandleText,
|
||||
sameNodeText: sameNodeText,
|
||||
parentNodeText: parentNodeText,
|
||||
sameNodeClass: sameNodeClass,
|
||||
parentNodeClass: parentNodeClass,
|
||||
dragHandleInsertClass: dragHandleInsertClass,
|
||||
callback: Y.bind(proxyCallback, context)
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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 base javascript module.
|
||||
*
|
||||
* @module tool_lp/event_base
|
||||
* @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($) {
|
||||
|
||||
/**
|
||||
* Base class.
|
||||
*/
|
||||
var Base = function() {
|
||||
this._eventNode = $('<div></div>');
|
||||
};
|
||||
|
||||
/** @type {Node} The node we attach the events to. */
|
||||
Base.prototype._eventNode = null;
|
||||
|
||||
/**
|
||||
* Register an event listener.
|
||||
*
|
||||
* @param {String} type The event type.
|
||||
* @param {Function} handler The event listener.
|
||||
* @method on
|
||||
*/
|
||||
Base.prototype.on = function(type, handler) {
|
||||
this._eventNode.on(type, handler);
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger an event.
|
||||
*
|
||||
* @param {String} type The type of event.
|
||||
* @param {Object} The data to pass to the listeners.
|
||||
* @method _trigger
|
||||
*/
|
||||
Base.prototype._trigger = function(type, data) {
|
||||
this._eventNode.trigger(type, [data]);
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/event_base */ Base;
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Evidence delete.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery',
|
||||
'core/notification',
|
||||
'core/ajax',
|
||||
'core/str',
|
||||
'core/log'],
|
||||
function($, Notification, Ajax, Str, Log) {
|
||||
|
||||
var selectors = {};
|
||||
|
||||
/**
|
||||
* Register an event listener.
|
||||
*
|
||||
* @param {String} triggerSelector The node on which the click will happen.
|
||||
* @param {String} containerSelector The parent node that will be removed and contains the evidence ID.
|
||||
* @return {Void}
|
||||
*/
|
||||
var register = function(triggerSelector, containerSelector) {
|
||||
if (typeof selectors[triggerSelector] !== 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
selectors[triggerSelector] = $('body').delegate(triggerSelector, 'click', function(e) {
|
||||
var parent = $(e.currentTarget).parents(containerSelector);
|
||||
if (!parent.length || parent.length > 1) {
|
||||
Log.error('None or too many evidence container were found.');
|
||||
return;
|
||||
}
|
||||
var evidenceId = parent.data('id');
|
||||
if (!evidenceId) {
|
||||
Log.error('Evidence ID was not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
Str.get_strings([
|
||||
{ key: 'confirm', component: 'moodle' },
|
||||
{ key: 'areyousure', component: 'moodle' },
|
||||
{ key: 'delete', component: 'moodle' },
|
||||
{ key: 'cancel', component: 'moodle' }
|
||||
]).done(function (strings) {
|
||||
Notification.confirm(
|
||||
strings[0], // Confirm.
|
||||
strings[1], // Are you sure?
|
||||
strings[2], // Delete.
|
||||
strings[3], // Cancel.
|
||||
function() {
|
||||
var promise = Ajax.call([{
|
||||
methodname: 'core_competency_delete_evidence',
|
||||
args: {
|
||||
id: evidenceId
|
||||
}
|
||||
}]);
|
||||
promise[0].then(function() {
|
||||
parent.remove();
|
||||
}).fail(Notification.exception);
|
||||
}
|
||||
);
|
||||
}).fail(Notification.exception);
|
||||
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/evidence_delete */ {
|
||||
|
||||
/**
|
||||
* Register an event listener.
|
||||
*
|
||||
* @param {String} triggerSelector The node on which the click will happen.
|
||||
* @param {String} containerSelector The parent node that will be removed and contains the evidence ID.
|
||||
* @return {Void}
|
||||
*/
|
||||
register: register
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
// 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 selector module.
|
||||
*
|
||||
* @module tool_lp/form-cohort-selector
|
||||
* @class form-cohort-selector
|
||||
* @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/ajax', 'core/templates'], function($, Ajax, Templates) {
|
||||
|
||||
return /** @alias module:tool_lp/form-cohort-selector */ {
|
||||
|
||||
processResults: function(selector, results) {
|
||||
var cohorts = [];
|
||||
$.each(results, function(index, cohort) {
|
||||
cohorts.push({
|
||||
value: cohort.id,
|
||||
label: cohort._label
|
||||
});
|
||||
});
|
||||
return cohorts;
|
||||
},
|
||||
|
||||
transport: function(selector, query, success, failure) {
|
||||
var promise,
|
||||
contextid = parseInt($(selector).data('contextid'), 10),
|
||||
includes = $(selector).data('includes');
|
||||
|
||||
promise = Ajax.call([{
|
||||
methodname: 'tool_lp_search_cohorts',
|
||||
args: {
|
||||
query: query,
|
||||
context: { contextid: contextid },
|
||||
includes: includes
|
||||
}
|
||||
}]);
|
||||
|
||||
promise[0].then(function(results) {
|
||||
var promises = [],
|
||||
i = 0;
|
||||
|
||||
// Render the label.
|
||||
$.each(results.cohorts, function(index, cohort) {
|
||||
promises.push(Templates.render('tool_lp/form-cohort-selector-suggestion', cohort));
|
||||
});
|
||||
|
||||
// Apply the label to the results.
|
||||
return $.when.apply($.when, promises).then(function() {
|
||||
var args = arguments;
|
||||
$.each(results.cohorts, function(index, cohort) {
|
||||
cohort._label = args[i];
|
||||
i++;
|
||||
});
|
||||
success(results.cohorts);
|
||||
});
|
||||
|
||||
}, failure);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* User selector module.
|
||||
*
|
||||
* @module tool_lp/form-user-selector
|
||||
* @class form-user-selector
|
||||
* @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/ajax', 'core/templates'], function($, Ajax, Templates) {
|
||||
|
||||
return /** @alias module:tool_lp/form-user-selector */ {
|
||||
|
||||
processResults: function(selector, results) {
|
||||
var users = [];
|
||||
$.each(results, function(index, user) {
|
||||
users.push({
|
||||
value: user.id,
|
||||
label: user._label
|
||||
});
|
||||
});
|
||||
return users;
|
||||
},
|
||||
|
||||
transport: function(selector, query, success, failure) {
|
||||
var promise;
|
||||
var capability = $(selector).data('capability');
|
||||
if (typeof capability === "undefined") {
|
||||
capability = '';
|
||||
}
|
||||
|
||||
promise = Ajax.call([{
|
||||
methodname: 'tool_lp_search_users',
|
||||
args: {
|
||||
query: query,
|
||||
capability: capability
|
||||
}
|
||||
}]);
|
||||
|
||||
promise[0].then(function(results) {
|
||||
var promises = [],
|
||||
i = 0;
|
||||
|
||||
// Render the label.
|
||||
$.each(results.users, function(index, user) {
|
||||
var ctx = user,
|
||||
identity = [];
|
||||
$.each(['idnumber', 'email', 'phone1', 'phone2', 'department', 'institution'], function(i, k) {
|
||||
if (typeof user[k] !== 'undefined' && user[k] !== '') {
|
||||
ctx.hasidentity = true;
|
||||
identity.push(user[k]);
|
||||
}
|
||||
});
|
||||
ctx.identity = identity.join(', ');
|
||||
promises.push(Templates.render('tool_lp/form-user-selector-suggestion', ctx));
|
||||
});
|
||||
|
||||
// Apply the label to the results.
|
||||
return $.when.apply($.when, promises).then(function() {
|
||||
var args = arguments;
|
||||
$.each(results.users, function(index, user) {
|
||||
user._label = args[i];
|
||||
i++;
|
||||
});
|
||||
success(results.users);
|
||||
});
|
||||
|
||||
}, failure);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,179 @@
|
||||
// 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 frameworks actions via ajax.
|
||||
*
|
||||
* @module tool_lp/frameworkactions
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'], function($, templates, ajax, notification, str) {
|
||||
// Private variables and functions.
|
||||
|
||||
/** @var {Number} pagecontextid The id of the context */
|
||||
var pagecontextid = 0;
|
||||
|
||||
/** @var {Number} frameworkid The id of the framework */
|
||||
var frameworkid = 0;
|
||||
|
||||
/**
|
||||
* Callback to replace the dom element with the rendered template.
|
||||
*
|
||||
* @param {String} newhtml The new html to insert.
|
||||
* @param {String} newjs The new js to run.
|
||||
*/
|
||||
var updatePage = function(newhtml, newjs) {
|
||||
$('[data-region="managecompetencies"]').replaceWith(newhtml);
|
||||
templates.runTemplateJS(newjs);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to render the page template again and update the page.
|
||||
*
|
||||
* @param {Object} context The context for the template.
|
||||
*/
|
||||
var reloadList = function(context) {
|
||||
templates.render('tool_lp/manage_competency_frameworks_page', context)
|
||||
.done(updatePage)
|
||||
.fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Duplicate a framework and reload the page.
|
||||
* @method doDuplicate
|
||||
* @param {Event} e
|
||||
*/
|
||||
var doDuplicate = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
frameworkid = $(this).attr('data-frameworkid');
|
||||
|
||||
// We are chaining ajax requests here.
|
||||
var requests = ajax.call([{
|
||||
methodname: 'core_competency_duplicate_competency_framework',
|
||||
args: { id: frameworkid }
|
||||
}, {
|
||||
methodname: 'tool_lp_data_for_competency_frameworks_manage_page',
|
||||
args: {
|
||||
pagecontext: {
|
||||
contextid: pagecontextid
|
||||
}
|
||||
}
|
||||
}]);
|
||||
requests[1].done(reloadList).fail(notification.exception);
|
||||
};
|
||||
/**
|
||||
* Delete a framework and reload the page.
|
||||
*/
|
||||
var doDelete = function() {
|
||||
|
||||
// We are chaining ajax requests here.
|
||||
var requests = ajax.call([{
|
||||
methodname: 'core_competency_delete_competency_framework',
|
||||
args: { id: frameworkid }
|
||||
}, {
|
||||
methodname: 'tool_lp_data_for_competency_frameworks_manage_page',
|
||||
args: {
|
||||
pagecontext: {
|
||||
contextid: pagecontextid
|
||||
}
|
||||
}
|
||||
}]);
|
||||
requests[0].done(function (success) {
|
||||
if (success === false) {
|
||||
var req = ajax.call([{
|
||||
methodname: 'core_competency_read_competency_framework',
|
||||
args: { id: frameworkid }
|
||||
}]);
|
||||
req[0].done(function (framework) {
|
||||
str.get_strings([
|
||||
{ key: 'frameworkcannotbedeleted', component: 'tool_lp', param: framework.shortname },
|
||||
{ key: 'cancel', component: 'moodle' }
|
||||
]).done(function (strings) {
|
||||
notification.alert(
|
||||
null,
|
||||
strings[0]
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
});
|
||||
}
|
||||
}).fail(notification.exception);
|
||||
requests[1].done(reloadList).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for "Delete competency framework" actions.
|
||||
* @param {Event} e
|
||||
*/
|
||||
var confirmDelete = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var id = $(this).attr('data-frameworkid');
|
||||
frameworkid = id;
|
||||
|
||||
var requests = ajax.call([{
|
||||
methodname: 'core_competency_read_competency_framework',
|
||||
args: { id: frameworkid }
|
||||
}]);
|
||||
|
||||
requests[0].done(function(framework) {
|
||||
str.get_strings([
|
||||
{ key: 'confirm', component: 'moodle' },
|
||||
{ key: 'deletecompetencyframework', component: 'tool_lp', param: framework.shortname },
|
||||
{ key: 'delete', component: 'moodle' },
|
||||
{ key: 'cancel', component: 'moodle' }
|
||||
]).done(function (strings) {
|
||||
notification.confirm(
|
||||
strings[0], // Confirm.
|
||||
strings[1], // Delete competency framework X?
|
||||
strings[2], // Delete.
|
||||
strings[3], // Cancel.
|
||||
doDelete
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
|
||||
};
|
||||
|
||||
|
||||
return /** @alias module:tool_lp/frameworkactions */ {
|
||||
// Public variables and functions.
|
||||
|
||||
/**
|
||||
* Expose the event handler for delete.
|
||||
* @method deleteHandler
|
||||
* @param {Event} e
|
||||
*/
|
||||
deleteHandler: confirmDelete,
|
||||
|
||||
/**
|
||||
* Expose the event handler for duplicate.
|
||||
* @method duplicateHandler
|
||||
* @param {Event} e
|
||||
*/
|
||||
duplicateHandler: doDuplicate,
|
||||
|
||||
/**
|
||||
* Initialise the module.
|
||||
* @method init
|
||||
* @param {Number} contextid The context id of the page.
|
||||
*/
|
||||
init: function(contextid) {
|
||||
pagecontextid = contextid;
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Frameworks datasource.
|
||||
*
|
||||
* This module is compatible with core/form-autocomplete.
|
||||
*
|
||||
* @package tool_lpmigrate
|
||||
* @copyright 2016 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notification) {
|
||||
|
||||
return /** @alias module:tool_lpmigrate/frameworks_datasource */ {
|
||||
|
||||
/**
|
||||
* List frameworks.
|
||||
*
|
||||
* @param {Number} contextId The context ID.
|
||||
* @param {Object} options Additional parameters to pass to the external function.
|
||||
* @return {Promise}
|
||||
*/
|
||||
list: function(contextId, options) {
|
||||
var promise,
|
||||
args = {
|
||||
context: {
|
||||
contextid: contextId
|
||||
}
|
||||
};
|
||||
|
||||
$.extend(args, typeof options === 'undefined' ? {} : options);
|
||||
promise = Ajax.call([{
|
||||
methodname: 'core_competency_list_competency_frameworks',
|
||||
args: args
|
||||
}])[0];
|
||||
|
||||
return promise.fail(Notification.exception);
|
||||
},
|
||||
|
||||
/**
|
||||
* Process the results for auto complete elements.
|
||||
*
|
||||
* @param {String} selector The selector of the auto complete element.
|
||||
* @param {Array} results An array or results.
|
||||
* @return {Array} New array of results.
|
||||
*/
|
||||
processResults: function(selector, results) {
|
||||
var options = [];
|
||||
$.each(results, function(index, data) {
|
||||
options.push({
|
||||
value: data.id,
|
||||
label: data.shortname + ' ' + data.idnumber
|
||||
});
|
||||
});
|
||||
return options;
|
||||
},
|
||||
|
||||
/**
|
||||
* Source of data for Ajax element.
|
||||
*
|
||||
* @param {String} selector The selector of the auto complete element.
|
||||
* @param {String} query The query string.
|
||||
* @param {Function} callback A callback function receiving an array of results.
|
||||
* @return {Void}
|
||||
*/
|
||||
transport: function(selector, query, callback) {
|
||||
var el = $(selector),
|
||||
contextId = el.data('contextid'),
|
||||
onlyVisible = el.data('onlyvisible');
|
||||
|
||||
if (!contextId) {
|
||||
throw new Error('The attribute data-contextid is required on ' + selector);
|
||||
}
|
||||
|
||||
this.list(contextId, {
|
||||
query: query,
|
||||
onlyvisible: onlyVisible,
|
||||
}).then(callback);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,144 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Grade dialogue.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery',
|
||||
'core/notification',
|
||||
'core/templates',
|
||||
'tool_lp/dialogue',
|
||||
'tool_lp/event_base',
|
||||
'core/str'],
|
||||
function($, Notification, Templates, Dialogue, EventBase, Str) {
|
||||
|
||||
/**
|
||||
* Grade dialogue class.
|
||||
*/
|
||||
var Grade = function(ratingOptions) {
|
||||
EventBase.prototype.constructor.apply(this, []);
|
||||
this._ratingOptions = ratingOptions;
|
||||
};
|
||||
Grade.prototype = Object.create(EventBase.prototype);
|
||||
|
||||
/** @type {Dialogue} The dialogue. */
|
||||
Grade.prototype._popup = null;
|
||||
/** @type {Array} Array of objects containing, 'value', 'name' and optionally 'selected'. */
|
||||
Grade.prototype._ratingOptions = null;
|
||||
|
||||
/**
|
||||
* After render hook.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @method _afterRender
|
||||
* @protected
|
||||
*/
|
||||
Grade.prototype._afterRender = function() {
|
||||
var btnRate = this._find('[data-action="rate"]'),
|
||||
lstRating = this._find('[name="rating"]'),
|
||||
txtComment = this._find('[name="comment"]');
|
||||
|
||||
this._find('[data-action="cancel"]').click(function(e) {
|
||||
e.preventDefault();
|
||||
this._trigger('cancelled');
|
||||
this.close();
|
||||
}.bind(this));
|
||||
|
||||
lstRating.change(function() {
|
||||
var node = $(this);
|
||||
if (!node.val()) {
|
||||
btnRate.prop('disabled', true);
|
||||
} else {
|
||||
btnRate.prop('disabled', false);
|
||||
}
|
||||
}).change();
|
||||
|
||||
btnRate.click(function(e) {
|
||||
e.preventDefault();
|
||||
var val = lstRating.val();
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
this._trigger('rated', {
|
||||
'rating': val,
|
||||
'note': txtComment.val()
|
||||
});
|
||||
this.close();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the dialogue.
|
||||
*
|
||||
* @method close
|
||||
*/
|
||||
Grade.prototype.close = function() {
|
||||
this._popup.close();
|
||||
this._popup = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens the picker.
|
||||
*
|
||||
* @param {Number} competencyId The competency ID of the competency to work on.
|
||||
* @method display
|
||||
* @return {Promise}
|
||||
*/
|
||||
Grade.prototype.display = function() {
|
||||
return this._render().then(function(html) {
|
||||
return Str.get_string('rate', 'tool_lp').then(function(title) {
|
||||
this._popup = new Dialogue(
|
||||
title,
|
||||
html,
|
||||
this._afterRender.bind(this)
|
||||
);
|
||||
}.bind(this));
|
||||
}.bind(this)).fail(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find a node in the dialogue.
|
||||
*
|
||||
* @param {String} selector
|
||||
* @method _find
|
||||
* @protected
|
||||
*/
|
||||
Grade.prototype._find = function(selector) {
|
||||
return $(this._popup.getContent()).find(selector);
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the dialogue.
|
||||
*
|
||||
* @method _render
|
||||
* @protected
|
||||
* @return {Promise}
|
||||
*/
|
||||
Grade.prototype._render = function() {
|
||||
var context = {
|
||||
cangrade: this._canGrade,
|
||||
ratings: this._ratingOptions
|
||||
};
|
||||
return Templates.render('tool_lp/competency_grader', context);
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/grade_dialogue */ Grade;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,146 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Module to enable inline editing of a comptency grade.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery',
|
||||
'core/notification',
|
||||
'core/ajax',
|
||||
'core/log',
|
||||
'tool_lp/grade_dialogue',
|
||||
'tool_lp/event_base',
|
||||
'tool_lp/scalevalues',
|
||||
], function($, notification, ajax, log, GradeDialogue, EventBase, ScaleValues) {
|
||||
|
||||
/**
|
||||
* InlineEditor
|
||||
*
|
||||
* @param {String} selector The selector to trigger the grading.
|
||||
* @param {Number} The id of the scale for this competency.
|
||||
* @param {Number} The id of the competency.
|
||||
* @param {Number} The id of the user.
|
||||
* @param {Number} The id of the plan.
|
||||
* @param {Number} The id of the course.
|
||||
* @param {String} Language string for choose a rating.
|
||||
*/
|
||||
var InlineEditor = function(selector, scaleId, competencyId, userId, planId, courseId, chooseStr) {
|
||||
EventBase.prototype.constructor.apply(this, []);
|
||||
|
||||
var trigger = $(selector);
|
||||
if (!trigger.length) {
|
||||
throw new Error('Could not find the trigger');
|
||||
}
|
||||
|
||||
this._scaleId = scaleId;
|
||||
this._competencyId = competencyId;
|
||||
this._userId = userId;
|
||||
this._planId = planId;
|
||||
this._courseId = courseId;
|
||||
this._chooseStr = chooseStr;
|
||||
this._setUp();
|
||||
|
||||
trigger.click(function(e) {
|
||||
e.preventDefault();
|
||||
this._dialogue.display();
|
||||
}.bind(this));
|
||||
|
||||
if (this._planId) {
|
||||
this._methodName = 'core_competency_grade_competency_in_plan';
|
||||
this._args = {
|
||||
competencyid: this._competencyId,
|
||||
planid: this._planId
|
||||
};
|
||||
} else if (this._courseId) {
|
||||
this._methodName = 'core_competency_grade_competency_in_course';
|
||||
this._args = {
|
||||
competencyid: this._competencyId,
|
||||
courseid: this._courseId,
|
||||
userid: this._userId
|
||||
};
|
||||
} else {
|
||||
this._methodName = 'core_competency_grade_competency';
|
||||
this._args = {
|
||||
userid: this._userId,
|
||||
competencyid: this._competencyId
|
||||
};
|
||||
}
|
||||
};
|
||||
InlineEditor.prototype = Object.create(EventBase.prototype);
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*
|
||||
* @method _setUp
|
||||
*/
|
||||
InlineEditor.prototype._setUp = function() {
|
||||
var options = [],
|
||||
self = this;
|
||||
|
||||
var promise = ScaleValues.get_values(self._scaleId);
|
||||
promise.done(function(scalevalues) {
|
||||
options.push({
|
||||
value: '',
|
||||
name: self._chooseStr
|
||||
});
|
||||
|
||||
for (var i = 0; i < scalevalues.length; i++) {
|
||||
var optionConfig = scalevalues[i];
|
||||
options.push({
|
||||
value: optionConfig.id,
|
||||
name: optionConfig.name
|
||||
});
|
||||
}
|
||||
|
||||
self._dialogue = new GradeDialogue(options);
|
||||
self._dialogue.on('rated', function(e, data) {
|
||||
var args = self._args;
|
||||
args.grade = data.rating;
|
||||
args.note = data.note;
|
||||
ajax.call([{
|
||||
methodname: self._methodName,
|
||||
args: args,
|
||||
done: function(evidence) {
|
||||
self._trigger('competencyupdated', { args: args, evidence: evidence });
|
||||
}.bind(self),
|
||||
fail: notification.exception
|
||||
}]);
|
||||
}.bind(self));
|
||||
}).fail(notification.exception);
|
||||
};
|
||||
|
||||
/** @type {Number} The scale id for this competency. */
|
||||
InlineEditor.prototype._scaleId = null;
|
||||
/** @type {Number} The id of the competency. */
|
||||
InlineEditor.prototype._competencyId = null;
|
||||
/** @type {Number} The id of the user. */
|
||||
InlineEditor.prototype._userId = null;
|
||||
/** @type {Number} The id of the plan. */
|
||||
InlineEditor.prototype._planId = null;
|
||||
/** @type {Number} The id of the course. */
|
||||
InlineEditor.prototype._courseId = null;
|
||||
/** @type {String} The text for Choose rating. */
|
||||
InlineEditor.prototype._chooseStr = null;
|
||||
/** @type {GradeDialogue} The grading dialogue. */
|
||||
InlineEditor.prototype._dialogue = null;
|
||||
|
||||
return /** @alias module:tool_lp/grade_user_competency_inline */ InlineEditor;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,817 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Aria menubar functionality. Enhances a simple nested list structure into a full aria widget.
|
||||
* Based on the open ajax example: http://oaa-accessibility.org/example/26/
|
||||
*
|
||||
* @module tool_lp/menubar
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery'], function($) {
|
||||
|
||||
/** @property {boolean} Flag to indicate if we have already registered a click event handler for the document. */
|
||||
var documentClickHandlerRegistered = false;
|
||||
|
||||
/** @property {boolean} Flag to indicate whether there's an active, open menu. */
|
||||
var menuActive = false;
|
||||
|
||||
/**
|
||||
* Close all open submenus anywhere in the page (there should only ever be one open at a time).
|
||||
*
|
||||
* @method closeAllSubMenus
|
||||
*/
|
||||
var closeAllSubMenus = function() {
|
||||
$('.tool-lp-menu .tool-lp-sub-menu').attr('aria-hidden', 'true');
|
||||
// Every menu's closed at this point, so set the menu active flag to false.
|
||||
menuActive = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param {$} Jquery collection matching the root of the menu.
|
||||
* @param {Function[]} handlers, called when a menu item is chosen.
|
||||
*/
|
||||
var Menubar = function(menuRoot, handlers) {
|
||||
// Setup private class variables.
|
||||
this.menuRoot = menuRoot;
|
||||
this.handlers = handlers;
|
||||
this.rootMenus = this.menuRoot.children('li');
|
||||
this.subMenus = this.rootMenus.children('ul');
|
||||
this.subMenuItems = this.subMenus.children('li');
|
||||
this.allItems = this.rootMenus.add(this.subMenuItems);
|
||||
this.activeItem = null;
|
||||
this.isChildOpen = false;
|
||||
|
||||
this.keys = {
|
||||
tab: 9,
|
||||
enter: 13,
|
||||
esc: 27,
|
||||
space: 32,
|
||||
left: 37,
|
||||
up: 38,
|
||||
right: 39,
|
||||
down: 40
|
||||
};
|
||||
|
||||
this.addAriaAttributes();
|
||||
// Add the event listeners.
|
||||
this.addEventListeners();
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a submenu, first it closes all other sub-menus and sets the open direction.
|
||||
* @method openSubMenu
|
||||
* @param {Node} menu
|
||||
*/
|
||||
Menubar.prototype.openSubMenu = function(menu) {
|
||||
this.setOpenDirection();
|
||||
closeAllSubMenus();
|
||||
menu.attr('aria-hidden', 'false');
|
||||
// Set menu active flag to true when a menu is opened.
|
||||
menuActive = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Bind the event listeners to the DOM
|
||||
* @method addEventListeners
|
||||
*/
|
||||
Menubar.prototype.addEventListeners = function() {
|
||||
var currentThis = this;
|
||||
|
||||
// When clicking outside the menubar.
|
||||
if (documentClickHandlerRegistered === false) {
|
||||
$(document).click(function() {
|
||||
// Check if a menu is opened.
|
||||
if (menuActive) {
|
||||
// Close menu.
|
||||
closeAllSubMenus();
|
||||
}
|
||||
});
|
||||
// Set this flag to true so that we won't need to add a document click handler for the other Menubar instances.
|
||||
documentClickHandlerRegistered = true;
|
||||
}
|
||||
|
||||
// Hovers.
|
||||
this.subMenuItems.mouseenter(function() {
|
||||
$(this).addClass('menu-hover');
|
||||
return true;
|
||||
});
|
||||
|
||||
this.subMenuItems.mouseout(function() {
|
||||
$(this).removeClass('menu-hover');
|
||||
return true;
|
||||
});
|
||||
|
||||
// Mouse listeners.
|
||||
this.allItems.click(function(e) {
|
||||
return currentThis.handleClick($(this), e);
|
||||
});
|
||||
|
||||
// Key listeners.
|
||||
this.allItems.keydown(function(e) {
|
||||
return currentThis.handleKeyDown($(this), e);
|
||||
});
|
||||
|
||||
this.allItems.focus(function() {
|
||||
return currentThis.handleFocus($(this));
|
||||
});
|
||||
|
||||
this.allItems.blur(function() {
|
||||
return currentThis.handleBlur($(this));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Process click events for the top menus.
|
||||
*
|
||||
* @method handleClick
|
||||
* @param {Object} item is the jquery object of the item firing the event
|
||||
* @param {Event} e is the associated event object
|
||||
* @return boolean Returns false
|
||||
*/
|
||||
Menubar.prototype.handleClick = function(item, e) {
|
||||
e.stopPropagation();
|
||||
|
||||
var parentUL = item.parent();
|
||||
|
||||
if (parentUL.is('.tool-lp-menu')) {
|
||||
// Toggle the child menu open/closed.
|
||||
if (item.children('ul').first().attr('aria-hidden') == 'true') {
|
||||
this.openSubMenu(item.children('ul').first());
|
||||
} else {
|
||||
item.children('ul').first().attr('aria-hidden', 'true');
|
||||
}
|
||||
} else {
|
||||
// Remove hover and focus styling.
|
||||
this.allItems.removeClass('menu-hover menu-focus');
|
||||
|
||||
// Clear the active item.
|
||||
this.activeItem = null;
|
||||
|
||||
// Close the menu.
|
||||
this.menuRoot.find('ul').not('.root-level').attr('aria-hidden','true');
|
||||
// Follow any link, or call the click handlers.
|
||||
var anchor = item.find('a').first();
|
||||
var clickEvent = new $.Event('click');
|
||||
clickEvent.target = anchor;
|
||||
var eventHandled = false;
|
||||
if (this.handlers) {
|
||||
$.each(this.handlers, function(selector, handler) {
|
||||
if (eventHandled) {
|
||||
return false;
|
||||
}
|
||||
if (item.find(selector).length > 0) {
|
||||
var callable = $.proxy(handler, anchor);
|
||||
// False means stop propogatting events.
|
||||
eventHandled = (callable(clickEvent) === false) || clickEvent.isDefaultPrevented();
|
||||
}
|
||||
});
|
||||
}
|
||||
// If we didn't find a handler, and the HREF is # that probably means that
|
||||
// we are handling it from somewhere else. Let's just do nothing in that case.
|
||||
if (!eventHandled && anchor.attr('href') !== '#') {
|
||||
window.location.href = anchor.attr('href');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
* Process focus events for the menu.
|
||||
*
|
||||
* @method handleFocus
|
||||
* @param {Object} item is the jquery object of the item firing the event
|
||||
* @return boolean Returns false
|
||||
*/
|
||||
Menubar.prototype.handleFocus = function(item) {
|
||||
|
||||
// If activeItem is null, we are getting focus from outside the menu. Store
|
||||
// the item that triggered the event.
|
||||
if (this.activeItem === null) {
|
||||
this.activeItem = item;
|
||||
} else if (item[0] != this.activeItem[0]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the set of jquery objects for all the parent items of the active item.
|
||||
var parentItems = this.activeItem.parentsUntil('ul.tool-lp-menu').filter('li');
|
||||
|
||||
// Remove focus styling from all other menu items.
|
||||
this.allItems.removeClass('menu-focus');
|
||||
|
||||
// Add focus styling to the active item.
|
||||
this.activeItem.addClass('menu-focus');
|
||||
|
||||
// Add focus styling to all parent items.
|
||||
parentItems.addClass('menu-focus');
|
||||
|
||||
// If the bChildOpen flag has been set, open the active item's child menu (if applicable).
|
||||
if (this.isChildOpen === true) {
|
||||
|
||||
var itemUL = item.parent();
|
||||
|
||||
// If the itemUL is a root-level menu and item is a parent item,
|
||||
// show the child menu.
|
||||
if (itemUL.is('.tool-lp-menu') && (item.attr('aria-haspopup') == 'true')) {
|
||||
this.openSubMenu(item.children('ul').first());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
* Process blur events for the menu.
|
||||
*
|
||||
* @method handleBlur
|
||||
* @param {Object} item is the jquery object of the item firing the event
|
||||
* @return boolean Returns false
|
||||
*/
|
||||
Menubar.prototype.handleBlur = function(item) {
|
||||
item.removeClass('menu-focus');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
* Determine if the menu should open to the left, or the right,
|
||||
* based on the screen size and menu position.
|
||||
* @method setOpenDirection
|
||||
*/
|
||||
Menubar.prototype.setOpenDirection = function() {
|
||||
var pos = this.menuRoot.offset();
|
||||
var isRTL = $(document.body).hasClass('dir-rtl');
|
||||
var menuMinWidth = 160;
|
||||
var openLeft = false;
|
||||
|
||||
if (isRTL) {
|
||||
if (pos.left - menuMinWidth < 0) {
|
||||
openLeft = true;
|
||||
}
|
||||
} else {
|
||||
if (pos.left + menuMinWidth > $( window ).width()) {
|
||||
openLeft = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (openLeft) {
|
||||
this.menuRoot.addClass('tool-lp-menu-open-left');
|
||||
} else {
|
||||
this.menuRoot.removeClass('tool-lp-menu-open-left');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Process keyDown events for the menu.
|
||||
*
|
||||
* @method handleKeyDown
|
||||
* @param {Object} item is the jquery object of the item firing the event
|
||||
* @param {Event} e is the associated event object
|
||||
* @return boolean Returns false if consuming the event
|
||||
*/
|
||||
Menubar.prototype.handleKeyDown = function(item, e) {
|
||||
|
||||
if (e.altKey || e.ctrlKey) {
|
||||
// Modifier key pressed: Do not process.
|
||||
return true;
|
||||
}
|
||||
|
||||
switch(e.keyCode) {
|
||||
case this.keys.tab: {
|
||||
|
||||
// Hide all menu items and update their aria attributes.
|
||||
this.menuRoot.find('ul').attr('aria-hidden', 'true');
|
||||
|
||||
// Remove focus styling from all menu items.
|
||||
this.allItems.removeClass('menu-focus');
|
||||
|
||||
this.activeItem = null;
|
||||
|
||||
this.isChildOpen = false;
|
||||
|
||||
break;
|
||||
}
|
||||
case this.keys.esc: {
|
||||
var itemUL = item.parent();
|
||||
|
||||
if (itemUL.is('.tool-lp-menu')) {
|
||||
// Hide the child menu and update the aria attributes.
|
||||
item.children('ul').first().attr('aria-hidden', 'true');
|
||||
} else {
|
||||
|
||||
// Move up one level.
|
||||
this.activeItem = itemUL.parent();
|
||||
|
||||
// Reset the isChildOpen flag.
|
||||
this.isChildOpen = false;
|
||||
|
||||
// Set focus on the new item.
|
||||
this.activeItem.focus();
|
||||
|
||||
// Hide the active menu and update the aria attributes.
|
||||
itemUL.attr('aria-hidden', 'true');
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
case this.keys.enter:
|
||||
case this.keys.space: {
|
||||
// Trigger click handler.
|
||||
return this.handleClick(item, e);
|
||||
}
|
||||
|
||||
case this.keys.left: {
|
||||
|
||||
this.activeItem = this.moveToPrevious(item);
|
||||
|
||||
this.activeItem.focus();
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
case this.keys.right: {
|
||||
|
||||
this.activeItem = this.moveToNext(item);
|
||||
|
||||
this.activeItem.focus();
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
case this.keys.up: {
|
||||
|
||||
this.activeItem = this.moveUp(item);
|
||||
|
||||
this.activeItem.focus();
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
case this.keys.down: {
|
||||
|
||||
this.activeItem = this.moveDown(item);
|
||||
|
||||
this.activeItem.focus();
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Move to the next menu level.
|
||||
* This will be either the next root-level menu or the child of a menu parent. If
|
||||
* at the root level and the active item is the last in the menu, this function will loop
|
||||
* to the first menu item.
|
||||
*
|
||||
* If the menu is a horizontal menu, the first child element of the newly selected menu will
|
||||
* be selected
|
||||
*
|
||||
* @method moveToNext
|
||||
* @param {Object} item is the active menu item
|
||||
* @return {Object} Returns the item to move to. Returns item is no move is possible
|
||||
*/
|
||||
Menubar.prototype.moveToNext = function(item) {
|
||||
// Item's containing menu.
|
||||
var itemUL = item.parent();
|
||||
|
||||
// The items in the currently active menu.
|
||||
var menuItems = itemUL.children('li');
|
||||
|
||||
// The number of items in the active menu.
|
||||
var menuNum = menuItems.length;
|
||||
// The items index in its menu.
|
||||
var menuIndex = menuItems.index(item);
|
||||
var newItem = null;
|
||||
var childMenu = null;
|
||||
|
||||
if (itemUL.is('.tool-lp-menu')) {
|
||||
// This is the root level move to next sibling. This will require closing
|
||||
// the current child menu and opening the new one.
|
||||
|
||||
if (menuIndex < menuNum-1) {
|
||||
// Not the last root menu.
|
||||
newItem = item.next();
|
||||
} else { // Wrap to first item.
|
||||
newItem = menuItems.first();
|
||||
}
|
||||
|
||||
// Close the current child menu (if applicable).
|
||||
if (item.attr('aria-haspopup') == 'true') {
|
||||
|
||||
childMenu = item.children('ul').first();
|
||||
|
||||
if (childMenu.attr('aria-hidden') == 'false') {
|
||||
// Update the child menu's aria-hidden attribute.
|
||||
childMenu.attr('aria-hidden', 'true');
|
||||
this.isChildOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the focus styling from the current menu.
|
||||
item.removeClass('menu-focus');
|
||||
|
||||
// Open the new child menu (if applicable).
|
||||
if ((newItem.attr('aria-haspopup') === 'true') && (this.isChildOpen === true)) {
|
||||
|
||||
childMenu = newItem.children('ul').first();
|
||||
|
||||
// Update the child's aria-hidden attribute.
|
||||
this.openSubMenu(childMenu);
|
||||
}
|
||||
} else {
|
||||
// This is not the root level. If there is a child menu to be moved into, do that;
|
||||
// otherwise, move to the next root-level menu if there is one.
|
||||
if (item.attr('aria-haspopup') == 'true') {
|
||||
|
||||
childMenu = item.children('ul').first();
|
||||
|
||||
newItem = childMenu.children('li').first();
|
||||
|
||||
// Show the child menu and update its aria attributes.
|
||||
this.openSubMenu(childMenu);
|
||||
} else {
|
||||
// At deepest level, move to the next root-level menu.
|
||||
|
||||
var parentMenus = null;
|
||||
var rootItem = null;
|
||||
|
||||
// Get list of all parent menus for item, up to the root level.
|
||||
parentMenus = item.parentsUntil('ul.tool-lp-menu').filter('ul').not('.tool-lp-menu');
|
||||
|
||||
// Hide the current menu and update its aria attributes accordingly.
|
||||
parentMenus.attr('aria-hidden', 'true');
|
||||
|
||||
// Remove the focus styling from the active menu.
|
||||
parentMenus.find('li').removeClass('menu-focus');
|
||||
parentMenus.last().parent().removeClass('menu-focus');
|
||||
|
||||
// The containing root for the menu.
|
||||
rootItem = parentMenus.last().parent();
|
||||
|
||||
menuIndex = this.rootMenus.index(rootItem);
|
||||
|
||||
// If this is not the last root menu item, move to the next one.
|
||||
if (menuIndex < this.rootMenus.length-1) {
|
||||
newItem = rootItem.next();
|
||||
} else {
|
||||
// Loop.
|
||||
newItem = this.rootMenus.first();
|
||||
}
|
||||
|
||||
// Add the focus styling to the new menu.
|
||||
newItem.addClass('menu-focus');
|
||||
|
||||
if (newItem.attr('aria-haspopup') == 'true') {
|
||||
childMenu = newItem.children('ul').first();
|
||||
|
||||
newItem = childMenu.children('li').first();
|
||||
|
||||
// Show the child menu and update it's aria attributes.
|
||||
this.openSubMenu(childMenu);
|
||||
this.isChildOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newItem;
|
||||
};
|
||||
|
||||
/**
|
||||
* Member function to move to the previous menu level.
|
||||
* This will be either the previous root-level menu or the child of a menu parent. If
|
||||
* at the root level and the active item is the first in the menu, this function will loop
|
||||
* to the last menu item.
|
||||
*
|
||||
* If the menu is a horizontal menu, the first child element of the newly selected menu will
|
||||
* be selected
|
||||
*
|
||||
* @method moveToPrevious
|
||||
* @param {Object} item is the active menu item
|
||||
* @return {Object} Returns the item to move to. Returns item is no move is possible
|
||||
*/
|
||||
Menubar.prototype.moveToPrevious = function(item) {
|
||||
// Item's containing menu.
|
||||
var itemUL = item.parent();
|
||||
// The items in the currently active menu.
|
||||
var menuItems = itemUL.children('li');
|
||||
// The items index in its menu.
|
||||
var menuIndex = menuItems.index(item);
|
||||
var newItem = null;
|
||||
var childMenu = null;
|
||||
|
||||
if (itemUL.is('.tool-lp-menu')) {
|
||||
// This is the root level move to previous sibling. This will require closing
|
||||
// the current child menu and opening the new one.
|
||||
|
||||
if (menuIndex > 0) {
|
||||
// Not the first root menu.
|
||||
newItem = item.prev();
|
||||
} else {
|
||||
// Wrap to last item.
|
||||
newItem = menuItems.last();
|
||||
}
|
||||
|
||||
// Close the current child menu (if applicable).
|
||||
if (item.attr('aria-haspopup') == 'true') {
|
||||
childMenu = item.children('ul').first();
|
||||
|
||||
if (childMenu.attr('aria-hidden') == 'false') {
|
||||
// Update the child menu's aria-hidden attribute.
|
||||
childMenu.attr('aria-hidden', 'true');
|
||||
this.isChildOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the focus styling from the current menu.
|
||||
item.removeClass('menu-focus');
|
||||
|
||||
// Open the new child menu (if applicable).
|
||||
if ((newItem.attr('aria-haspopup') === 'true') && (this.isChildOpen === true)) {
|
||||
|
||||
childMenu = newItem.children('ul').first();
|
||||
|
||||
// Update the child's aria-hidden attribute.
|
||||
this.openSubMenu(childMenu);
|
||||
|
||||
}
|
||||
} else {
|
||||
// This is not the root level. If there is a parent menu that is not the
|
||||
// root menu, move up one level; otherwise, move to first item of the previous
|
||||
// root menu.
|
||||
|
||||
var parentLI = itemUL.parent();
|
||||
var parentUL = parentLI.parent();
|
||||
|
||||
// If this is a vertical menu or is not the first child menu
|
||||
// of the root-level menu, move up one level.
|
||||
if (!parentUL.is('.tool-lp-menu')) {
|
||||
|
||||
newItem = itemUL.parent();
|
||||
|
||||
// Hide the active menu and update aria-hidden.
|
||||
itemUL.attr('aria-hidden', 'true');
|
||||
|
||||
// Remove the focus highlight from the item.
|
||||
item.removeClass('menu-focus');
|
||||
|
||||
} else {
|
||||
// Move to previous root-level menu.
|
||||
|
||||
// Hide the current menu and update the aria attributes accordingly.
|
||||
itemUL.attr('aria-hidden', 'true');
|
||||
|
||||
// Remove the focus styling from the active menu.
|
||||
item.removeClass('menu-focus');
|
||||
parentLI.removeClass('menu-focus');
|
||||
|
||||
menuIndex = this.rootMenus.index(parentLI);
|
||||
|
||||
if (menuIndex > 0) {
|
||||
// Move to the previous root-level menu.
|
||||
newItem = parentLI.prev();
|
||||
} else {
|
||||
// Loop to last root-level menu.
|
||||
newItem = this.rootMenus.last();
|
||||
}
|
||||
|
||||
// Add the focus styling to the new menu.
|
||||
newItem.addClass('menu-focus');
|
||||
|
||||
if (newItem.attr('aria-haspopup') == 'true') {
|
||||
childMenu = newItem.children('ul').first();
|
||||
|
||||
// Show the child menu and update it's aria attributes.
|
||||
this.openSubMenu(childMenu);
|
||||
this.isChildOpen = true;
|
||||
|
||||
newItem = childMenu.children('li').first();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newItem;
|
||||
};
|
||||
|
||||
/**
|
||||
* Member function to select the next item in a menu.
|
||||
* If the active item is the last in the menu, this function will loop to the
|
||||
* first menu item.
|
||||
*
|
||||
* @method moveDown
|
||||
* @param {Object} item is the active menu item
|
||||
* @param {String} startChr is the character to attempt to match against the beginning of the
|
||||
* menu item titles. If found, focus moves to the next menu item beginning with that character.
|
||||
* @return {Object} Returns the item to move to. Returns item is no move is possible
|
||||
*/
|
||||
Menubar.prototype.moveDown = function(item, startChr) {
|
||||
// Item's containing menu.
|
||||
var itemUL = item.parent();
|
||||
// The items in the currently active menu.
|
||||
var menuItems = itemUL.children('li').not('.separator');
|
||||
// The number of items in the active menu.
|
||||
var menuNum = menuItems.length;
|
||||
// The items index in its menu.
|
||||
var menuIndex = menuItems.index(item);
|
||||
var newItem = null;
|
||||
var newItemUL = null;
|
||||
|
||||
if (itemUL.is('.tool-lp-menu')) {
|
||||
// This is the root level menu.
|
||||
|
||||
if (item.attr('aria-haspopup') != 'true') {
|
||||
// No child menu to move to.
|
||||
return item;
|
||||
}
|
||||
|
||||
// Move to the first item in the child menu.
|
||||
newItemUL = item.children('ul').first();
|
||||
newItem = newItemUL.children('li').first();
|
||||
|
||||
// Make sure the child menu is visible.
|
||||
this.openSubMenu(newItemUL);
|
||||
|
||||
return newItem;
|
||||
}
|
||||
|
||||
// If $item is not the last item in its menu, move to the next item. If startChr is specified, move
|
||||
// to the next item with a title that begins with that character.
|
||||
if (startChr) {
|
||||
var match = false;
|
||||
var curNdx = menuIndex+1;
|
||||
|
||||
// Check if the active item was the last one on the list.
|
||||
if (curNdx == menuNum) {
|
||||
curNdx = 0;
|
||||
}
|
||||
|
||||
// Iterate through the menu items (starting from the current item and wrapping) until a match is found
|
||||
// or the loop returns to the current menu item.
|
||||
while (curNdx != menuIndex) {
|
||||
|
||||
var titleChr = menuItems.eq(curNdx).html().charAt(0);
|
||||
|
||||
if (titleChr.toLowerCase() == startChr) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
|
||||
curNdx = curNdx+1;
|
||||
|
||||
if (curNdx == menuNum) {
|
||||
// Reached the end of the list, start again at the beginning.
|
||||
curNdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (match === true) {
|
||||
newItem = menuItems.eq(curNdx);
|
||||
|
||||
// Remove the focus styling from the current item.
|
||||
item.removeClass('menu-focus');
|
||||
|
||||
return newItem;
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
} else {
|
||||
if (menuIndex < menuNum-1) {
|
||||
newItem = menuItems.eq(menuIndex+1);
|
||||
} else {
|
||||
newItem = menuItems.first();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the focus styling from the current item.
|
||||
item.removeClass('menu-focus');
|
||||
|
||||
return newItem;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function moveUp() is a member function to select the previous item in a menu.
|
||||
* If the active item is the first in the menu, this function will loop to the
|
||||
* last menu item.
|
||||
*
|
||||
* @method moveUp
|
||||
* @param {Object} item is the active menu item
|
||||
* @return {Object} Returns the item to move to. Returns item is no move is possible
|
||||
*/
|
||||
Menubar.prototype.moveUp = function(item) {
|
||||
// Item's containing menu.
|
||||
var itemUL = item.parent();
|
||||
// The items in the currently active menu.
|
||||
var menuItems = itemUL.children('li').not('.separator');
|
||||
// The items index in its menu.
|
||||
var menuIndex = menuItems.index(item);
|
||||
var newItem = null;
|
||||
|
||||
if (itemUL.is('.tool-lp-menu')) {
|
||||
// This is the root level menu.
|
||||
// Nothing to do.
|
||||
return item;
|
||||
}
|
||||
|
||||
// If item is not the first item in its menu, move to the previous item.
|
||||
if (menuIndex > 0) {
|
||||
newItem = menuItems.eq(menuIndex-1);
|
||||
} else {
|
||||
// Loop to top of menu.
|
||||
newItem = menuItems.last();
|
||||
}
|
||||
|
||||
// Remove the focus styling from the current item.
|
||||
item.removeClass('menu-focus');
|
||||
|
||||
return newItem;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enhance the dom with aria attributes.
|
||||
* @method addAriaAttributes
|
||||
*/
|
||||
Menubar.prototype.addAriaAttributes = function() {
|
||||
this.menuRoot.attr('role', 'menubar');
|
||||
this.rootMenus.attr('role', 'menuitem');
|
||||
this.rootMenus.attr('tabindex', '0');
|
||||
this.rootMenus.attr('aria-haspopup', 'true');
|
||||
this.subMenus.attr('role', 'menu');
|
||||
this.subMenus.attr('aria-hidden', 'true');
|
||||
this.subMenuItems.attr('role', 'menuitem');
|
||||
this.subMenuItems.attr('tabindex', '-1');
|
||||
|
||||
// For CSS styling and effects.
|
||||
this.menuRoot.addClass('tool-lp-menu');
|
||||
this.allItems.addClass('tool-lp-menu-item');
|
||||
this.rootMenus.addClass('tool-lp-root-menu');
|
||||
this.subMenus.addClass('tool-lp-sub-menu');
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/menubar */ {
|
||||
/**
|
||||
* Create a menu bar object for every node matching the selector.
|
||||
*
|
||||
* The expected DOM structure is shown below.
|
||||
* <ul> <- This is the target of the selector parameter.
|
||||
* <li> <- This is repeated for each top level menu.
|
||||
* Text <- This is the text for the top level menu.
|
||||
* <ul> <- This is a list of the entries in this top level menu.
|
||||
* <li> <- This is repeated for each menu entry.
|
||||
* <a href="someurl">Choice 1</a> <- The anchor for the menu.
|
||||
* </li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @method enhance
|
||||
* @param {String} selector - The selector for the outer most menu node.
|
||||
* @param {Function} handler - Javascript handler for when a menu item was chosen. If the
|
||||
* handler returns true (or does not exist), the
|
||||
* menu will look for an anchor with a link to follow.
|
||||
* For example, if the menu entry has a "data-action" attribute
|
||||
* and we want to call a javascript function when that entry is chosen,
|
||||
* we could pass a list of handlers like this:
|
||||
* { "[data-action='add']" : callAddFunction }
|
||||
*/
|
||||
enhance: function(selector, handler) {
|
||||
$(selector).each(function (index, element) {
|
||||
var menuRoot = $(element);
|
||||
// Don't enhance the same menu twice.
|
||||
if (menuRoot.data("menubarEnhanced") !== true) {
|
||||
(new Menubar(menuRoot, handler));
|
||||
menuRoot.data("menubarEnhanced", true);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handy function to close all open menus anywhere on the page.
|
||||
* @method closeAll
|
||||
*/
|
||||
closeAll: closeAllSubMenus
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,207 @@
|
||||
// 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 selecting parent competency in competency form.
|
||||
*
|
||||
* @module tool_lp/parentcompetency_form
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Issam Taboubi <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/ajax', 'core/str', 'tool_lp/competencypicker', 'core/templates', 'core/notification'],
|
||||
function($, ajax, Str, Picker, Templates, Notification) {
|
||||
|
||||
/**
|
||||
* Parent Competency Form object.
|
||||
* @param {String} buttonSelector The parent competency button selector.
|
||||
* @param {String} inputHiddenSelector The hidden input field selector.
|
||||
* @param {String} staticElementSelector The static element displaying the parent competency.
|
||||
* @param {Number} frameworkId The competency framework ID.
|
||||
* @param {Number} frameworkMaxLevel The framework max level.
|
||||
* @param {Number} pageContextId The page context ID.
|
||||
*/
|
||||
var ParentCompetencyForm = function(buttonSelector,
|
||||
inputHiddenSelector,
|
||||
staticElementSelector,
|
||||
frameworkId,
|
||||
frameworkMaxLevel,
|
||||
pageContextId) {
|
||||
this.buttonSelector = buttonSelector;
|
||||
this.inputHiddenSelector = inputHiddenSelector;
|
||||
this.staticElementSelector = staticElementSelector;
|
||||
this.frameworkId = frameworkId;
|
||||
this.frameworkMaxLevel = frameworkMaxLevel;
|
||||
this.pageContextId = pageContextId;
|
||||
|
||||
// Register the events.
|
||||
this.registerEvents();
|
||||
};
|
||||
|
||||
/** @var {String} The parent competency button selector. */
|
||||
ParentCompetencyForm.prototype.buttonSelector = null;
|
||||
/** @var {String} The hidden input field selector. */
|
||||
ParentCompetencyForm.prototype.inputHiddenSelector = null;
|
||||
/** @var {String} The static element displaying the parent competency. */
|
||||
ParentCompetencyForm.prototype.staticElementSelector = null;
|
||||
/** @var {Number} The competency framework ID. */
|
||||
ParentCompetencyForm.prototype.frameworkId = null;
|
||||
/** @var {Number} The framework max level. */
|
||||
ParentCompetencyForm.prototype.frameworkMaxLevel = null;
|
||||
/** @var {Number} The page context ID. */
|
||||
ParentCompetencyForm.prototype.pageContextId = null;
|
||||
|
||||
/**
|
||||
* Set the parent competency in the competency form.
|
||||
*
|
||||
* @param {Object} Data containing selected cmpetency.
|
||||
* @method setParent
|
||||
*/
|
||||
ParentCompetencyForm.prototype.setParent = function(data) {
|
||||
var self = this;
|
||||
|
||||
if (data.competencyId !== 0) {
|
||||
ajax.call([
|
||||
{ methodname: 'core_competency_read_competency', args: {
|
||||
id: data.competencyId
|
||||
}}
|
||||
])[0].done(function(competency) {
|
||||
$(self.staticElementSelector).html(competency.shortname);
|
||||
$(self.inputHiddenSelector).val(competency.id);
|
||||
}).fail(Notification.exception);
|
||||
} else {
|
||||
// Root of competency framework selected.
|
||||
Str.get_string('competencyframeworkroot', 'tool_lp').then(function(rootframework) {
|
||||
$(self.staticElementSelector).html(rootframework);
|
||||
$(self.inputHiddenSelector).val(data.competencyId);
|
||||
}).fail(Notification.exception);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the events of parent competency button click.
|
||||
*
|
||||
* @method registerEvents
|
||||
*/
|
||||
ParentCompetencyForm.prototype.registerEvents = function() {
|
||||
var self = this;
|
||||
|
||||
// Event on edit parent button.
|
||||
$(self.buttonSelector).on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var picker = new Picker(self.pageContextId, self.frameworkId, 'self', false);
|
||||
var maxlevel = self.frameworkMaxLevel;
|
||||
// Override the fetchcompetencies method to filter by max level.
|
||||
picker._fetchCompetencies = function(frameworkId, searchText) {
|
||||
var self = this;
|
||||
|
||||
return ajax.call([
|
||||
{ methodname: 'core_competency_search_competencies', args: {
|
||||
searchtext: searchText,
|
||||
competencyframeworkid: frameworkId
|
||||
}}
|
||||
])[0].done(function(competencies) {
|
||||
|
||||
var disabledcompetencies = [];
|
||||
function addCompetencyChildren(parent, competencies) {
|
||||
for (var i = 0; i < competencies.length; i++) {
|
||||
// Check if competency does not exceed the framework max level.
|
||||
var path = String(competencies[i].path),
|
||||
level = path.split('/').length - 2;
|
||||
if (level >= maxlevel && competencies[i].id !== "0") {
|
||||
disabledcompetencies.push(competencies[i].id);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expand the list of competencies into a tree.
|
||||
var i, tree = [], comp;
|
||||
for (i = 0; i < competencies.length; i++) {
|
||||
comp = competencies[i];
|
||||
if (comp.parentid == "0") { // Loose check for now, because WS returns a string.
|
||||
comp.children = [];
|
||||
comp.haschildren = 0;
|
||||
tree[tree.length] = comp;
|
||||
addCompetencyChildren(comp, competencies);
|
||||
}
|
||||
}
|
||||
|
||||
self._competencies = tree;
|
||||
self.setDisallowedCompetencyIDs(disabledcompetencies);
|
||||
|
||||
}.bind(self)).fail(Notification.exception);
|
||||
};
|
||||
// Override the render method to make framework selectable.
|
||||
picker._render = function() {
|
||||
var self = this;
|
||||
return self._preRender().then(function() {
|
||||
var context = {
|
||||
competencies: self._competencies,
|
||||
framework: self._getFramework(self._frameworkId),
|
||||
frameworks: self._frameworks,
|
||||
search: self._searchText,
|
||||
singleFramework: self._singleFramework,
|
||||
};
|
||||
|
||||
return Templates.render('tool_lp/competency_picker_competencyform', context);
|
||||
}.bind(self));
|
||||
};
|
||||
|
||||
// On selected competency.
|
||||
picker.on('save', function(e, data) {
|
||||
self.setParent(data);
|
||||
}.bind(self));
|
||||
|
||||
picker.display();
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* Main initialisation.
|
||||
* @param {String} buttonSelector The parent competency button selector.
|
||||
* @param {String} inputHiddenSelector The hidden input field selector.
|
||||
* @param {String} staticElementSelector The static element displaying the parent competency.
|
||||
* @param {Number} frameworkId The competency framework ID.
|
||||
* @param {Number} frameworkMaxLevel The framework max level.
|
||||
* @param {Number} pageContextId The page context ID.
|
||||
* @method init
|
||||
*/
|
||||
init: function(buttonSelector,
|
||||
inputSelector,
|
||||
staticElementSelector,
|
||||
frameworkId,
|
||||
frameworkMaxLevel,
|
||||
pageContextId) {
|
||||
// Create instance.
|
||||
new ParentCompetencyForm(buttonSelector,
|
||||
inputSelector,
|
||||
staticElementSelector,
|
||||
frameworkId,
|
||||
frameworkMaxLevel,
|
||||
pageContextId);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,600 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Plan actions via ajax.
|
||||
*
|
||||
* @module tool_lp/planactions
|
||||
* @package tool_lp
|
||||
* @copyright 2015 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery',
|
||||
'core/templates',
|
||||
'core/ajax',
|
||||
'core/notification',
|
||||
'core/str',
|
||||
'tool_lp/menubar',
|
||||
'tool_lp/dialogue'],
|
||||
function($, templates, ajax, notification, str, Menubar, Dialogue) {
|
||||
|
||||
/**
|
||||
* PlanActions class.
|
||||
*
|
||||
* Note that presently this cannot be instantiated more than once per page.
|
||||
*
|
||||
* @param {String} type The type of page we're in.
|
||||
*/
|
||||
var PlanActions = function(type) {
|
||||
this._type = type;
|
||||
|
||||
if (type === 'plan') {
|
||||
// This is the page to view one plan.
|
||||
this._region = '[data-region="plan-page"]';
|
||||
this._planNode = '[data-region="plan-page"]';
|
||||
this._template = 'tool_lp/plan_page';
|
||||
this._contextMethod = 'tool_lp_data_for_plan_page';
|
||||
|
||||
} else if (type === 'plans') {
|
||||
// This is the page to view a list of plans.
|
||||
this._region = '[data-region="plans"]';
|
||||
this._planNode = '[data-region="plan-node"]';
|
||||
this._template = 'tool_lp/plans_page';
|
||||
this._contextMethod = 'tool_lp_data_for_plans_page';
|
||||
|
||||
} else {
|
||||
throw new TypeError('Unexpected type.');
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {String} Ajax method to fetch the page data from. */
|
||||
PlanActions.prototype._contextMethod = null;
|
||||
/** @type {String} Selector to find the node describing the plan. */
|
||||
PlanActions.prototype._planNode = null;
|
||||
/** @type {String} Selector mapping to the region to update. Usually similar to wrapper. */
|
||||
PlanActions.prototype._region = null;
|
||||
/** @type {String} Name of the template used to render the region. */
|
||||
PlanActions.prototype._template = null;
|
||||
/** @type {String} Type of page/region we're in. */
|
||||
PlanActions.prototype._type = null;
|
||||
|
||||
/**
|
||||
* Resolve the arguments to refresh the region.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @return {Object} List of arguments.
|
||||
*/
|
||||
PlanActions.prototype._getContextArgs = function(planData) {
|
||||
var self = this,
|
||||
args = {};
|
||||
|
||||
if (self._type === 'plan') {
|
||||
args = {
|
||||
planid: planData.id
|
||||
};
|
||||
|
||||
} else if (self._type === 'plans') {
|
||||
args = {
|
||||
userid: planData.userid
|
||||
};
|
||||
}
|
||||
|
||||
return args;
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh the plan view.
|
||||
*
|
||||
* This is useful when you only want to refresh the view.
|
||||
*
|
||||
* @param {String} selector The node to search the plan data from.
|
||||
*/
|
||||
PlanActions.prototype.refresh = function(selector) {
|
||||
var planData = this._findPlanData($(selector));
|
||||
this._callAndRefresh([], planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to render the region template.
|
||||
*
|
||||
* @param {Object} context The context for the template.
|
||||
*/
|
||||
PlanActions.prototype._renderView = function(context) {
|
||||
var self = this;
|
||||
templates.render(self._template, context)
|
||||
.done(function(newhtml, newjs) {
|
||||
$(self._region).replaceWith(newhtml);
|
||||
templates.runTemplateJS(newjs);
|
||||
}.bind(self))
|
||||
.fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Call multiple ajax methods, and refresh.
|
||||
*
|
||||
* @param {Array} calls List of Ajax calls.
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @return {Promise}
|
||||
*/
|
||||
PlanActions.prototype._callAndRefresh = function(calls, planData) {
|
||||
var self = this;
|
||||
|
||||
calls.push({
|
||||
methodname: self._contextMethod,
|
||||
args: self._getContextArgs(planData)
|
||||
});
|
||||
|
||||
// Apply all the promises, and refresh when the last one is resolved.
|
||||
return $.when.apply($.when, ajax.call(calls))
|
||||
.then(function() {
|
||||
self._renderView.call(self, arguments[arguments.length - 1]);
|
||||
})
|
||||
.fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a plan and reload the region.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
*/
|
||||
PlanActions.prototype._doDelete = function(planData) {
|
||||
var self = this,
|
||||
calls = [{
|
||||
methodname: 'core_competency_delete_plan',
|
||||
args: { id: planData.id }
|
||||
}];
|
||||
self._callAndRefresh(calls, planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
*/
|
||||
PlanActions.prototype.deletePlan = function(planData) {
|
||||
var self = this,
|
||||
requests;
|
||||
|
||||
requests = ajax.call([{
|
||||
methodname: 'core_competency_read_plan',
|
||||
args: { id: planData.id }
|
||||
}]);
|
||||
|
||||
requests[0].done(function(plan) {
|
||||
str.get_strings([
|
||||
{ key: 'confirm', component: 'moodle' },
|
||||
{ key: 'deleteplan', component: 'tool_lp', param: plan.name },
|
||||
{ key: 'delete', component: 'moodle' },
|
||||
{ key: 'cancel', component: 'moodle' }
|
||||
]).done(function (strings) {
|
||||
notification.confirm(
|
||||
strings[0], // Confirm.
|
||||
strings[1], // Delete plan X?
|
||||
strings[2], // Delete.
|
||||
strings[3], // Cancel.
|
||||
function() {
|
||||
self._doDelete(planData);
|
||||
}.bind(self)
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Reopen plan and reload the region.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
*/
|
||||
PlanActions.prototype._doReopenPlan = function(planData) {
|
||||
var self = this,
|
||||
calls = [{
|
||||
methodname: 'core_competency_reopen_plan',
|
||||
args: { planid: planData.id}
|
||||
}];
|
||||
self._callAndRefresh(calls, planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reopen a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
*/
|
||||
PlanActions.prototype.reopenPlan = function(planData) {
|
||||
var self = this,
|
||||
requests = ajax.call([{
|
||||
methodname: 'core_competency_read_plan',
|
||||
args: { id: planData.id }
|
||||
}]);
|
||||
|
||||
requests[0].done(function(plan) {
|
||||
str.get_strings([
|
||||
{ key: 'confirm', component: 'moodle' },
|
||||
{ key: 'reopenplanconfirm', component: 'tool_lp', param: plan.name },
|
||||
{ key: 'reopenplan', component: 'tool_lp' },
|
||||
{ key: 'cancel', component: 'moodle' }
|
||||
]).done(function (strings) {
|
||||
notification.confirm(
|
||||
strings[0], // Confirm.
|
||||
strings[1], // Reopen plan X?
|
||||
strings[2], // reopen.
|
||||
strings[3], // Cancel.
|
||||
function() {
|
||||
self._doReopenPlan(planData);
|
||||
}.bind(self)
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Complete plan and reload the region.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
*/
|
||||
PlanActions.prototype._doCompletePlan = function(planData) {
|
||||
var self = this,
|
||||
calls = [{
|
||||
methodname: 'core_competency_complete_plan',
|
||||
args: { planid: planData.id}
|
||||
}];
|
||||
self._callAndRefresh(calls, planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Complete a plan process.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
*/
|
||||
PlanActions.prototype.completePlan = function(planData) {
|
||||
var self = this,
|
||||
requests = ajax.call([{
|
||||
methodname: 'core_competency_read_plan',
|
||||
args: { id: planData.id }
|
||||
}]);
|
||||
|
||||
requests[0].done(function(plan) {
|
||||
str.get_strings([
|
||||
{ key: 'confirm', component: 'moodle' },
|
||||
{ key: 'completeplanconfirm', component: 'tool_lp', param: plan.name },
|
||||
{ key: 'completeplan', component: 'tool_lp' },
|
||||
{ key: 'cancel', component: 'moodle' }
|
||||
]).done(function (strings) {
|
||||
notification.confirm(
|
||||
strings[0], // Confirm.
|
||||
strings[1], // Complete plan X?
|
||||
strings[2], // Complete.
|
||||
strings[3], // Cancel.
|
||||
function() {
|
||||
self._doCompletePlan(planData);
|
||||
}.bind(self)
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unlink plan and reload the region.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
*/
|
||||
PlanActions.prototype._doUnlinkPlan = function(planData) {
|
||||
var self = this,
|
||||
calls = [{
|
||||
methodname: 'core_competency_unlink_plan_from_template',
|
||||
args: { planid: planData.id}
|
||||
}];
|
||||
self._callAndRefresh(calls, planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unlink a plan process.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
*/
|
||||
PlanActions.prototype.unlinkPlan = function(planData) {
|
||||
var self = this,
|
||||
requests = ajax.call([{
|
||||
methodname: 'core_competency_read_plan',
|
||||
args: { id: planData.id }
|
||||
}]);
|
||||
|
||||
requests[0].done(function(plan) {
|
||||
str.get_strings([
|
||||
{ key: 'confirm', component: 'moodle' },
|
||||
{ key: 'unlinkplantemplateconfirm', component: 'tool_lp', param: plan.name },
|
||||
{ key: 'unlinkplantemplate', component: 'tool_lp' },
|
||||
{ key: 'cancel', component: 'moodle' }
|
||||
]).done(function (strings) {
|
||||
notification.confirm(
|
||||
strings[0], // Confirm.
|
||||
strings[1], // Unlink plan X?
|
||||
strings[2], // Unlink.
|
||||
strings[3], // Cancel.
|
||||
function() {
|
||||
self._doUnlinkPlan(planData);
|
||||
}.bind(self)
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Request review of a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method _doRequestReview
|
||||
*/
|
||||
PlanActions.prototype._doRequestReview = function(planData) {
|
||||
var calls = [{
|
||||
methodname: 'core_competency_plan_request_review',
|
||||
args: {
|
||||
id: planData.id
|
||||
}
|
||||
}];
|
||||
this._callAndRefresh(calls, planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Request review of a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method requestReview
|
||||
*/
|
||||
PlanActions.prototype.requestReview = function(planData) {
|
||||
this._doRequestReview(planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel review request of a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method _doCancelReviewRequest
|
||||
*/
|
||||
PlanActions.prototype._doCancelReviewRequest = function(planData) {
|
||||
var calls = [{
|
||||
methodname: 'core_competency_plan_cancel_review_request',
|
||||
args: {
|
||||
id: planData.id
|
||||
}
|
||||
}];
|
||||
this._callAndRefresh(calls, planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel review request of a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method cancelReviewRequest
|
||||
*/
|
||||
PlanActions.prototype.cancelReviewRequest = function(planData) {
|
||||
this._doCancelReviewRequest(planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start review of a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method _doStartReview
|
||||
*/
|
||||
PlanActions.prototype._doStartReview = function(planData) {
|
||||
var calls = [{
|
||||
methodname: 'core_competency_plan_start_review',
|
||||
args: {
|
||||
id: planData.id
|
||||
}
|
||||
}];
|
||||
this._callAndRefresh(calls, planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start review of a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method startReview
|
||||
*/
|
||||
PlanActions.prototype.startReview = function(planData) {
|
||||
this._doStartReview(planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop review of a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method _doStopReview
|
||||
*/
|
||||
PlanActions.prototype._doStopReview = function(planData) {
|
||||
var calls = [{
|
||||
methodname: 'core_competency_plan_stop_review',
|
||||
args: {
|
||||
id: planData.id
|
||||
}
|
||||
}];
|
||||
this._callAndRefresh(calls, planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop review of a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method stopReview
|
||||
*/
|
||||
PlanActions.prototype.stopReview = function(planData) {
|
||||
this._doStopReview(planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Approve a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method _doApprove
|
||||
*/
|
||||
PlanActions.prototype._doApprove = function(planData) {
|
||||
var calls = [{
|
||||
methodname: 'core_competency_approve_plan',
|
||||
args: {
|
||||
id: planData.id
|
||||
}
|
||||
}];
|
||||
this._callAndRefresh(calls, planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Approve a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method approve
|
||||
*/
|
||||
PlanActions.prototype.approve = function(planData) {
|
||||
this._doApprove(planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unapprove a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method _doUnapprove
|
||||
*/
|
||||
PlanActions.prototype._doUnapprove = function(planData) {
|
||||
var calls = [{
|
||||
methodname: 'core_competency_unapprove_plan',
|
||||
args: {
|
||||
id: planData.id
|
||||
}
|
||||
}];
|
||||
this._callAndRefresh(calls, planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unapprove a plan.
|
||||
*
|
||||
* @param {Object} planData Plan data from plan node.
|
||||
* @method unapprove
|
||||
*/
|
||||
PlanActions.prototype.unapprove = function(planData) {
|
||||
this._doUnapprove(planData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Display list of linked courses on a modal dialogue.
|
||||
*
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
PlanActions.prototype._showLinkedCoursesHandler = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var competencyid = $(e.target).data('id');
|
||||
var requests = ajax.call([{
|
||||
methodname: 'tool_lp_list_courses_using_competency',
|
||||
args: { id: competencyid }
|
||||
}]);
|
||||
|
||||
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.
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Plan event handler.
|
||||
*
|
||||
* @param {String} method The method to call.
|
||||
* @param {Event} e The event.
|
||||
* @method _eventHandler
|
||||
*/
|
||||
PlanActions.prototype._eventHandler = function(method, e) {
|
||||
e.preventDefault();
|
||||
var data = this._findPlanData($(e.target));
|
||||
this[method](data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the plan data from the plan node.
|
||||
*
|
||||
* @param {Node} node The node to search from.
|
||||
* @return {Object} Plan data.
|
||||
*/
|
||||
PlanActions.prototype._findPlanData = function(node) {
|
||||
var parent = node.parentsUntil($(this._region).parent(), this._planNode),
|
||||
data;
|
||||
|
||||
if (parent.length != 1) {
|
||||
throw new Error('The plan node was not located.');
|
||||
}
|
||||
|
||||
data = parent.data();
|
||||
if (typeof data === 'undefined' || typeof data.id === 'undefined') {
|
||||
throw new Error('Plan data could not be found.');
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enhance a menu bar.
|
||||
*
|
||||
* @param {String} selector Menubar selector.
|
||||
*/
|
||||
PlanActions.prototype.enhanceMenubar = function(selector) {
|
||||
Menubar.enhance(selector, {
|
||||
'[data-action="plan-delete"]': this._eventHandler.bind(this, 'deletePlan'),
|
||||
'[data-action="plan-complete"]': this._eventHandler.bind(this, 'completePlan'),
|
||||
'[data-action="plan-reopen"]': this._eventHandler.bind(this, 'reopenPlan'),
|
||||
'[data-action="plan-unlink"]': this._eventHandler.bind(this, 'unlinkPlan'),
|
||||
'[data-action="plan-request-review"]': this._eventHandler.bind(this, 'requestReview'),
|
||||
'[data-action="plan-cancel-review-request"]': this._eventHandler.bind(this, 'cancelReviewRequest'),
|
||||
'[data-action="plan-start-review"]': this._eventHandler.bind(this, 'startReview'),
|
||||
'[data-action="plan-stop-review"]': this._eventHandler.bind(this, 'stopReview'),
|
||||
'[data-action="plan-approve"]': this._eventHandler.bind(this, 'approve'),
|
||||
'[data-action="plan-unapprove"]': this._eventHandler.bind(this, 'unapprove'),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the events in the region.
|
||||
*
|
||||
* At this stage this cannot be used with enhanceMenubar or multiple handlers
|
||||
* will be added to the same node.
|
||||
*/
|
||||
PlanActions.prototype.registerEvents = function() {
|
||||
var wrapper = $(this._region);
|
||||
|
||||
wrapper.find('[data-action="plan-delete"]').click(this._eventHandler.bind(this, 'deletePlan'));
|
||||
wrapper.find('[data-action="plan-complete"]').click(this._eventHandler.bind(this, 'completePlan'));
|
||||
wrapper.find('[data-action="plan-reopen"]').click(this._eventHandler.bind(this, 'reopenPlan'));
|
||||
wrapper.find('[data-action="plan-unlink"]').click(this._eventHandler.bind(this, 'unlinkPlan'));
|
||||
|
||||
wrapper.find('[data-action="plan-request-review"]').click(this._eventHandler.bind(this, 'requestReview'));
|
||||
wrapper.find('[data-action="plan-cancel-review-request"]').click(this._eventHandler.bind(this, 'cancelReviewRequest'));
|
||||
wrapper.find('[data-action="plan-start-review"]').click(this._eventHandler.bind(this, 'startReview'));
|
||||
wrapper.find('[data-action="plan-stop-review"]').click(this._eventHandler.bind(this, 'stopReview'));
|
||||
wrapper.find('[data-action="plan-approve"]').click(this._eventHandler.bind(this, 'approve'));
|
||||
wrapper.find('[data-action="plan-unapprove"]').click(this._eventHandler.bind(this, 'unapprove'));
|
||||
|
||||
wrapper.find('[data-action="find-courses-link"]').click(this._showLinkedCoursesHandler.bind(this));
|
||||
};
|
||||
|
||||
return PlanActions;
|
||||
});
|
||||
@@ -0,0 +1,228 @@
|
||||
// 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 opening a dialogue to configure scale data.
|
||||
*
|
||||
* @module tool_lp/scaleconfig
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Adrian Greeve <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/notification', 'core/templates', 'core/ajax', 'tool_lp/dialogue', 'tool_lp/scalevalues'],
|
||||
function($, notification, templates, ajax, Dialogue, ModScaleValues) {
|
||||
|
||||
/**
|
||||
* Scale config object.
|
||||
* @param {String} selectSelector The select box selector.
|
||||
* @param {String} inputSelector The hidden input field selector.
|
||||
* @param {String} triggerSelector The trigger selector.
|
||||
*/
|
||||
var ScaleConfig = function(selectSelector, inputSelector, triggerSelector) {
|
||||
this.selectSelector = selectSelector;
|
||||
this.inputSelector = inputSelector;
|
||||
this.triggerSelector = triggerSelector;
|
||||
|
||||
// Get the current scale ID.
|
||||
this.originalscaleid = $(selectSelector).val();
|
||||
$(selectSelector).on('change', this.scaleChangeHandler.bind(this)).change();
|
||||
$(triggerSelector).click(this.showConfig.bind(this));
|
||||
};
|
||||
|
||||
/** @var {String} The select box selector. */
|
||||
ScaleConfig.prototype.selectSelector = null;
|
||||
/** @var {String} The hidden field selector. */
|
||||
ScaleConfig.prototype.inputSelector = null;
|
||||
/** @var {String} The trigger selector. */
|
||||
ScaleConfig.prototype.triggerSelector = null;
|
||||
/** @var {Array} scalevalues ID and name of the scales. */
|
||||
ScaleConfig.prototype.scalevalues = null;
|
||||
/** @var {Number) originalscaleid Original scale ID when the page loads. */
|
||||
ScaleConfig.prototype.originalscaleid = 0;
|
||||
/** @var {Number} scaleid Current scale ID. */
|
||||
ScaleConfig.prototype.scaleid = 0;
|
||||
/** @var {Dialogue} Reference to the popup. */
|
||||
ScaleConfig.prototype.popup = null;
|
||||
|
||||
/**
|
||||
* Displays the scale configuration dialogue.
|
||||
*
|
||||
* @method showConfig
|
||||
*/
|
||||
ScaleConfig.prototype.showConfig = function() {
|
||||
var self = this;
|
||||
|
||||
this.scaleid = $(this.selectSelector).val();
|
||||
if (this.scaleid <= 0) {
|
||||
// This should not happen.
|
||||
return;
|
||||
}
|
||||
|
||||
var scalename = $(this.selectSelector).find("option:selected").text();
|
||||
this.getScaleValues(this.scaleid).done(function() {
|
||||
|
||||
var context = {
|
||||
scalename: scalename,
|
||||
scales: self.scalevalues
|
||||
};
|
||||
|
||||
// Dish up the form.
|
||||
templates.render('tool_lp/scale_configuration_page', context)
|
||||
.done(function(html) {
|
||||
new Dialogue(
|
||||
scalename,
|
||||
html,
|
||||
self.initScaleConfig.bind(self)
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the original scale configuration if it was set.
|
||||
*
|
||||
* @method retrieveOriginalScaleConfig
|
||||
* @return {Object|String} scale configuration or empty string.
|
||||
*/
|
||||
ScaleConfig.prototype.retrieveOriginalScaleConfig = function() {
|
||||
var jsonstring = $(this.inputSelector).val();
|
||||
if (jsonstring !== '') {
|
||||
var scaleconfiguration = $.parseJSON(jsonstring);
|
||||
// The first object should contain the scale ID for the configuration.
|
||||
var scaledetail = scaleconfiguration.shift();
|
||||
// Check that this scale id matches the one from the page before returning the configuration.
|
||||
if (scaledetail.scaleid === this.originalscaleid) {
|
||||
return scaleconfiguration;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialises the scale configuration dialogue.
|
||||
*
|
||||
* @method initScaleConfig
|
||||
* @param {Dialogue} popup Dialogue object to initialise.
|
||||
*/
|
||||
ScaleConfig.prototype.initScaleConfig = function(popup) {
|
||||
this.popup = popup;
|
||||
var body = $(popup.getContent());
|
||||
if (this.originalscaleid === this.scaleid) {
|
||||
// Set up the popup to show the current configuration.
|
||||
var currentconfig = this.retrieveOriginalScaleConfig();
|
||||
// Set up the form only if there is configuration settings to set.
|
||||
if (currentconfig !== '') {
|
||||
currentconfig.forEach(function(value) {
|
||||
if (value.scaledefault === 1) {
|
||||
body.find('[data-field="tool_lp_scale_default_' + value.id + '"]').attr('checked', true);
|
||||
}
|
||||
if (value.proficient === 1) {
|
||||
body.find('[data-field="tool_lp_scale_proficient_' + value.id + '"]').attr('checked', true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
body.on('click', '[data-action="close"]', function() {
|
||||
this.setScaleConfig();
|
||||
popup.close();
|
||||
}.bind(this));
|
||||
body.on('click', '[data-action="cancel"]', function() {
|
||||
popup.close();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the scale configuration back into a JSON string in the hidden element.
|
||||
*
|
||||
* @method setScaleConfig
|
||||
*/
|
||||
ScaleConfig.prototype.setScaleConfig = function() {
|
||||
var body = $(this.popup.getContent());
|
||||
// Get the data.
|
||||
var data = [{ scaleid: this.scaleid}];
|
||||
this.scalevalues.forEach(function(value) {
|
||||
var scaledefault = 0;
|
||||
var proficient = 0;
|
||||
if (body.find('[data-field="tool_lp_scale_default_' + value.id + '"]').is(':checked')) {
|
||||
scaledefault = 1;
|
||||
}
|
||||
if (body.find('[data-field="tool_lp_scale_proficient_' + value.id + '"]').is(':checked')) {
|
||||
proficient = 1;
|
||||
}
|
||||
|
||||
if (!scaledefault && !proficient) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.push({
|
||||
id: value.id,
|
||||
scaledefault: scaledefault,
|
||||
proficient: proficient
|
||||
});
|
||||
});
|
||||
var datastring = JSON.stringify(data);
|
||||
// Send to the hidden field on the form.
|
||||
$(this.inputSelector).val(datastring);
|
||||
// Once the configuration has been saved then the original scale ID is set to the current scale ID.
|
||||
this.originalscaleid = this.scaleid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the scale values for the selected scale.
|
||||
*
|
||||
* @method getScaleValues
|
||||
* @param {Number} scaleid The scale ID of the selected scale.
|
||||
* @return {Promise} A deffered object with the scale values.
|
||||
*/
|
||||
ScaleConfig.prototype.getScaleValues = function(scaleid) {
|
||||
return ModScaleValues.get_values(scaleid).then(function(values) {
|
||||
this.scalevalues = values;
|
||||
return values;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Triggered when a scale is selected.
|
||||
*
|
||||
* @name scaleChangeHandler
|
||||
* @param {Event} e
|
||||
* @return {Void}
|
||||
* @function
|
||||
*/
|
||||
ScaleConfig.prototype.scaleChangeHandler = function(e) {
|
||||
if ($(e.target).val() <= 0) {
|
||||
$(this.triggerSelector).prop('disabled', true);
|
||||
} else {
|
||||
$(this.triggerSelector).prop('disabled', false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* Main initialisation.
|
||||
*
|
||||
* @param {String} selectSelector The select box selector.
|
||||
* @param {String} inputSelector The hidden input field selector.
|
||||
* @param {String} triggerSelector The trigger selector.
|
||||
* @return {ScaleConfig} A new instance of ScaleConfig.
|
||||
* @method init
|
||||
*/
|
||||
init: function(selectSelector, inputSelector, triggerSelector) {
|
||||
return new ScaleConfig(selectSelector, inputSelector, triggerSelector);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Module to get the scale values.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Serge Gauthier
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/ajax'], function($, ajax) {
|
||||
var localCache = [];
|
||||
|
||||
return /** @alias module:tool_lp/scalevalues */ {
|
||||
|
||||
/**
|
||||
* Return a promise object that will be resolved into a string eventually (maybe immediately).
|
||||
*
|
||||
* @method get_values
|
||||
* @param {Number} scaleid The scale id
|
||||
* @return [] {Promise}
|
||||
*/
|
||||
|
||||
get_values: function(scaleid) {
|
||||
|
||||
var deferred = $.Deferred();
|
||||
|
||||
if (typeof localCache[scaleid] === 'undefined') {
|
||||
ajax.call([{
|
||||
methodname: 'core_competency_get_scale_values',
|
||||
args: {scaleid : scaleid},
|
||||
done: function(scaleinfo) {
|
||||
localCache[scaleid] = scaleinfo;
|
||||
deferred.resolve(scaleinfo);
|
||||
},
|
||||
fail: (deferred.reject)
|
||||
}]);
|
||||
} else {
|
||||
deferred.resolve(localCache[scaleid]);
|
||||
}
|
||||
|
||||
return deferred.promise();
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,202 @@
|
||||
// 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 actions on learning plan templates via ajax.
|
||||
*
|
||||
* @module tool_lp/templateactions
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str','tool_lp/actionselector'],
|
||||
function($, templates, ajax, notification, str, Actionselector) {
|
||||
// Private variables and functions.
|
||||
|
||||
/** @var {Number} pagecontextid The id of the context */
|
||||
var pagecontextid = 0;
|
||||
|
||||
/** @var {Number} templateid The id of the template */
|
||||
var templateid = 0;
|
||||
|
||||
/** @var {Boolean} Action to apply to plans when deleting a template */
|
||||
var deleteplans = true;
|
||||
|
||||
/**
|
||||
* Callback to replace the dom element with the rendered template.
|
||||
*
|
||||
* @method updatePage
|
||||
* @param {String} newhtml The new html to insert.
|
||||
* @param {String} newjs The new js to run.
|
||||
*/
|
||||
var updatePage = function(newhtml, newjs) {
|
||||
$('[data-region="managetemplates"]').replaceWith(newhtml);
|
||||
templates.runTemplateJS(newjs);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to render the page template again and update the page.
|
||||
*
|
||||
* @method reloadList
|
||||
* @param {Object} context The context for the template.
|
||||
*/
|
||||
var reloadList = function(context) {
|
||||
templates.render('tool_lp/manage_templates_page', context)
|
||||
.done(updatePage)
|
||||
.fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a template and reload the page.
|
||||
* @method doDelete
|
||||
*/
|
||||
var doDelete = function() {
|
||||
|
||||
// We are chaining ajax requests here.
|
||||
var requests = ajax.call([{
|
||||
methodname: 'core_competency_delete_template',
|
||||
args: { id: templateid,
|
||||
deleteplans: deleteplans }
|
||||
}, {
|
||||
methodname: 'tool_lp_data_for_templates_manage_page',
|
||||
args: {
|
||||
pagecontext: {
|
||||
contextid: pagecontextid
|
||||
}
|
||||
}
|
||||
}]);
|
||||
requests[1].done(reloadList).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Duplicate a template and reload the page.
|
||||
* @method doDuplicate
|
||||
* @param {Event} e
|
||||
*/
|
||||
var doDuplicate = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
templateid = $(this).attr('data-templateid');
|
||||
|
||||
// We are chaining ajax requests here.
|
||||
var requests = ajax.call([{
|
||||
methodname: 'core_competency_duplicate_template',
|
||||
args: { id: templateid }
|
||||
}, {
|
||||
methodname: 'tool_lp_data_for_templates_manage_page',
|
||||
args: {
|
||||
pagecontext: {
|
||||
contextid: pagecontextid
|
||||
}
|
||||
}
|
||||
}]);
|
||||
requests[1].done(reloadList).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for "Delete learning plan template" actions.
|
||||
* @method confirmDelete
|
||||
* @param {Event} e
|
||||
*/
|
||||
var confirmDelete = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var id = $(this).attr('data-templateid');
|
||||
templateid = id;
|
||||
deleteplans = true;
|
||||
|
||||
var requests = ajax.call([{
|
||||
methodname: 'core_competency_read_template',
|
||||
args: { id: templateid }
|
||||
}, {
|
||||
methodname: 'core_competency_template_has_related_data',
|
||||
args: { id: templateid }
|
||||
}]);
|
||||
|
||||
requests[0].done(function(template) {
|
||||
requests[1].done(function(templatehasrelateddata) {
|
||||
if (templatehasrelateddata) {
|
||||
str.get_strings([
|
||||
{ key: 'deletetemplate', component: 'tool_lp', param: template.shortname },
|
||||
{ key: 'deletetemplatewithplans', component: 'tool_lp' },
|
||||
{ key: 'deleteplans', component: 'tool_lp' },
|
||||
{ key: 'unlinkplanstemplate', component: 'tool_lp' },
|
||||
{ key: 'confirm', component: 'moodle' },
|
||||
{ key: 'cancel', component: 'moodle' }
|
||||
]).done(function (strings) {
|
||||
var actions = [{'text': strings[2], 'value' : 'delete'},
|
||||
{'text': strings[3], 'value' : 'unlink'}];
|
||||
var actionselector = new Actionselector(
|
||||
strings[0], // Title.
|
||||
strings[1], // Message
|
||||
actions, // Radio button options.
|
||||
strings[4], // Confirm.
|
||||
strings[5]); // Cancel.
|
||||
actionselector.display();
|
||||
actionselector.on('save', function(e, data) {
|
||||
if (data.action != 'delete') {
|
||||
deleteplans = false;
|
||||
}
|
||||
doDelete();
|
||||
});
|
||||
}).fail(notification.exception);
|
||||
}
|
||||
else {
|
||||
str.get_strings([
|
||||
{ key: 'confirm', component: 'moodle' },
|
||||
{ key: 'deletetemplate', component: 'tool_lp', param: template.shortname },
|
||||
{ key: 'delete', component: 'moodle' },
|
||||
{ key: 'cancel', component: 'moodle' }
|
||||
]).done(function (strings) {
|
||||
notification.confirm(
|
||||
strings[0], // Confirm.
|
||||
strings[1], // Delete learning plan template X?
|
||||
strings[2], // Delete.
|
||||
strings[3], // Cancel.
|
||||
doDelete
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
}
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/templateactions */ {
|
||||
// Public variables and functions.
|
||||
/**
|
||||
* Expose the event handler for the delete.
|
||||
* @method deleteHandler
|
||||
* @param {Event} e
|
||||
*/
|
||||
deleteHandler: confirmDelete,
|
||||
|
||||
/**
|
||||
* Expose the event handler for the duplicate.
|
||||
* @method duplicateHandler
|
||||
* @param {Event} e
|
||||
*/
|
||||
duplicateHandler: doDuplicate,
|
||||
|
||||
/**
|
||||
* Initialise the module.
|
||||
* @method init
|
||||
* @param {Number} contextid The context id of the page.
|
||||
*/
|
||||
init: function(contextid) {
|
||||
pagecontextid = contextid;
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,624 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Implement an accessible aria tree widget, from a nested unordered list.
|
||||
* Based on http://oaa-accessibility.org/example/41/
|
||||
*
|
||||
* To respond to selection changed events - use tree.on("selectionchanged", handler).
|
||||
* The handler will receive an array of nodes, which are the list items that are currently
|
||||
* selected. (Or a single node if multiselect is disabled).
|
||||
*
|
||||
* @module tool_lp/tree
|
||||
* @package core
|
||||
* @copyright 2015 Damyon Wiese <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/url', 'core/log'], function($, url, log) {
|
||||
// Private variables and functions.
|
||||
/** @var {String} expandedImage The html for an expanded tree node twistie. */
|
||||
var expandedImage = $('<img alt="" src="' + url.imageUrl('t/expanded') + '"/>');
|
||||
/** @var {String} collapsedImage The html for a collapsed tree node twistie. */
|
||||
var collapsedImage = $('<img alt="" src="' + url.imageUrl('t/collapsed') + '"/>');
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param {String} selector
|
||||
* @param {Boolean} multiSelect
|
||||
*/
|
||||
var Tree = function(selector, multiSelect) {
|
||||
this.treeRoot = $(selector);
|
||||
this.multiSelect = (typeof multiSelect === 'undefined' || multiSelect === true);
|
||||
|
||||
this.items = this.treeRoot.find('li');
|
||||
this.expandAll = this.items.length < 20;
|
||||
this.parents = this.treeRoot.find('li:has(ul)');
|
||||
|
||||
if (multiSelect) {
|
||||
this.treeRoot.attr('aria-multiselectable', 'true');
|
||||
}
|
||||
|
||||
this.items.attr('aria-selected', 'false');
|
||||
|
||||
this.visibleItems = null;
|
||||
this.activeItem = null;
|
||||
this.lastActiveItem = null;
|
||||
|
||||
this.keys = {
|
||||
tab: 9,
|
||||
enter: 13,
|
||||
space: 32,
|
||||
pageup: 33,
|
||||
pagedown: 34,
|
||||
end: 35,
|
||||
home: 36,
|
||||
left: 37,
|
||||
up: 38,
|
||||
right: 39,
|
||||
down: 40,
|
||||
asterisk: 106
|
||||
};
|
||||
|
||||
this.init();
|
||||
|
||||
this.bindEventHandlers();
|
||||
};
|
||||
// Public variables and functions.
|
||||
|
||||
/**
|
||||
* Init this tree
|
||||
* @method init
|
||||
*/
|
||||
Tree.prototype.init = function() {
|
||||
this.parents.attr('aria-expanded', 'true');
|
||||
this.parents.prepend(expandedImage.clone());
|
||||
|
||||
this.items.attr('role', 'tree-item');
|
||||
this.items.attr('tabindex', '-1');
|
||||
this.parents.attr('role', 'group');
|
||||
this.treeRoot.attr('role', 'tree');
|
||||
|
||||
this.visibleItems = this.treeRoot.find('li');
|
||||
|
||||
var thisObj = this;
|
||||
if (!this.expandAll) {
|
||||
this.parents.each(function() {
|
||||
thisObj.collapseGroup($(this));
|
||||
});
|
||||
this.expandGroup(this.parents.first());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Expand a collapsed group.
|
||||
*
|
||||
* @method expandGroup
|
||||
* @param {Object} item is the jquery id of the parent item of the group
|
||||
*/
|
||||
Tree.prototype.expandGroup = function(item) {
|
||||
// Find the first child ul node.
|
||||
var group = item.children('ul');
|
||||
|
||||
// Expand the group.
|
||||
group.show().attr('aria-hidden', 'false');
|
||||
|
||||
item.attr('aria-expanded', 'true');
|
||||
|
||||
item.children('img').attr('src', expandedImage.attr('src'));
|
||||
|
||||
// Update the list of visible items.
|
||||
this.visibleItems = this.treeRoot.find('li:visible');
|
||||
};
|
||||
|
||||
/**
|
||||
* Collapse an expanded group.
|
||||
*
|
||||
* @method collapseGroup
|
||||
* @param {Object} item is the jquery id of the parent item of the group
|
||||
*/
|
||||
Tree.prototype.collapseGroup = function(item) {
|
||||
var group = item.children('ul');
|
||||
|
||||
// Collapse the group.
|
||||
group.hide().attr('aria-hidden', 'true');
|
||||
|
||||
item.attr('aria-expanded', 'false');
|
||||
|
||||
item.children('img').attr('src', collapsedImage.attr('src'));
|
||||
|
||||
// Update the list of visible items.
|
||||
this.visibleItems = this.treeRoot.find('li:visible');
|
||||
};
|
||||
|
||||
/**
|
||||
* Expand or collapse a group.
|
||||
*
|
||||
* @method toggleGroup
|
||||
* @param {Object} item is the jquery id of the parent item of the group
|
||||
*/
|
||||
Tree.prototype.toggleGroup = function(item) {
|
||||
if (item.attr('aria-expanded') == 'true') {
|
||||
this.collapseGroup(item);
|
||||
} else {
|
||||
this.expandGroup(item);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Whenever the currently selected node has changed, trigger an event using this function.
|
||||
*
|
||||
* @method triggerChange
|
||||
*/
|
||||
Tree.prototype.triggerChange = function() {
|
||||
var allSelected = this.items.filter('[aria-selected=true]');
|
||||
if (!this.multiSelect) {
|
||||
allSelected = allSelected.first();
|
||||
}
|
||||
this.treeRoot.trigger('selectionchanged', { selected: allSelected });
|
||||
};
|
||||
|
||||
/**
|
||||
* Select all the items between the last focused item and this currently focused item.
|
||||
*
|
||||
* @method multiSelectItem
|
||||
* @param {Object} item is the jquery id of the newly selected item.
|
||||
*/
|
||||
Tree.prototype.multiSelectItem = function(item) {
|
||||
if (!this.multiSelect) {
|
||||
this.items.attr('aria-selected', 'false');
|
||||
} else if (this.lastActiveItem !== null) {
|
||||
var lastIndex = this.visibleItems.index(this.lastActiveItem);
|
||||
var currentIndex = this.visibleItems.index(this.activeItem);
|
||||
var oneItem = null;
|
||||
|
||||
while (lastIndex < currentIndex) {
|
||||
oneItem = $(this.visibleItems.get(lastIndex));
|
||||
oneItem.attr('aria-selected', 'true');
|
||||
lastIndex++;
|
||||
}
|
||||
while (lastIndex > currentIndex) {
|
||||
oneItem = $(this.visibleItems.get(lastIndex));
|
||||
oneItem.attr('aria-selected', 'true');
|
||||
lastIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
item.attr('aria-selected', 'true');
|
||||
this.triggerChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Select a single item. Make sure all the parents are expanded. De-select all other items.
|
||||
*
|
||||
* @method selectItem
|
||||
* @param {Object} item is the jquery id of the newly selected item.
|
||||
*/
|
||||
Tree.prototype.selectItem = function(item) {
|
||||
// Expand all nodes up the tree.
|
||||
var walk = item.parent();
|
||||
while (walk.attr('role') != 'tree') {
|
||||
walk = walk.parent();
|
||||
if (walk.attr('aria-expanded') == 'false') {
|
||||
this.expandGroup(walk);
|
||||
}
|
||||
walk = walk.parent();
|
||||
}
|
||||
this.items.attr('aria-selected', 'false');
|
||||
item.attr('aria-selected', 'true');
|
||||
this.triggerChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the selected state for an item back and forth.
|
||||
*
|
||||
* @method toggleItem
|
||||
* @param {Object} item is the jquery id of the item to toggle.
|
||||
*/
|
||||
Tree.prototype.toggleItem = function(item) {
|
||||
if (!this.multiSelect) {
|
||||
return this.selectItem(item);
|
||||
}
|
||||
|
||||
var current = item.attr('aria-selected');
|
||||
if (current === 'true') {
|
||||
current = 'false';
|
||||
} else {
|
||||
current = 'true';
|
||||
}
|
||||
item.attr('aria-selected', current);
|
||||
this.triggerChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the focus to this item.
|
||||
*
|
||||
* @method updateFocus
|
||||
* @param {Object} item is the jquery id of the parent item of the group
|
||||
*/
|
||||
Tree.prototype.updateFocus = function(item) {
|
||||
this.lastActiveItem = this.activeItem;
|
||||
this.activeItem = item;
|
||||
// Expand all nodes up the tree.
|
||||
var walk = item.parent();
|
||||
while (walk.attr('role') != 'tree') {
|
||||
walk = walk.parent();
|
||||
if (walk.attr('aria-expanded') == 'false') {
|
||||
this.expandGroup(walk);
|
||||
}
|
||||
walk = walk.parent();
|
||||
}
|
||||
this.items.attr('tabindex', '-1');
|
||||
item.attr('tabindex', 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a key down event - ie navigate the tree.
|
||||
*
|
||||
* @method handleKeyDown
|
||||
* @param {Object} item is the jquery id of the parent item of the group
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
Tree.prototype.handleKeyDown = function(item, e) {
|
||||
var currentIndex = this.visibleItems.index(item);
|
||||
var newItem = null;
|
||||
|
||||
switch (e.keyCode) {
|
||||
case this.keys.home: {
|
||||
// Jump to first item in tree.
|
||||
newItem = this.parents.first();
|
||||
newItem.focus();
|
||||
if (e.shiftKey) {
|
||||
this.multiSelectItem(newItem);
|
||||
} else if (!e.ctrlKey) {
|
||||
this.selectItem(newItem);
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
case this.keys.end: {
|
||||
// Jump to last visible item.
|
||||
newItem = this.visibleItems.last();
|
||||
newItem.focus();
|
||||
if (e.shiftKey) {
|
||||
this.multiSelectItem(newItem);
|
||||
} else if (!e.ctrlKey) {
|
||||
this.selectItem(newItem);
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
case this.keys.enter:
|
||||
case this.keys.space: {
|
||||
|
||||
if (e.shiftKey) {
|
||||
this.multiSelectItem(item);
|
||||
} else if (e.ctrlKey) {
|
||||
this.toggleItem(item);
|
||||
} else {
|
||||
this.selectItem(item);
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
case this.keys.left: {
|
||||
if (item.has('ul') && item.attr('aria-expanded') == 'true') {
|
||||
this.collapseGroup(item);
|
||||
} else {
|
||||
// Move up to the parent.
|
||||
var itemUL = item.parent();
|
||||
var itemParent = itemUL.parent();
|
||||
if (itemParent.is('li')) {
|
||||
itemParent.focus();
|
||||
if (e.shiftKey) {
|
||||
this.multiSelectItem(itemParent);
|
||||
} else if (!e.ctrlKey) {
|
||||
this.selectItem(itemParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
case this.keys.right: {
|
||||
if (item.has('ul') && item.attr('aria-expanded') == 'false') {
|
||||
this.expandGroup(item);
|
||||
} else {
|
||||
// Move to the first item in the child group.
|
||||
newItem = item.children('ul').children('li').first();
|
||||
if (newItem.length > 0) {
|
||||
newItem.focus();
|
||||
if (e.shiftKey) {
|
||||
this.multiSelectItem(newItem);
|
||||
} else if (!e.ctrlKey) {
|
||||
this.selectItem(newItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
case this.keys.up: {
|
||||
|
||||
if (currentIndex > 0) {
|
||||
var prev = this.visibleItems.eq(currentIndex - 1);
|
||||
prev.focus();
|
||||
if (e.shiftKey) {
|
||||
this.multiSelectItem(prev);
|
||||
} else if (!e.ctrlKey) {
|
||||
this.selectItem(prev);
|
||||
}
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
case this.keys.down: {
|
||||
|
||||
if (currentIndex < this.visibleItems.length - 1) {
|
||||
var next = this.visibleItems.eq(currentIndex + 1);
|
||||
next.focus();
|
||||
if (e.shiftKey) {
|
||||
this.multiSelectItem(next);
|
||||
} else if (!e.ctrlKey) {
|
||||
this.selectItem(next);
|
||||
}
|
||||
}
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
case this.keys.asterisk: {
|
||||
// Expand all groups.
|
||||
|
||||
var thisObj = this;
|
||||
|
||||
this.parents.each(function() {
|
||||
thisObj.expandGroup($(this));
|
||||
});
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a key press event - ie navigate the tree.
|
||||
*
|
||||
* @method handleKeyPress
|
||||
* @param {Object} item is the jquery id of the parent item of the group
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
Tree.prototype.handleKeyPress = function(item, e) {
|
||||
if (e.altKey || e.ctrlKey || e.shiftKey) {
|
||||
// Do nothing.
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (e.keyCode) {
|
||||
case this.keys.tab: {
|
||||
return true;
|
||||
}
|
||||
case this.keys.enter:
|
||||
case this.keys.home:
|
||||
case this.keys.end:
|
||||
case this.keys.left:
|
||||
case this.keys.right:
|
||||
case this.keys.up:
|
||||
case this.keys.down: {
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
default : {
|
||||
var chr = String.fromCharCode(e.which);
|
||||
var match = false;
|
||||
var itemIndex = this.visibleItems.index(item);
|
||||
var itemCount = this.visibleItems.length;
|
||||
var currentIndex = itemIndex + 1;
|
||||
|
||||
// Check if the active item was the last one on the list.
|
||||
if (currentIndex == itemCount) {
|
||||
currentIndex = 0;
|
||||
}
|
||||
|
||||
// Iterate through the menu items (starting from the current item and wrapping) until a match is found
|
||||
// or the loop returns to the current menu item.
|
||||
while (currentIndex != itemIndex) {
|
||||
|
||||
var currentItem = this.visibleItems.eq(currentIndex);
|
||||
var titleChr = currentItem.text().charAt(0);
|
||||
|
||||
if (currentItem.has('ul')) {
|
||||
titleChr = currentItem.find('span').text().charAt(0);
|
||||
}
|
||||
|
||||
if (titleChr.toLowerCase() == chr) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
|
||||
currentIndex = currentIndex+1;
|
||||
if (currentIndex == itemCount) {
|
||||
// Reached the end of the list, start again at the beginning.
|
||||
currentIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (match === true) {
|
||||
this.updateFocus(this.visibleItems.eq(currentIndex));
|
||||
}
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attach an event listener to the tree.
|
||||
*
|
||||
* @method on
|
||||
* @param {String} eventname This is the name of the event to listen for. Only 'selectionchanged' is supported for now.
|
||||
* @param {Function} handler The function to call when the event is triggered.
|
||||
*/
|
||||
Tree.prototype.on = function(eventname, handler) {
|
||||
if (eventname !== 'selectionchanged') {
|
||||
log.warning('Invalid custom event name for tree. Only "selectionchanged" is supported.');
|
||||
} else {
|
||||
this.treeRoot.on(eventname, handler);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a double click (expand/collapse).
|
||||
*
|
||||
* @method handleDblClick
|
||||
* @param {Object} item is the jquery id of the parent item of the group
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
Tree.prototype.handleDblClick = function(item, e) {
|
||||
|
||||
if (e.altKey || e.ctrlKey || e.shiftKey) {
|
||||
// Do nothing.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Apply the focus markup.
|
||||
this.updateFocus(item);
|
||||
|
||||
// Expand or collapse the group.
|
||||
this.toggleGroup(item);
|
||||
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a click (select).
|
||||
*
|
||||
* @method handleExpandCollapseClick
|
||||
* @param {Object} item is the jquery id of the parent item of the group
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
Tree.prototype.handleExpandCollapseClick = function(item, e) {
|
||||
|
||||
// Do not shift the focus.
|
||||
this.toggleGroup(item);
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle a click (select).
|
||||
*
|
||||
* @method handleClick
|
||||
* @param {Object} item is the jquery id of the parent item of the group
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
Tree.prototype.handleClick = function(item, e) {
|
||||
|
||||
if (e.shiftKey) {
|
||||
this.multiSelectItem(item);
|
||||
} else if (e.ctrlKey) {
|
||||
this.toggleItem(item);
|
||||
} else {
|
||||
this.selectItem(item);
|
||||
}
|
||||
this.updateFocus(item);
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a blur event
|
||||
*
|
||||
* @method handleBlur
|
||||
* @param {Object} item item is the jquery id of the parent item of the group
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
Tree.prototype.handleBlur = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a focus event
|
||||
*
|
||||
* @method handleFocus
|
||||
* @param {Object} item item is the jquery id of the parent item of the group
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
Tree.prototype.handleFocus = function(item) {
|
||||
|
||||
this.updateFocus(item);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bind the event listeners we require.
|
||||
*
|
||||
* @method bindEventHandlers
|
||||
*/
|
||||
Tree.prototype.bindEventHandlers = function() {
|
||||
var thisObj = this;
|
||||
|
||||
// Bind a dblclick handler to the parent items.
|
||||
this.parents.dblclick(function(e) {
|
||||
return thisObj.handleDblClick($(this), e);
|
||||
});
|
||||
|
||||
// Bind a click handler.
|
||||
this.items.click(function(e) {
|
||||
return thisObj.handleClick($(this), e);
|
||||
});
|
||||
|
||||
// Bind a toggle handler to the expand/collapse icons.
|
||||
this.items.children('img').click(function(e) {
|
||||
return thisObj.handleExpandCollapseClick($(this).parent(), e);
|
||||
});
|
||||
|
||||
// Bind a keydown handler.
|
||||
this.items.keydown(function(e) {
|
||||
return thisObj.handleKeyDown($(this), e);
|
||||
});
|
||||
|
||||
// Bind a keypress handler.
|
||||
this.items.keypress(function(e) {
|
||||
return thisObj.handleKeyPress($(this), e);
|
||||
});
|
||||
|
||||
// Bind a focus handler.
|
||||
this.items.focus(function(e) {
|
||||
return thisObj.handleFocus($(this), e);
|
||||
});
|
||||
|
||||
// Bind a blur handler.
|
||||
this.items.blur(function(e) {
|
||||
return thisObj.handleBlur($(this), e);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/tree */ Tree;
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Module to enable inline editing of a comptency grade.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery'], function($) {
|
||||
|
||||
/**
|
||||
* UserCompetencyCourseNavigation
|
||||
*
|
||||
* @param {String} The selector of the user element.
|
||||
* @param {String} The selector of the competency element.
|
||||
* @param {String} The base url for the page (no params).
|
||||
* @param {Number} The course id
|
||||
* @param {Number} The user id
|
||||
* @param {Number} The competency id
|
||||
*/
|
||||
var UserCompetencyCourseNavigation = function(userSelector, competencySelector, baseUrl, userId, competencyId, courseId) {
|
||||
this._baseUrl = baseUrl;
|
||||
this._userId = userId + '';
|
||||
this._competencyId = competencyId + '';
|
||||
this._courseId = courseId;
|
||||
this._ignoreFirstUser = true;
|
||||
this._ignoreFirstCompetency = true;
|
||||
|
||||
$(userSelector).on('change', this._userChanged.bind(this));
|
||||
$(competencySelector).on('change', this._competencyChanged.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* The user was changed in the select list.
|
||||
*
|
||||
* @method _userChanged
|
||||
* @param {Event} e
|
||||
*/
|
||||
UserCompetencyCourseNavigation.prototype._userChanged = function(e) {
|
||||
if (this._ignoreFirstUser) {
|
||||
this._ignoreFirstUser = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var newUserId = $(e.target).val();
|
||||
var queryStr = '?userid=' + newUserId + '&courseid=' + this._courseId + '&competencyid=' + this._competencyId;
|
||||
document.location = this._baseUrl + queryStr;
|
||||
};
|
||||
|
||||
/**
|
||||
* The competency was changed in the select list.
|
||||
*
|
||||
* @method _competencyChanged
|
||||
* @param {Event} e
|
||||
*/
|
||||
UserCompetencyCourseNavigation.prototype._competencyChanged = function(e) {
|
||||
if (this._ignoreFirstCompetency) {
|
||||
this._ignoreFirstCompetency = false;
|
||||
return;
|
||||
}
|
||||
var newCompetencyId = $(e.target).val();
|
||||
var queryStr = '?userid=' + this._userId + '&courseid=' + this._courseId + '&competencyid=' + newCompetencyId;
|
||||
document.location = this._baseUrl + queryStr;
|
||||
};
|
||||
|
||||
/** @type {Number} The id of the competency. */
|
||||
UserCompetencyCourseNavigation.prototype._competencyId = null;
|
||||
/** @type {Number} The id of the user. */
|
||||
UserCompetencyCourseNavigation.prototype._userId = null;
|
||||
/** @type {Number} The id of the course. */
|
||||
UserCompetencyCourseNavigation.prototype._courseId = null;
|
||||
/** @type {String} Plugin base url. */
|
||||
UserCompetencyCourseNavigation.prototype._baseUrl = null;
|
||||
/** @type {Boolean} Ignore the first change event for users. */
|
||||
UserCompetencyCourseNavigation.prototype._ignoreFirstUser = null;
|
||||
/** @type {Boolean} Ignore the first change event for competencies. */
|
||||
UserCompetencyCourseNavigation.prototype._ignoreFirstCompetency = null;
|
||||
|
||||
return /** @alias module:tool_lp/user_competency_course_navigation */ UserCompetencyCourseNavigation;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,112 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Module to refresh a user competency summary in a page.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery', 'core/notification', 'core/ajax', 'core/templates'], function($, notification, ajax, templates) {
|
||||
|
||||
/**
|
||||
* Info
|
||||
*
|
||||
* @param {JQuery} Selector to replace when the information needs updating.
|
||||
* @param {Number} The id of the competency.
|
||||
* @param {Number} The id of the user.
|
||||
* @param {Number} The id of the plan.
|
||||
* @param {Number} The id of the course.
|
||||
* @param {Boolean} If we should display the user info.
|
||||
*/
|
||||
var Info = function(rootElement, competencyId, userId, planId, courseId, displayuser) {
|
||||
this._rootElement = rootElement;
|
||||
this._competencyId = competencyId;
|
||||
this._userId = userId;
|
||||
this._planId = planId;
|
||||
this._courseId = courseId;
|
||||
this._valid = true;
|
||||
this._displayuser = (typeof displayuser !== 'undefined') ? displayuser : false;
|
||||
|
||||
if (this._planId) {
|
||||
this._methodName = 'tool_lp_data_for_user_competency_summary_in_plan';
|
||||
this._args = { competencyid: this._competencyId, planid: this._planId };
|
||||
this._templateName = 'tool_lp/user_competency_summary_in_plan';
|
||||
} else if (this._courseId) {
|
||||
this._methodName = 'tool_lp_data_for_user_competency_summary_in_course';
|
||||
this._args = { userid: this._userId, competencyid: this._competencyId, courseid: this._courseId };
|
||||
this._templateName = 'tool_lp/user_competency_summary_in_course';
|
||||
} else {
|
||||
this._methodName = 'tool_lp_data_for_user_competency_summary';
|
||||
this._args = { userid: this._userId, competencyid: this._competencyId };
|
||||
this._templateName = 'tool_lp/user_competency_summary';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reload the info for this user competency.
|
||||
*
|
||||
* @method reload
|
||||
*/
|
||||
Info.prototype.reload = function() {
|
||||
var self = this,
|
||||
promises = [];
|
||||
|
||||
if (!this._valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
promises = ajax.call([{
|
||||
methodname: this._methodName,
|
||||
args: this._args
|
||||
}]);
|
||||
|
||||
promises[0].done(function(context) {
|
||||
// Check if we should also the user info.
|
||||
if (self._displayuser) {
|
||||
context.displayuser = true;
|
||||
}
|
||||
templates.render(self._templateName, context).done(function(html, js) {
|
||||
templates.replaceNode(self._rootElement, html, js);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
};
|
||||
|
||||
/** @type {JQuery} The root element to replace in the DOM. */
|
||||
Info.prototype._rootElement = null;
|
||||
/** @type {Number} The id of the course. */
|
||||
Info.prototype._courseId = null;
|
||||
/** @type {Boolean} Is this module valid? */
|
||||
Info.prototype._valid = null;
|
||||
/** @type {Number} The id of the plan. */
|
||||
Info.prototype._planId = null;
|
||||
/** @type {Number} The id of the competency. */
|
||||
Info.prototype._competencyId = null;
|
||||
/** @type {Number} The id of the user. */
|
||||
Info.prototype._userId = null;
|
||||
/** @type {String} The method name to load the data. */
|
||||
Info.prototype._methodName = null;
|
||||
/** @type {Object} The arguments to load the data. */
|
||||
Info.prototype._args = null;
|
||||
/** @type {String} The template to reload the fragment. */
|
||||
Info.prototype._templateName = null;
|
||||
/** @type {Boolean} If we should display the user info? */
|
||||
Info.prototype._displayuser = false;
|
||||
|
||||
return /** @alias module:tool_lp/user_competency_info */ Info;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,301 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* User competency workflow.
|
||||
*
|
||||
* @module tool_lp/user_competency_workflow
|
||||
* @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/templates',
|
||||
'core/ajax',
|
||||
'core/notification',
|
||||
'core/str',
|
||||
'tool_lp/menubar',
|
||||
'tool_lp/event_base'],
|
||||
function($, Templates, Ajax, Notification, Str, Menubar, EventBase) {
|
||||
|
||||
/**
|
||||
* UserCompetencyWorkflow class.
|
||||
*
|
||||
* @param {String} selector The node containing the buttons to switch mode.
|
||||
*/
|
||||
var UserCompetencyWorkflow = function() {
|
||||
EventBase.prototype.constructor.apply(this, []);
|
||||
};
|
||||
UserCompetencyWorkflow.prototype = Object.create(EventBase.prototype);
|
||||
|
||||
/** @type {String} The selector to find the user competency data. */
|
||||
UserCompetencyWorkflow.prototype._nodeSelector = '[data-node="user-competency"]';
|
||||
|
||||
/**
|
||||
* Cancel a review request and refresh the view.
|
||||
*
|
||||
* @param {Object} data The user competency data.
|
||||
* @return {Void}
|
||||
* @method _cancelReviewRequest
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype._cancelReviewRequest = function(data) {
|
||||
var call = {
|
||||
methodname: 'core_competency_user_competency_cancel_review_request',
|
||||
args: {
|
||||
userid: data.userid,
|
||||
competencyid: data.competencyid
|
||||
}
|
||||
};
|
||||
|
||||
Ajax.call([call])[0].then(function() {
|
||||
this._trigger('review-request-cancelled', data);
|
||||
this._trigger('status-changed', data);
|
||||
}.bind(this), function() {
|
||||
this._trigger('error-occured', data);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel a review request an refresh the view.
|
||||
*
|
||||
* @param {Object} data The user competency data.
|
||||
* @return {Void}
|
||||
* @method cancelReviewRequest
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype.cancelReviewRequest = function(data) {
|
||||
this._cancelReviewRequest(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel a review request handler.
|
||||
*
|
||||
* @param {Event} e The event.
|
||||
* @return {Void}
|
||||
* @method _cancelReviewRequestHandler
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype._cancelReviewRequestHandler = function(e) {
|
||||
e.preventDefault();
|
||||
var data = this._findUserCompetencyData($(e.target));
|
||||
this.cancelReviewRequest(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Request a review and refresh the view.
|
||||
*
|
||||
* @param {Object} data The user competency data.
|
||||
* @return {Void}
|
||||
* @method _requestReview
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype._requestReview = function(data) {
|
||||
var call = {
|
||||
methodname: 'core_competency_user_competency_request_review',
|
||||
args: {
|
||||
userid: data.userid,
|
||||
competencyid: data.competencyid
|
||||
}
|
||||
};
|
||||
|
||||
Ajax.call([call])[0].then(function() {
|
||||
this._trigger('review-requested', data);
|
||||
this._trigger('status-changed', data);
|
||||
}.bind(this), function() {
|
||||
this._trigger('error-occured', data);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Request a review.
|
||||
*
|
||||
* @param {Object} data The user competency data.
|
||||
* @return {Void}
|
||||
* @method requestReview
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype.requestReview = function(data) {
|
||||
this._requestReview(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Request a review handler.
|
||||
*
|
||||
* @param {Event} e The event.
|
||||
* @return {Void}
|
||||
* @method _requestReviewHandler
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype._requestReviewHandler = function(e) {
|
||||
e.preventDefault();
|
||||
var data = this._findUserCompetencyData($(e.target));
|
||||
this.requestReview(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a review and refresh the view.
|
||||
*
|
||||
* @param {Object} data The user competency data.
|
||||
* @return {Void}
|
||||
* @method _startReview
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype._startReview = function(data) {
|
||||
var call = {
|
||||
methodname: 'core_competency_user_competency_start_review',
|
||||
args: {
|
||||
userid: data.userid,
|
||||
competencyid: data.competencyid
|
||||
}
|
||||
};
|
||||
|
||||
Ajax.call([call])[0].then(function() {
|
||||
this._trigger('review-started', data);
|
||||
this._trigger('status-changed', data);
|
||||
}.bind(this), function() {
|
||||
this._trigger('error-occured', data);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a review.
|
||||
*
|
||||
* @param {Object} data The user competency data.
|
||||
* @return {Void}
|
||||
* @method startReview
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype.startReview = function(data) {
|
||||
this._startReview(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a review handler.
|
||||
*
|
||||
* @param {Event} e The event.
|
||||
* @return {Void}
|
||||
* @method _startReviewHandler
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype._startReviewHandler = function(e) {
|
||||
e.preventDefault();
|
||||
var data = this._findUserCompetencyData($(e.target));
|
||||
this.startReview(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop a review and refresh the view.
|
||||
*
|
||||
* @param {Object} data The user competency data.
|
||||
* @return {Void}
|
||||
* @method _stopReview
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype._stopReview = function(data) {
|
||||
var call = {
|
||||
methodname: 'core_competency_user_competency_stop_review',
|
||||
args: {
|
||||
userid: data.userid,
|
||||
competencyid: data.competencyid
|
||||
}
|
||||
};
|
||||
|
||||
Ajax.call([call])[0].then(function() {
|
||||
this._trigger('review-stopped', data);
|
||||
this._trigger('status-changed', data);
|
||||
}.bind(this), function() {
|
||||
this._trigger('error-occured', data);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop a review.
|
||||
*
|
||||
* @param {Object} data The user competency data.
|
||||
* @return {Void}
|
||||
* @method stopReview
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype.stopReview = function(data) {
|
||||
this._stopReview(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop a review handler.
|
||||
*
|
||||
* @param {Event} e The event.
|
||||
* @return {Void}
|
||||
* @method _stopReviewHandler
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype._stopReviewHandler = function(e) {
|
||||
e.preventDefault();
|
||||
var data = this._findUserCompetencyData($(e.target));
|
||||
this.stopReview(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enhance a menu bar.
|
||||
*
|
||||
* @param {String} selector Menubar selector.
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype.enhanceMenubar = function(selector) {
|
||||
Menubar.enhance(selector, {
|
||||
'[data-action="request-review"]': this._requestReviewHandler.bind(this),
|
||||
'[data-action="cancel-review-request"]': this._cancelReviewRequestHandler.bind(this),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the user competency data from a node.
|
||||
*
|
||||
* @param {Node} node The node to search from.
|
||||
* @return {Object} User competency data.
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype._findUserCompetencyData = function(node) {
|
||||
var parent = node.parents(this._nodeSelector),
|
||||
data;
|
||||
|
||||
if (parent.length != 1) {
|
||||
throw new Error('The evidence node was not located.');
|
||||
}
|
||||
|
||||
data = parent.data();
|
||||
if (typeof data === 'undefined' || typeof data.userid === 'undefined' || typeof data.competencyid === 'undefined') {
|
||||
throw new Error('User competency data could not be found.');
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enhance a menu bar.
|
||||
*
|
||||
* @param {String} selector Menubar selector.
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype.enhanceMenubar = function(selector) {
|
||||
Menubar.enhance(selector, {
|
||||
'[data-action="request-review"]': this._requestReviewHandler.bind(this),
|
||||
'[data-action="cancel-review-request"]': this._cancelReviewRequestHandler.bind(this),
|
||||
'[data-action="start-review"]': this._startReviewHandler.bind(this),
|
||||
'[data-action="stop-review"]': this._stopReviewHandler.bind(this),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the events in the region.
|
||||
*
|
||||
* @param {String} selector The base selector to search nodes in and attach events.
|
||||
*/
|
||||
UserCompetencyWorkflow.prototype.registerEvents = function(selector) {
|
||||
var wrapper = $(selector);
|
||||
|
||||
wrapper.find('[data-action="request-review"]').click(this._requestReviewHandler.bind(this));
|
||||
wrapper.find('[data-action="cancel-review-request"]').click(this._cancelReviewRequestHandler.bind(this));
|
||||
wrapper.find('[data-action="start-review"]').click(this._startReviewHandler.bind(this));
|
||||
wrapper.find('[data-action="stop-review"]').click(this._stopReviewHandler.bind(this));
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/user_competency_actions */ UserCompetencyWorkflow;
|
||||
});
|
||||
@@ -0,0 +1,402 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* User evidence actions.
|
||||
*
|
||||
* @module tool_lp/user_evidence_actions
|
||||
* @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/templates',
|
||||
'core/ajax',
|
||||
'core/notification',
|
||||
'core/str',
|
||||
'tool_lp/menubar',
|
||||
'tool_lp/competencypicker_user_plans'],
|
||||
function($, templates, ajax, notification, str, Menubar, PickerUserPlans) {
|
||||
|
||||
/**
|
||||
* UserEvidenceActions class.
|
||||
*
|
||||
* Note that presently this cannot be instantiated more than once per page.
|
||||
*
|
||||
* @param {String} type The type of page we're in.
|
||||
*/
|
||||
var UserEvidenceActions = function(type) {
|
||||
this._type = type;
|
||||
|
||||
if (type === 'evidence') {
|
||||
// This is the page to view one evidence.
|
||||
this._region = '[data-region="user-evidence-page"]';
|
||||
this._evidenceNode = '[data-region="user-evidence-page"]';
|
||||
this._template = 'tool_lp/user_evidence_page';
|
||||
this._contextMethod = 'tool_lp_data_for_user_evidence_page';
|
||||
|
||||
} else if (type === 'list') {
|
||||
// This is the page to view a list of evidence.
|
||||
this._region = '[data-region="user-evidence-list"]';
|
||||
this._evidenceNode = '[data-region="user-evidence-node"]';
|
||||
this._template = 'tool_lp/user_evidence_list_page';
|
||||
this._contextMethod = 'tool_lp_data_for_user_evidence_list_page';
|
||||
|
||||
} else {
|
||||
throw new TypeError('Unexpected type.');
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {String} Ajax method to fetch the page data from. */
|
||||
UserEvidenceActions.prototype._contextMethod = null;
|
||||
/** @type {String} Selector to find the node describing the evidence. */
|
||||
UserEvidenceActions.prototype._evidenceNode = null;
|
||||
/** @type {String} Selector mapping to the region to update. Usually similar to wrapper. */
|
||||
UserEvidenceActions.prototype._region = null;
|
||||
/** @type {String} Name of the template used to render the region. */
|
||||
UserEvidenceActions.prototype._template = null;
|
||||
/** @type {String} Type of page/region we're in. */
|
||||
UserEvidenceActions.prototype._type = null;
|
||||
|
||||
/**
|
||||
* Resolve the arguments to refresh the region.
|
||||
*
|
||||
* @param {Object} evidenceData Evidence data from evidence node.
|
||||
* @return {Object} List of arguments.
|
||||
*/
|
||||
UserEvidenceActions.prototype._getContextArgs = function(evidenceData) {
|
||||
var self = this,
|
||||
args = {};
|
||||
|
||||
if (self._type === 'evidence') {
|
||||
args = {
|
||||
id: evidenceData.id
|
||||
};
|
||||
|
||||
} else if (self._type === 'list') {
|
||||
args = {
|
||||
userid: evidenceData.userid
|
||||
};
|
||||
}
|
||||
|
||||
return args;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to render the region template.
|
||||
*
|
||||
* @param {Object} context The context for the template.
|
||||
*/
|
||||
UserEvidenceActions.prototype._renderView = function(context) {
|
||||
var self = this;
|
||||
templates.render(self._template, context)
|
||||
.done(function(newhtml, newjs) {
|
||||
templates.replaceNode($(self._region), newhtml, newjs);
|
||||
}.bind(self))
|
||||
.fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Call multiple ajax methods, and refresh.
|
||||
*
|
||||
* @param {Array} calls List of Ajax calls.
|
||||
* @param {Object} evidenceData Evidence data from evidence node.
|
||||
* @return {Promise}
|
||||
*/
|
||||
UserEvidenceActions.prototype._callAndRefresh = function(calls, evidenceData) {
|
||||
var self = this;
|
||||
|
||||
calls.push({
|
||||
methodname: self._contextMethod,
|
||||
args: self._getContextArgs(evidenceData)
|
||||
});
|
||||
|
||||
// Apply all the promises, and refresh when the last one is resolved.
|
||||
return $.when.apply($.when, ajax.call(calls))
|
||||
.then(function() {
|
||||
self._renderView.call(self, arguments[arguments.length - 1]);
|
||||
})
|
||||
.fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a plan and reload the region.
|
||||
*
|
||||
* @param {Object} evidenceData Evidence data from evidence node.
|
||||
*/
|
||||
UserEvidenceActions.prototype._doDelete = function(evidenceData) {
|
||||
var self = this,
|
||||
calls = [{
|
||||
methodname: 'core_competency_delete_user_evidence',
|
||||
args: { id: evidenceData.id }
|
||||
}];
|
||||
self._callAndRefresh(calls, evidenceData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a plan.
|
||||
*
|
||||
* @param {Object} evidenceData Evidence data from evidence node.
|
||||
*/
|
||||
UserEvidenceActions.prototype.deleteEvidence = function(evidenceData) {
|
||||
var self = this,
|
||||
requests;
|
||||
|
||||
requests = ajax.call([{
|
||||
methodname: 'core_competency_read_user_evidence',
|
||||
args: { id: evidenceData.id }
|
||||
}]);
|
||||
|
||||
requests[0].done(function(evidence) {
|
||||
str.get_strings([
|
||||
{ key: 'confirm', component: 'moodle' },
|
||||
{ key: 'deleteuserevidence', component: 'tool_lp', param: evidence.name },
|
||||
{ key: 'delete', component: 'moodle' },
|
||||
{ key: 'cancel', component: 'moodle' }
|
||||
]).done(function (strings) {
|
||||
notification.confirm(
|
||||
strings[0], // Confirm.
|
||||
strings[1], // Delete evidence X?
|
||||
strings[2], // Delete.
|
||||
strings[3], // Cancel.
|
||||
function() {
|
||||
self._doDelete(evidenceData);
|
||||
}.bind(self)
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete evidence handler.
|
||||
*
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
UserEvidenceActions.prototype._deleteEvidenceHandler = function(e) {
|
||||
e.preventDefault();
|
||||
var data = this._findEvidenceData($(e.target));
|
||||
this.deleteEvidence(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Link a competency and reload.
|
||||
*
|
||||
* @param {Object} evidenceData Evidence data from evidence node.
|
||||
* @param {Number} competencyIds The competency IDs.
|
||||
* @param {Boolean} requestReview Send competencies to review.
|
||||
*/
|
||||
UserEvidenceActions.prototype._doCreateUserEvidenceCompetency = function(evidenceData, competencyIds) {
|
||||
var self = this,
|
||||
calls = [];
|
||||
|
||||
$.each(competencyIds, function(index, competencyId) {
|
||||
calls.push({
|
||||
methodname: 'core_competency_create_user_evidence_competency',
|
||||
args: {
|
||||
userevidenceid: evidenceData.id,
|
||||
competencyid: competencyId,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
self._callAndRefresh(calls, evidenceData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a user evidence competency.
|
||||
*
|
||||
* @param {Object} evidenceData Evidence data from evidence node.
|
||||
*/
|
||||
UserEvidenceActions.prototype.createUserEvidenceCompetency = function(evidenceData) {
|
||||
var self = this,
|
||||
picker = new PickerUserPlans(evidenceData.userid);
|
||||
|
||||
picker.on('save', function(e, data) {
|
||||
var competencyIds = data.competencyIds;
|
||||
self._doCreateUserEvidenceCompetency(evidenceData, competencyIds, data.requestReview);
|
||||
}.bind(self));
|
||||
|
||||
picker.display();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create user evidence competency handler.
|
||||
*
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
UserEvidenceActions.prototype._createUserEvidenceCompetencyHandler = function(e) {
|
||||
e.preventDefault();
|
||||
var data = this._findEvidenceData($(e.target));
|
||||
this.createUserEvidenceCompetency(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a linked competency and reload.
|
||||
*
|
||||
* @param {Object} evidenceData Evidence data from evidence node.
|
||||
* @param {Number} competencyId The competency ID.
|
||||
*/
|
||||
UserEvidenceActions.prototype._doDeleteUserEvidenceCompetency = function(evidenceData, competencyId) {
|
||||
var self = this,
|
||||
calls = [];
|
||||
|
||||
calls.push({
|
||||
methodname: 'core_competency_delete_user_evidence_competency',
|
||||
args: {
|
||||
userevidenceid: evidenceData.id,
|
||||
competencyid: competencyId,
|
||||
}
|
||||
});
|
||||
|
||||
self._callAndRefresh(calls, evidenceData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a user evidence competency.
|
||||
*
|
||||
* @param {Object} evidenceData Evidence data from evidence node.
|
||||
* @param {Number} competencyId The competency ID.
|
||||
*/
|
||||
UserEvidenceActions.prototype.deleteUserEvidenceCompetency = function(evidenceData, competencyId) {
|
||||
this._doDeleteUserEvidenceCompetency(evidenceData, competencyId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete user evidence competency handler.
|
||||
*
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
UserEvidenceActions.prototype._deleteUserEvidenceCompetencyHandler = function(e) {
|
||||
var data = this._findEvidenceData($(e.currentTarget)),
|
||||
competencyId = $(e.currentTarget).data('id');
|
||||
e.preventDefault();
|
||||
this.deleteUserEvidenceCompetency(data, competencyId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send request review for user evidence competencies and reload the region.
|
||||
*
|
||||
* @param {Object} evidenceData Evidence data from evidence node.
|
||||
*/
|
||||
UserEvidenceActions.prototype._doReviewUserEvidenceCompetencies = function(evidenceData) {
|
||||
var self = this,
|
||||
calls = [{
|
||||
methodname: 'core_competency_request_review_of_user_evidence_linked_competencies',
|
||||
args: { id: evidenceData.id }
|
||||
}];
|
||||
self._callAndRefresh(calls, evidenceData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send request review for user evidence competencies.
|
||||
*
|
||||
* @param {Object} evidenceData Evidence data from evidence node.
|
||||
*/
|
||||
UserEvidenceActions.prototype.reviewUserEvidenceCompetencies = function(evidenceData) {
|
||||
var self = this,
|
||||
requests;
|
||||
|
||||
requests = ajax.call([{
|
||||
methodname: 'core_competency_read_user_evidence',
|
||||
args: { id: evidenceData.id }
|
||||
}]);
|
||||
|
||||
requests[0].done(function(evidence) {
|
||||
str.get_strings([
|
||||
{ key: 'confirm', component: 'moodle' },
|
||||
{ key: 'sendallcompetenciestoreview', component: 'tool_lp', param: evidence.name },
|
||||
{ key: 'confirm', component: 'moodle' },
|
||||
{ key: 'cancel', component: 'moodle' }
|
||||
]).done(function (strings) {
|
||||
notification.confirm(
|
||||
strings[0], // Confirm.
|
||||
strings[1], // Send all competencies in review for X?
|
||||
strings[2], // Confirm.
|
||||
strings[3], // Cancel.
|
||||
function() {
|
||||
self._doReviewUserEvidenceCompetencies(evidenceData);
|
||||
}.bind(self)
|
||||
);
|
||||
}).fail(notification.exception);
|
||||
}).fail(notification.exception);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Send request review for user evidence competencies handler.
|
||||
*
|
||||
* @param {Event} e The event.
|
||||
*/
|
||||
UserEvidenceActions.prototype._reviewUserEvidenceCompetenciesHandler = function(e) {
|
||||
e.preventDefault();
|
||||
var data = this._findEvidenceData($(e.target));
|
||||
this.reviewUserEvidenceCompetencies(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the evidence data from the evidence node.
|
||||
*
|
||||
* @param {Node} node The node to search from.
|
||||
* @return {Object} Evidence data.
|
||||
*/
|
||||
UserEvidenceActions.prototype._findEvidenceData = function(node) {
|
||||
var parent = node.parentsUntil($(this._region).parent(), this._evidenceNode),
|
||||
data;
|
||||
|
||||
if (parent.length != 1) {
|
||||
throw new Error('The evidence node was not located.');
|
||||
}
|
||||
|
||||
data = parent.data();
|
||||
if (typeof data === 'undefined' || typeof data.id === 'undefined') {
|
||||
throw new Error('Evidence data could not be found.');
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enhance a menu bar.
|
||||
*
|
||||
* @param {String} selector Menubar selector.
|
||||
*/
|
||||
UserEvidenceActions.prototype.enhanceMenubar = function(selector) {
|
||||
var self = this;
|
||||
Menubar.enhance(selector, {
|
||||
'[data-action="user-evidence-delete"]': self._deleteEvidenceHandler.bind(self),
|
||||
'[data-action="link-competency"]': self._createUserEvidenceCompetencyHandler.bind(self),
|
||||
'[data-action="send-competencies-review"]': self._reviewUserEvidenceCompetenciesHandler.bind(self),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the events in the region.
|
||||
*
|
||||
* At this stage this cannot be used with enhanceMenubar or multiple handlers
|
||||
* will be added to the same node.
|
||||
*/
|
||||
UserEvidenceActions.prototype.registerEvents = function() {
|
||||
var wrapper = $(this._region),
|
||||
self = this;
|
||||
|
||||
wrapper.find('[data-action="user-evidence-delete"]').click(self._deleteEvidenceHandler.bind(self));
|
||||
wrapper.find('[data-action="link-competency"]').click(self._createUserEvidenceCompetencyHandler.bind(self));
|
||||
wrapper.find('[data-action="delete-competency-link"]').click(self._deleteUserEvidenceCompetencyHandler.bind(self));
|
||||
wrapper.find('[data-action="send-competencies-review"]').click(self._reviewUserEvidenceCompetenciesHandler.bind(self));
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/user_evidence_actions */ UserEvidenceActions;
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
<?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/>.
|
||||
|
||||
|
||||
/**
|
||||
* Course competencies element.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
use core_competency\api;
|
||||
use core_competency\external\competency_exporter;
|
||||
require_once($CFG->libdir . '/form/autocomplete.php');
|
||||
|
||||
/**
|
||||
* Course competencies element.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_lp_course_competencies_form_element extends MoodleQuickForm_autocomplete {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $elementName Element name
|
||||
* @param mixed $elementLabel Label(s) for an element
|
||||
* @param array $options Options to control the element's display
|
||||
* @param mixed $attributes Either a typical HTML attribute string or an associative array.
|
||||
*/
|
||||
public function __construct($elementName=null, $elementLabel=null, $options=array(), $attributes=null) {
|
||||
global $OUTPUT;
|
||||
|
||||
if ($elementName == null) {
|
||||
// This is broken quickforms messing with the constructors.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($options['courseid'])) {
|
||||
throw new coding_exception('Course id is required for the course_competencies form element');
|
||||
}
|
||||
$courseid = $options['courseid'];
|
||||
|
||||
if (!empty($options['cmid'])) {
|
||||
$current = \core_competency\api::list_course_module_competencies_in_course_module($options['cmid']);
|
||||
$ids = array();
|
||||
foreach ($current as $coursemodulecompetency) {
|
||||
array_push($ids, $coursemodulecompetency->get_competencyid());
|
||||
}
|
||||
$this->setValue($ids);
|
||||
}
|
||||
|
||||
$competencies = api::list_course_competencies($courseid);
|
||||
$validoptions = array();
|
||||
|
||||
$context = context_course::instance($courseid);
|
||||
foreach ($competencies as $competency) {
|
||||
$exporter = new competency_exporter($competency['competency'], array('context' => $context));
|
||||
$templatecontext = array('competency' => $exporter->export($OUTPUT));
|
||||
$id = $competency['competency']->get_id();
|
||||
$validoptions[$id] = $OUTPUT->render_from_template('tool_lp/competency_summary', $templatecontext);
|
||||
}
|
||||
$attributes['tags'] = false;
|
||||
$attributes['multiple'] = 'multiple';
|
||||
parent::__construct($elementName, $elementLabel, $validoptions, $attributes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?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/>.
|
||||
|
||||
|
||||
/**
|
||||
* Course competency rule element.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
use core_competency\api;
|
||||
use core_competency\external\competency_exporter;
|
||||
use core_competency\course_module_competency;
|
||||
|
||||
require_once($CFG->libdir . '/form/select.php');
|
||||
|
||||
/**
|
||||
* Course competency rule element.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_lp_course_competency_rule_form_element extends MoodleQuickForm_select {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $elementName Element name
|
||||
* @param mixed $elementLabel Label(s) for an element
|
||||
* @param array $options Options to control the element's display
|
||||
* @param mixed $attributes Either a typical HTML attribute string or an associative array.
|
||||
*/
|
||||
public function __construct($elementName=null, $elementLabel=null, $options=array(), $attributes=null) {
|
||||
if ($elementName == null) {
|
||||
// This is broken quickforms messing with the constructors.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($options['cmid'])) {
|
||||
$cmid = $options['cmid'];
|
||||
|
||||
$current = \core_competency\api::list_course_module_competencies_in_course_module($cmid);
|
||||
|
||||
// Note: We just pick the outcome set on the first course_module_competency - because in our UI are are
|
||||
// forcing them to be all the same for each activity.
|
||||
if (!empty($current)) {
|
||||
$one = array_pop($current);
|
||||
$this->setValue($one->get_ruleoutcome());
|
||||
}
|
||||
}
|
||||
$validoptions = course_module_competency::get_ruleoutcome_list();
|
||||
parent::__construct($elementName, $elementLabel, $validoptions, $attributes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Course competency statistics class
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_lp;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core_competency\api;
|
||||
|
||||
/**
|
||||
* Course competency statistics class.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class course_competency_statistics {
|
||||
|
||||
/** @var $competencycount The number of competencies in the course */
|
||||
public $competencycount = 0;
|
||||
|
||||
/** @var $proficientcompetencycount The number of proficient competencies for the current user */
|
||||
public $proficientcompetencycount = 0;
|
||||
|
||||
/** @var $leastproficientcompetencies The competencies in this course that were proficient the least times */
|
||||
public $leastproficientcompetencies = array();
|
||||
|
||||
/**
|
||||
* Return the custom definition of the properties of this model.
|
||||
*
|
||||
* @param int $courseid The course we want to generate statistics for.
|
||||
*/
|
||||
public function __construct($courseid) {
|
||||
global $USER;
|
||||
|
||||
$this->competencycount = api::count_competencies_in_course($courseid);
|
||||
$this->proficientcompetencycount = api::count_proficient_competencies_in_course_for_user($courseid, $USER->id);
|
||||
$this->leastproficientcompetencies = api::get_least_proficient_competencies_for_course($courseid, 0, 3);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
<?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 exporting a cohort summary from an stdClass.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_lp\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderer_base;
|
||||
use moodle_url;
|
||||
|
||||
/**
|
||||
* Class for exporting a cohort summary from an stdClass.
|
||||
*
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cohort_summary_exporter extends \core_competency\external\exporter {
|
||||
|
||||
protected static function define_related() {
|
||||
// Cohorts can exist on a category context.
|
||||
return array('context' => '\\context');
|
||||
}
|
||||
|
||||
public static function define_properties() {
|
||||
return array(
|
||||
'id' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
'name' => array(
|
||||
'type' => PARAM_TEXT,
|
||||
),
|
||||
'idnumber' => array(
|
||||
'type' => PARAM_RAW, // ID numbers are plain text.
|
||||
'default' => ''
|
||||
),
|
||||
'visible' => array(
|
||||
'type' => PARAM_BOOL,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function define_other_properties() {
|
||||
return array(
|
||||
'contextname' => array(
|
||||
'type' => PARAM_TEXT
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
return array(
|
||||
'contextname' => $this->related['context']->get_context_name()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?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 exporting competency_path data.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Issam Taboubi <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_lp\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderer_base;
|
||||
use moodle_url;
|
||||
|
||||
/**
|
||||
* Class for exporting competency_path data.
|
||||
*
|
||||
* @copyright 2016 Issam Taboubi <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class competency_path_exporter extends \core_competency\external\exporter {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $related - related objects.
|
||||
*/
|
||||
public function __construct($related) {
|
||||
parent::__construct([], $related);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of properties.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_related() {
|
||||
return [
|
||||
'ancestors' => 'core_competency\\competency[]',
|
||||
'framework' => 'core_competency\\competency_framework',
|
||||
'context' => 'context'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of additional properties used only for display.
|
||||
*
|
||||
* @return array - Keys with their types.
|
||||
*/
|
||||
protected static function define_other_properties() {
|
||||
return [
|
||||
'ancestors' => [
|
||||
'type' => path_node_exporter::read_properties_definition(),
|
||||
'multiple' => true,
|
||||
],
|
||||
'framework' => [
|
||||
'type' => path_node_exporter::read_properties_definition()
|
||||
],
|
||||
'pluginbaseurl' => [
|
||||
'type' => PARAM_TEXT
|
||||
],
|
||||
'pagecontextid' => [
|
||||
'type' => PARAM_INT
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the additional values to inject while exporting.
|
||||
*
|
||||
* @param renderer_base $output The renderer.
|
||||
* @return array Keys are the property names, values are their values.
|
||||
*/
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
$result = new \stdClass();
|
||||
$ancestors = [];
|
||||
$nodescount = count($this->related['ancestors']);
|
||||
$i = 1;
|
||||
foreach ($this->related['ancestors'] as $competency) {
|
||||
$exporter = new path_node_exporter([
|
||||
'id' => $competency->get_id(),
|
||||
'name' => $competency->get_idnumber(),
|
||||
'position' => $i,
|
||||
'first' => $i == 1,
|
||||
'last' => $i == $nodescount
|
||||
], [
|
||||
'context' => $this->related['context'],
|
||||
]
|
||||
);
|
||||
$ancestors[] = $exporter->export($output);
|
||||
$i++;
|
||||
}
|
||||
$result->ancestors = $ancestors;
|
||||
$exporter = new path_node_exporter([
|
||||
'id' => $this->related['framework']->get_id(),
|
||||
'name' => $this->related['framework']->get_shortname(),
|
||||
'first' => 0,
|
||||
'last' => 0,
|
||||
'position' => -1
|
||||
], [
|
||||
'context' => $this->related['context']
|
||||
]
|
||||
);
|
||||
$result->framework = $exporter->export($output);
|
||||
$result->pluginbaseurl = (new moodle_url('/admin/tool/lp'))->out(true);
|
||||
$result->pagecontextid = $this->related['context']->id;
|
||||
return (array) $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?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 exporting competency data with the set of linked courses.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_lp\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use context_course;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use core_competency\competency_framework;
|
||||
use core_competency\external\competency_exporter;
|
||||
use core_competency\external\competency_framework_exporter;
|
||||
|
||||
/**
|
||||
* Class for exporting competency data with additional related data.
|
||||
*
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class competency_summary_exporter extends \core_competency\external\exporter {
|
||||
|
||||
protected static function define_related() {
|
||||
// We cache the context so it does not need to be retrieved from the framework every time.
|
||||
return array('context' => '\\context',
|
||||
'competency' => '\\core_competency\\competency',
|
||||
'framework' => '\\core_competency\\competency_framework',
|
||||
'linkedcourses' => '\\stdClass[]',
|
||||
'relatedcompetencies' => '\\core_competency\\competency[]');
|
||||
}
|
||||
|
||||
protected static function define_other_properties() {
|
||||
return array(
|
||||
'linkedcourses' => array(
|
||||
'type' => course_summary_exporter::read_properties_definition(),
|
||||
'multiple' => true
|
||||
),
|
||||
'relatedcompetencies' => array(
|
||||
'type' => competency_exporter::read_properties_definition(),
|
||||
'multiple' => true
|
||||
),
|
||||
'competency' => array(
|
||||
'type' => competency_exporter::read_properties_definition()
|
||||
),
|
||||
'framework' => array(
|
||||
'type' => competency_framework_exporter::read_properties_definition()
|
||||
),
|
||||
'hascourses' => array(
|
||||
'type' => PARAM_BOOL
|
||||
),
|
||||
'hasrelatedcompetencies' => array(
|
||||
'type' => PARAM_BOOL
|
||||
),
|
||||
'scaleid' => array(
|
||||
'type' => PARAM_INT
|
||||
),
|
||||
'scaleconfiguration' => array(
|
||||
'type' => PARAM_RAW
|
||||
),
|
||||
'taxonomyterm' => array(
|
||||
'type' => PARAM_TEXT
|
||||
),
|
||||
'comppath' => array(
|
||||
'type' => competency_path_exporter::read_properties_definition(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
$result = new stdClass();
|
||||
$context = $this->related['context'];
|
||||
|
||||
$courses = $this->related['linkedcourses'];
|
||||
$linkedcourses = array();
|
||||
foreach ($courses as $course) {
|
||||
$context = context_course::instance($course->id);
|
||||
$exporter = new course_summary_exporter($course, array('context' => $context));
|
||||
$courseexport = $exporter->export($output);
|
||||
array_push($linkedcourses, $courseexport);
|
||||
}
|
||||
$result->linkedcourses = $linkedcourses;
|
||||
$result->hascourses = count($linkedcourses) > 0;
|
||||
|
||||
$relatedcompetencies = array();
|
||||
foreach ($this->related['relatedcompetencies'] as $competency) {
|
||||
$exporter = new competency_exporter($competency, array('context' => $context));
|
||||
$competencyexport = $exporter->export($output);
|
||||
array_push($relatedcompetencies, $competencyexport);
|
||||
}
|
||||
$result->relatedcompetencies = $relatedcompetencies;
|
||||
$result->hasrelatedcompetencies = count($relatedcompetencies) > 0;
|
||||
|
||||
$competency = $this->related['competency'];
|
||||
$exporter = new competency_exporter($competency, array('context' => $context));
|
||||
$result->competency = $exporter->export($output);
|
||||
|
||||
$exporter = new competency_framework_exporter($this->related['framework']);
|
||||
$result->framework = $exporter->export($output);
|
||||
$scaleconfiguration = $this->related['framework']->get_scaleconfiguration();
|
||||
$scaleid = $this->related['framework']->get_scaleid();
|
||||
if ($competency->get_scaleid()) {
|
||||
$scaleconfiguration = $competency->get_scaleconfiguration();
|
||||
$scaleid = $competency->get_scaleid();
|
||||
}
|
||||
$result->scaleconfiguration = $scaleconfiguration;
|
||||
$result->scaleid = $scaleid;
|
||||
|
||||
$level = $competency->get_level();
|
||||
$taxonomy = $this->related['framework']->get_taxonomy($level);
|
||||
$result->taxonomyterm = (string) (competency_framework::get_taxonomies_list()[$taxonomy]);
|
||||
|
||||
// Competency path.
|
||||
$exporter = new competency_path_exporter([
|
||||
'ancestors' => $competency->get_ancestors(),
|
||||
'framework' => $this->related['framework'],
|
||||
'context' => $context
|
||||
]);
|
||||
$result->comppath = $exporter->export($output);
|
||||
|
||||
return (array) $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?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 exporting a course competency statistics summary.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_lp\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderer_base;
|
||||
use moodle_url;
|
||||
use core_competency\external\competency_exporter;
|
||||
|
||||
/**
|
||||
* Class for exporting a course competency statistics summary.
|
||||
*
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class course_competency_statistics_exporter extends \core_competency\external\exporter {
|
||||
|
||||
public static function define_properties() {
|
||||
return array(
|
||||
'competencycount' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
'proficientcompetencycount' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public static function define_other_properties() {
|
||||
return array(
|
||||
'proficientcompetencypercentage' => array(
|
||||
'type' => PARAM_FLOAT
|
||||
),
|
||||
'proficientcompetencypercentageformatted' => array(
|
||||
'type' => PARAM_RAW
|
||||
),
|
||||
'leastproficient' => array(
|
||||
'type' => competency_exporter::read_properties_definition(),
|
||||
'multiple' => true
|
||||
),
|
||||
'leastproficientcount' => array(
|
||||
'type' => PARAM_INT
|
||||
),
|
||||
'canbegradedincourse' => array(
|
||||
'type' => PARAM_BOOL
|
||||
),
|
||||
'canmanagecoursecompetencies' => array(
|
||||
'type' => PARAM_BOOL
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected static function define_related() {
|
||||
return array('context' => 'context');
|
||||
}
|
||||
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
$proficientcompetencypercentage = 0;
|
||||
$proficientcompetencypercentageformatted = '';
|
||||
if ($this->data->competencycount > 0) {
|
||||
$proficientcompetencypercentage = ((float) $this->data->proficientcompetencycount
|
||||
/ (float) $this->data->competencycount) * 100.0;
|
||||
$proficientcompetencypercentageformatted = format_float($proficientcompetencypercentage);
|
||||
}
|
||||
$competencies = array();
|
||||
$contextcache = array();
|
||||
foreach ($this->data->leastproficientcompetencies as $competency) {
|
||||
if (!isset($contextcache[$competency->get_competencyframeworkid()])) {
|
||||
$contextcache[$competency->get_competencyframeworkid()] = $competency->get_context();
|
||||
}
|
||||
$context = $contextcache[$competency->get_competencyframeworkid()];
|
||||
$exporter = new competency_exporter($competency, array('context' => $context));
|
||||
$competencies[] = $exporter->export($output);
|
||||
}
|
||||
return array(
|
||||
'proficientcompetencypercentage' => $proficientcompetencypercentage,
|
||||
'proficientcompetencypercentageformatted' => $proficientcompetencypercentageformatted,
|
||||
'leastproficient' => $competencies,
|
||||
'leastproficientcount' => count($competencies),
|
||||
'canbegradedincourse' => has_capability('moodle/competency:coursecompetencygradable', $this->related['context']),
|
||||
'canmanagecoursecompetencies' => has_capability('moodle/competency:coursecompetencymanage', $this->related['context'])
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?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 exporting a course module summary from an stdClass.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_lp\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderer_base;
|
||||
|
||||
|
||||
/**
|
||||
* Class for exporting a course module summary from a cm_info class.
|
||||
*
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class course_module_summary_exporter extends \core_competency\external\exporter {
|
||||
|
||||
protected static function define_related() {
|
||||
return array('cm' => 'cm_info');
|
||||
}
|
||||
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
$context = $this->related['cm']->context;
|
||||
|
||||
return array(
|
||||
'id' => $this->related['cm']->id,
|
||||
'name' => external_format_string($this->related['cm']->name, $context->id),
|
||||
'url' => $this->related['cm']->url->out(),
|
||||
'iconurl' => $this->related['cm']->get_icon_url()->out()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public static function define_other_properties() {
|
||||
return array(
|
||||
'id' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
'name' => array(
|
||||
'type' => PARAM_TEXT
|
||||
),
|
||||
'url' => array(
|
||||
'type' => PARAM_URL
|
||||
),
|
||||
'iconurl' => array(
|
||||
'type' => PARAM_URL
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?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 exporting a course summary from an stdClass.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_lp\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderer_base;
|
||||
use moodle_url;
|
||||
|
||||
/**
|
||||
* Class for exporting a course summary from an stdClass.
|
||||
*
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class course_summary_exporter extends \core_competency\external\exporter {
|
||||
|
||||
protected static function define_related() {
|
||||
// We cache the context so it does not need to be retrieved from the course.
|
||||
return array('context' => '\\context');
|
||||
}
|
||||
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
return array(
|
||||
'viewurl' => (new moodle_url('/course/view.php', array('id' => $this->data->id)))->out(false)
|
||||
);
|
||||
}
|
||||
|
||||
public static function define_properties() {
|
||||
return array(
|
||||
'id' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
'fullname' => array(
|
||||
'type' => PARAM_TEXT,
|
||||
),
|
||||
'shortname' => array(
|
||||
'type' => PARAM_TEXT,
|
||||
),
|
||||
'idnumber' => array(
|
||||
'type' => PARAM_TEXT,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function define_other_properties() {
|
||||
return array(
|
||||
'viewurl' => array(
|
||||
'type' => PARAM_URL,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?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 exporting path_node data.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Issam Taboubi <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_lp\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
/**
|
||||
* Class for exporting path_node data.
|
||||
*
|
||||
* @copyright 2016 Issam Taboubi <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class path_node_exporter extends \core_competency\external\exporter {
|
||||
|
||||
/**
|
||||
* Return the list of properties.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties() {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => PARAM_INT,
|
||||
'null' => NULL_ALLOWED
|
||||
],
|
||||
'name' => [
|
||||
'type' => PARAM_TEXT
|
||||
],
|
||||
'first' => [
|
||||
'type' => PARAM_BOOL
|
||||
],
|
||||
'last' => [
|
||||
'type' => PARAM_BOOL
|
||||
],
|
||||
'position' => [
|
||||
'type' => PARAM_INT
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<?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 exporting a template statistics summary.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_lp\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderer_base;
|
||||
use moodle_url;
|
||||
use core_competency\external\competency_exporter;
|
||||
|
||||
/**
|
||||
* Class for exporting a cohort summary from an stdClass.
|
||||
*
|
||||
* @copyright 2015 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class template_statistics_exporter extends \core_competency\external\exporter {
|
||||
|
||||
public static function define_properties() {
|
||||
return array(
|
||||
'competencycount' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
'unlinkedcompetencycount' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
'plancount' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
'completedplancount' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
'usercompetencyplancount' => array(
|
||||
'type' => PARAM_INT,
|
||||
),
|
||||
'proficientusercompetencyplancount' => array(
|
||||
'type' => PARAM_INT,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function define_other_properties() {
|
||||
return array(
|
||||
'linkedcompetencypercentage' => array(
|
||||
'type' => PARAM_FLOAT
|
||||
),
|
||||
'linkedcompetencypercentageformatted' => array(
|
||||
'type' => PARAM_RAW
|
||||
),
|
||||
'linkedcompetencycount' => array(
|
||||
'type' => PARAM_INT
|
||||
),
|
||||
'completedplanpercentage' => array(
|
||||
'type' => PARAM_FLOAT
|
||||
),
|
||||
'completedplanpercentageformatted' => array(
|
||||
'type' => PARAM_RAW
|
||||
),
|
||||
'proficientusercompetencyplanpercentage' => array(
|
||||
'type' => PARAM_FLOAT
|
||||
),
|
||||
'proficientusercompetencyplanpercentageformatted' => array(
|
||||
'type' => PARAM_RAW
|
||||
),
|
||||
'leastproficient' => array(
|
||||
'type' => competency_exporter::read_properties_definition(),
|
||||
'multiple' => true
|
||||
),
|
||||
'leastproficientcount' => array(
|
||||
'type' => PARAM_INT
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
$linkedcompetencycount = $this->data->competencycount - $this->data->unlinkedcompetencycount;
|
||||
if ($linkedcompetencycount < 0) {
|
||||
// Should never happen.
|
||||
$linkedcompetencycount = 0;
|
||||
}
|
||||
$linkedcompetencypercentage = 0;
|
||||
$linkedcompetencypercentageformatted = '';
|
||||
if ($this->data->competencycount > 0) {
|
||||
$linkedcompetencypercentage = ((float) $linkedcompetencycount / (float) $this->data->competencycount) * 100.0;
|
||||
$linkedcompetencypercentageformatted = format_float($linkedcompetencypercentage);
|
||||
}
|
||||
$completedplanpercentage = 0;
|
||||
$completedplanpercentageformatted = '';
|
||||
if ($this->data->plancount > 0) {
|
||||
$completedplanpercentage = ((float) $this->data->completedplancount / (float) $this->data->plancount) * 100.0;
|
||||
$completedplanpercentageformatted = format_float($completedplanpercentage);
|
||||
}
|
||||
$proficientusercompetencyplanpercentage = 0;
|
||||
$proficientusercompetencyplanpercentageformatted = '';
|
||||
if ($this->data->usercompetencyplancount > 0) {
|
||||
$proficientusercompetencyplanpercentage = ((float) $this->data->proficientusercompetencyplancount
|
||||
/ (float) $this->data->usercompetencyplancount) * 100.0;
|
||||
$proficientusercompetencyplanpercentageformatted = format_float($proficientusercompetencyplanpercentage);
|
||||
}
|
||||
$competencies = array();
|
||||
$contextcache = array();
|
||||
foreach ($this->data->leastproficientcompetencies as $competency) {
|
||||
if (!isset($contextcache[$competency->get_competencyframeworkid()])) {
|
||||
$contextcache[$competency->get_competencyframeworkid()] = $competency->get_context();
|
||||
}
|
||||
$context = $contextcache[$competency->get_competencyframeworkid()];
|
||||
$exporter = new competency_exporter($competency, array('context' => $context));
|
||||
$competencies[] = $exporter->export($output);
|
||||
}
|
||||
return array(
|
||||
'linkedcompetencycount' => $linkedcompetencycount,
|
||||
'linkedcompetencypercentage' => $linkedcompetencypercentage,
|
||||
'linkedcompetencypercentageformatted' => $linkedcompetencypercentageformatted,
|
||||
'completedplanpercentage' => $completedplanpercentage,
|
||||
'completedplanpercentageformatted' => $completedplanpercentageformatted,
|
||||
'proficientusercompetencyplanpercentage' => $proficientusercompetencyplanpercentage,
|
||||
'proficientusercompetencyplanpercentageformatted' => $proficientusercompetencyplanpercentageformatted,
|
||||
'leastproficient' => $competencies,
|
||||
'leastproficientcount' => count($competencies)
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user