. /** * Class for loading/storing competency frameworks from the DB. * * @package tool_lp * @copyright 2015 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace tool_lp; use stdClass; use context; use context_helper; use context_system; use context_course; use context_user; use coding_exception; use required_capability_exception; /** * Class for doing things with competency frameworks. * * @copyright 2015 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class api { /** * Create a competency from a record containing all the data for the class. * * Requires tool/lp:competencymanage 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_competency(stdClass $record) { $competency = new competency(0, $record); // First we do a permissions check. require_capability('tool/lp:competencymanage', $competency->get_context()); // OK - all set. $id = $competency->create(); return $competency; } /** * Delete a competency by id. * * Requires tool/lp:competencymanage capability at the system context. * * @param int $id The record to delete. This will delete alot of related data - you better be sure. * @return boolean */ public static function delete_competency($id) { $competency = new competency($id); // First we do a permissions check. require_capability('tool/lp:competencymanage', $competency->get_context()); // OK - all set. return $competency->delete(); } /** * Reorder this competency. * * Requires tool/lp:competencymanage capability at the system context. * * @param int $id The id of the competency to move. * @return boolean */ public static function move_down_competency($id) { $current = new competency($id); // First we do a permissions check. require_capability('tool/lp:competencymanage', $current->get_context()); $max = self::count_competencies(array('parentid' => $current->get_parentid(), 'competencyframeworkid' => $current->get_competencyframeworkid())); if ($max > 0) { $max--; } $sortorder = $current->get_sortorder(); if ($sortorder >= $max) { return false; } $sortorder = $sortorder + 1; $current->set_sortorder($sortorder); $filters = array('parentid' => $current->get_parentid(), 'competencyframeworkid' => $current->get_competencyframeworkid(), 'sortorder' => $sortorder); $children = self::list_competencies($filters, 'id'); foreach ($children as $needtoswap) { $needtoswap->set_sortorder($sortorder - 1); $needtoswap->update(); } // OK - all set. $current->update(); } /** * Reorder this competency. * * Requires tool/lp:competencymanage capability at the system context. * * @param int $id The id of the competency to move. * @return boolean */ public static function move_up_competency($id) { $current = new competency($id); // First we do a permissions check. require_capability('tool/lp:competencymanage', $current->get_context()); $sortorder = $current->get_sortorder(); if ($sortorder == 0) { return false; } $sortorder = $sortorder - 1; $current->set_sortorder($sortorder); $filters = array('parentid' => $current->get_parentid(), 'competencyframeworkid' => $current->get_competencyframeworkid(), 'sortorder' => $sortorder); $children = self::list_competencies($filters, 'id'); foreach ($children as $needtoswap) { $needtoswap->set_sortorder($sortorder + 1); $needtoswap->update(); } // OK - all set. return $current->update(); } /** * Move this competency so it sits in a new parent. * * Requires tool/lp:competencymanage capability at the system context. * * @param int $id The id of the competency to move. * @param int $newparentid The new parent id for the competency. * @return boolean */ public static function set_parent_competency($id, $newparentid) { $current = new competency($id); // First we do a permissions check. require_capability('tool/lp:competencymanage', $current->get_context()); // This will throw an exception if the parent does not exist. // Check the current one too. $parentframeworkid = $current->get_competencyframeworkid(); $parentpath = '/0/'; if ($newparentid) { $parent = new competency($newparentid); $parentframeworkid = $parent->get_competencyframeworkid(); $parentpath = $parent->get_path(); } // If we are moving a node to a child of itself, promote all the child nodes by one level. $newparents = explode('/', $parentpath); if (in_array($current->get_id(), $newparents)) { $filters = array('parentid' => $current->get_id(), 'competencyframeworkid' => $current->get_competencyframeworkid()); $children = self::list_competencies($filters, 'id'); foreach ($children as $child) { $child->set_parentid($current->get_parentid()); $child->update(); } } $current->set_parentid($newparentid); // OK - all set. return $current->update(); } /** * Update the details for a competency. * * Requires tool/lp:competencymanage capability at the system context. * * @param stdClass $record The new details for the competency. * Note - must contain an id that points to the competency to update. * * @return boolean */ public static function update_competency($record) { $competency = new competency($record->id); // First we do a permissions check. require_capability('tool/lp:competencymanage', $competency->get_context()); // Some things should not be changed in an update - they should use a more specific method. $record->sortorder = $competency->get_sortorder(); $record->parentid = $competency->get_parentid(); $record->competencyframeworkid = $competency->get_competencyframeworkid(); $competency->from_record($record); require_capability('tool/lp:competencymanage', $competency->get_context()); // OK - all set. return $competency->update(); } /** * Read a the details for a single competency and return a record. * * Requires tool/lp:competencyread capability at the system context. * * @param int $id The id of the competency to read. * @param bool $includerelated Include related tags or not. * @return stdClass */ public static function read_competency($id, $includerelated = false) { $competency = new competency($id); // First we do a permissions check. $context = $competency->get_context(); if (!has_any_capability(array('tool/lp:competencyread', 'tool/lp:competencymanage'), $context)) { throw new required_capability_exception($context, 'tool/lp:competencyread', 'nopermissions', ''); } // OK - all set. if ($includerelated) { $relatedcompetency = new related_competency(); if ($related = $relatedcompetency->list_relations($id)) { $competency->relatedcompetencies = $related; } } return $competency; } /** * Perform a text search based and return all results and their parents. * * Requires tool/lp:competencyread capability at the framework context. * * @param string $textsearch A string to search for. * @param int $competencyframeworkid The id of the framework to limit the search. * @return array of competencies */ public static function search_competencies($textsearch, $competencyframeworkid) { $framework = new competency_framework($competencyframeworkid); // First we do a permissions check. $context = $framework->get_context(); if (!has_any_capability(array('tool/lp:competencyread', 'tool/lp:competencymanage'), $context)) { throw new required_capability_exception($context, 'tool/lp:competencyread', 'nopermissions', ''); } // OK - all set. $competencies = competency::search($textsearch, $competencyframeworkid); return $competencies; } /** * Perform a search based on the provided filters and return a paginated list of records. * * Requires tool/lp:competencyread capability at some context. * * @param array $filters A list of filters to apply to the list. * @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 competencies */ public static function list_competencies($filters, $sort = '', $order = 'ASC', $skip = 0, $limit = 0) { if (!isset($filters['competencyframeworkid'])) { $context = context_system::instance(); } else { $framework = new competency_framework($filters['competencyframeworkid']); $context = $framework->get_context(); } // First we do a permissions check. if (!has_any_capability(array('tool/lp:competencyread', 'tool/lp:competencymanage'), $context)) { throw new required_capability_exception($context, 'tool/lp:competencyread', 'nopermissions', ''); } // OK - all set. return competency::get_records($filters, $sort, $order, $skip, $limit); } /** * Perform a search based on the provided filters and return a paginated list of records. * * Requires tool/lp:competencyread capability at some context. * * @param array $filters A list of filters to apply to the list. * @return int */ public static function count_competencies($filters) { if (!isset($filters['competencyframeworkid'])) { $context = context_system::instance(); } else { $framework = new competency_framework($filters['competencyframeworkid']); $context = $framework->get_context(); } // First we do a permissions check. if (!has_any_capability(array('tool/lp:competencyread', 'tool/lp:competencymanage'), $context)) { throw new required_capability_exception($context, 'tool/lp:competencyread', 'nopermissions', ''); } // OK - all set. return competency::count_records($filters); } /** * Create a competency framework from a record containing all the data for the class. * * Requires tool/lp:competencymanage capability at the system context. * * @param stdClass $record Record containing all the data for an instance of the class. * @return competency_framework */ public static function create_framework(stdClass $record) { $framework = new competency_framework(0, $record); require_capability('tool/lp:competencymanage', $framework->get_context()); // Account for different formats of taxonomies. if (isset($record->taxonomies)) { $framework->set_taxonomies($record->taxonomies); } $id = $framework->create(); return $framework; } /** * Duplicate a competency framework by id. * * Requires tool/lp:competencymanage capability at the system context. * * @param int $id The record to duplicate. All competencies associated and related will be duplicated. * @return competency_framework the framework duplicated */ public static function duplicate_framework($id) { $framework = new competency_framework($id); require_capability('tool/lp:competencymanage', $framework->get_context()); // Get a uniq idnumber based on the origin framework. $idnumber = competency_framework::get_unused_idnumber($framework->get_idnumber()); $framework->set_idnumber($idnumber); // Adding the suffix copy to the shortname. $framework->set_shortname(get_string('duplicateditemname', 'tool_lp', $framework->get_shortname())); $framework->set_id(0); $framework->create(); // Array that match the old competencies ids with the new one to use when copying related competencies. $matchids = self::duplicate_competency_tree($framework->get_id(), competency::get_framework_tree($id), 0, 0); // Copy the related competencies. $relcomps = related_competency::get_multiple_relations(array_keys($matchids)); foreach ($relcomps as $relcomp) { $compid = $relcomp->get_competencyid(); $relcompid = $relcomp->get_relatedcompetencyid(); if (isset($matchids[$compid]) && isset($matchids[$relcompid])) { $newcompid = $matchids[$compid]; $newrelcompid = $matchids[$relcompid]; if ($newcompid < $newrelcompid) { $relcomp->set_competencyid($newcompid); $relcomp->set_relatedcompetencyid($newrelcompid); } else { $relcomp->set_competencyid($newrelcompid); $relcomp->set_relatedcompetencyid($newcompid); } $relcomp->set_id(0); $relcomp->create(); } else { // Debugging message when there is no match found. debugging('related competency id not found'); } } return $framework; } /** * Delete a competency framework by id. * * Requires tool/lp:competencymanage capability at the system context. * * @param int $id The record to delete. This will delete alot of related data - you better be sure. * @return boolean */ public static function delete_framework($id) { $framework = new competency_framework($id); require_capability('tool/lp:competencymanage', $framework->get_context()); return $framework->delete(); } /** * Update the details for a competency framework. * * Requires tool/lp:competencymanage capability at the system context. * * @param stdClass $record The new details for the framework. Note - must contain an id that points to the framework to update. * @return boolean */ public static function update_framework($record) { $framework = new competency_framework($record->id); // Check the permissions before update. require_capability('tool/lp:competencymanage', $framework->get_context()); // Account for different formats of taxonomies. $framework->from_record($record); if (isset($record->taxonomies)) { $framework->set_taxonomies($record->taxonomies); } return $framework->update(); } /** * Read a the details for a single competency framework and return a record. * * Requires tool/lp:competencyread capability at the system context. * * @param int $id The id of the framework to read. * @return competency_framework */ public static function read_framework($id) { $framework = new competency_framework($id); if (!has_any_capability(array('tool/lp:competencyread', 'tool/lp:competencymanage'), $framework->get_context())) { throw new required_capability_exception($framework->get_context(), 'tool/lp:competencyread', 'nopermissions', ''); } return $framework; } /** * Perform a search based on the provided filters and return a paginated list of records. * * Requires tool/lp:competencyread 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) * @param context $context The parent context of the frameworks. * @param string $includes Defines what other contexts to fetch frameworks from. * Accepted values are: * - children: All descendants * - parents: All parents, grand parents, etc... * - self: Context passed only. * @return array of competency_framework */ public static function list_frameworks($sort, $order, $skip, $limit, $context, $includes = 'children') { global $DB; // Get all the relevant contexts. $contexts = self::get_related_contexts($context, $includes, array('tool/lp:competencyread', 'tool/lp:competencymanage')); if (empty($contexts)) { throw new required_capability_exception($context, 'tool/lp:competencyread', 'nopermissions', ''); } // OK - all set. list($insql, $inparams) = $DB->get_in_or_equal(array_keys($contexts), SQL_PARAMS_NAMED); return competency_framework::get_records_select("contextid $insql", $inparams, $sort . ' ' . $order, '*', $skip, $limit); } /** * Perform a search based on the provided filters and return a paginated list of records. * * Requires tool/lp:competencyread capability at the system context. * * @param context $context The parent context of the frameworks. * @param string $includes Defines what other contexts to fetch frameworks from. * Accepted values are: * - children: All descendants * - parents: All parents, grand parents, etc... * - self: Context passed only. * @return int */ public static function count_frameworks($context, $includes) { global $DB; // Get all the relevant contexts. $contexts = self::get_related_contexts($context, $includes, array('tool/lp:competencyread', 'tool/lp:competencymanage')); if (empty($contexts)) { throw new required_capability_exception($context, 'tool/lp:competencyread', 'nopermissions', ''); } // OK - all set. list($insql, $inparams) = $DB->get_in_or_equal(array_keys($contexts), SQL_PARAMS_NAMED); return competency_framework::count_records_select("contextid $insql", $inparams); } /** * Fetches all the relevant contexts. * * Note: This currently only supports system, category and user contexts. However user contexts * behave a bit differently and will fallback on the system context. This is what makes the most * sense because a user context does not have descendants, and only has system as a parent. * * @param context $context The context to start from. * @param string $includes Defines what other contexts to find. * Accepted values are: * - children: All descendants * - parents: All parents, grand parents, etc... * - self: Context passed only. * @param array $hasanycapability Array of capabilities passed to {@link has_any_capability()} in each context. * @return context[] An array of contexts where keys are context IDs. */ public static function get_related_contexts($context, $includes, array $hasanycapability = null) { global $DB; if (!in_array($includes, array('children', 'parents', 'self'))) { throw new coding_exception('Invalid parameter value for \'includes\'.'); } // If context user swap it for the context_system. if ($context->contextlevel == CONTEXT_USER) { $context = context_system::instance(); } $contexts = array($context->id => $context); if ($includes == 'children') { $params = array('coursecatlevel' => CONTEXT_COURSECAT, 'path' => $context->path . '/%'); $pathlike = $DB->sql_like('path', ':path'); $sql = "contextlevel = :coursecatlevel AND $pathlike"; $rs = $DB->get_recordset_select('context', $sql, $params); foreach ($rs as $record) { $ctxid = $record->id; context_helper::preload_from_record($record); $contexts[$ctxid] = context::instance_by_id($ctxid); } $rs->close(); } else if ($includes == 'parents') { $children = $context->get_parent_contexts(); foreach ($children as $ctx) { $contexts[$ctx->id] = $ctx; } } // Filter according to the capabilities required. if (!empty($hasanycapability)) { foreach ($contexts as $key => $ctx) { if (!has_any_capability($hasanycapability, $ctx)) { unset($contexts[$key]); } } } return $contexts; } /** * Count all the courses using a competency. * * @param int $competencyid The id of the competency to check. * @return int */ public static function count_courses_using_competency($competencyid) { // OK - all set. $courses = course_competency::list_courses_min($competencyid); $count = 0; // Now check permissions on each course. foreach ($courses as $course) { $context = context_course::instance($course->id); $capabilities = array('tool/lp:coursecompetencyread', 'tool/lp:coursecompetencymanage'); if (!has_any_capability($capabilities, $context)) { continue; } if (!$course->visible && !has_capability('course:viewhidden', $context)) { continue; } $count++; } return $count; } /** * List all the courses using a competency. * * @param int $competencyid The id of the competency to check. * @return array[stdClass] Array of stdClass containing id and shortname. */ public static function list_courses_using_competency($competencyid) { // OK - all set. $courses = course_competency::list_courses($competencyid); $result = array(); // Now check permissions on each course. foreach ($courses as $id => $course) { $context = context_course::instance($course->id); $capabilities = array('tool/lp:coursecompetencyread', 'tool/lp:coursecompetencymanage'); if (!has_any_capability($capabilities, $context)) { unset($courses[$id]); continue; } if (!$course->visible && !has_capability('course:viewhidden', $context)) { unset($courses[$id]); continue; } $course->fullnameformatted = format_text($course->fullname, array('context' => $context)); $course->shortnameformatted = format_text($course->shortname, array('context' => $context)); array_push($result, $course); } return $result; } /** * Count all the competencies in a course. * * @param int $courseid The id of the course to check. * @return int */ public static function count_competencies_in_course($courseid) { // First we do a permissions check. $context = context_course::instance($courseid); $onlyvisible = 1; $capabilities = array('tool/lp:coursecompetencyread', 'tool/lp:coursecompetencymanage'); if (!has_any_capability($capabilities, $context)) { throw new required_capability_exception($context, 'tool/lp:coursecompetencyread', 'nopermissions', ''); } if (has_capability('tool/lp:coursecompetencymanage', $context)) { $onlyvisible = 0; } // OK - all set. return course_competency::count_competencies($courseid, $onlyvisible); } /** * List all the competencies in a course. * * @param int $courseid The id of the course to check. * @return array of competencies */ public static function list_competencies_in_course($courseid) { // First we do a permissions check. $context = context_course::instance($courseid); $onlyvisible = 1; $capabilities = array('tool/lp:coursecompetencyread', 'tool/lp:coursecompetencymanage'); if (!has_any_capability($capabilities, $context)) { throw new required_capability_exception($context, 'tool/lp:coursecompetencyread', 'nopermissions', ''); } if (has_capability('tool/lp:coursecompetencymanage', $context)) { $onlyvisible = 0; } // OK - all set. return course_competency::list_competencies($courseid, $onlyvisible); } /** * Add a competency to this course. * * @param int $courseid The id of the course * @param int $competencyid The id of the competency * @return bool */ public static function add_competency_to_course($courseid, $competencyid) { // First we do a permissions check. $context = context_course::instance($courseid); require_capability('tool/lp:coursecompetencymanage', $context); $record = new stdClass(); $record->courseid = $courseid; $record->competencyid = $competencyid; $competency = new competency(); $competency->set_id($competencyid); if (!$competency->read()) { throw new coding_exception('The competency does not exist'); } $coursecompetency = new course_competency(); $exists = $coursecompetency->get_records(array('courseid' => $courseid, 'competencyid' => $competencyid)); if (!$exists) { $coursecompetency->from_record($record); if ($coursecompetency->create()) { return true; } } return false; } /** * Remove a competency from this course. * * @param int $courseid The id of the course * @param int $competencyid The id of the competency * @return bool */ public static function remove_competency_from_course($courseid, $competencyid) { // First we do a permissions check. $context = context_course::instance($courseid); require_capability('tool/lp:coursecompetencymanage', $context); $record = new stdClass(); $record->courseid = $courseid; $record->competencyid = $competencyid; $competency = new competency(); $competency->set_id($competencyid); if (!$competency->read()) { throw new coding_exception('The competency does not exist'); } $coursecompetency = new course_competency(); $exists = $coursecompetency->get_records(array('courseid' => $courseid, 'competencyid' => $competencyid)); if ($exists) { $competency = array_pop($exists); return $competency->delete(); } return false; } /** * Move the course competency up or down in the display list. * * Requires tool/lp:coursecompetencymanage capability at the course context. * * @param int $courseid The course * @param int $competencyidfrom The id of the competency we are moving. * @param int $competencyidto The id of the competency we are moving to. * @return boolean */ public static function reorder_course_competency($courseid, $competencyidfrom, $competencyidto) { // First we do a permissions check. $context = context_course::instance($courseid); require_capability('tool/lp:coursecompetencymanage', $context); $down = true; $coursecompetency = new course_competency(); $matches = $coursecompetency->get_records(array('courseid' => $courseid, 'competencyid' => $competencyidfrom)); if (count($matches) == 0) { throw new coding_exception('The link does not exist'); } $competencyfrom = array_pop($matches); $matches = $coursecompetency->get_records(array('courseid' => $courseid, 'competencyid' => $competencyidto)); if (count($matches) == 0) { throw new coding_exception('The link does not exist'); } $competencyto = array_pop($matches); $all = $coursecompetency->get_records(array('courseid' => $courseid), 'sortorder', 'ASC', 0, 0); if ($competencyfrom->get_sortorder() > $competencyto->get_sortorder()) { // We are moving up, so put it before the "to" item. $down = false; } foreach ($all as $id => $coursecompetency) { $sort = $coursecompetency->get_sortorder(); if ($down && $sort > $competencyfrom->get_sortorder() && $sort <= $competencyto->get_sortorder()) { $coursecompetency->set_sortorder($coursecompetency->get_sortorder() - 1); $coursecompetency->update(); } else if (!$down && $sort >= $competencyto->get_sortorder() && $sort < $competencyfrom->get_sortorder()) { $coursecompetency->set_sortorder($coursecompetency->get_sortorder() + 1); $coursecompetency->update(); } } $competencyfrom->set_sortorder($competencyto->get_sortorder()); return $competencyfrom->update(); } /** * Create a learning plan template from a record containing all the data for the class. * * Requires tool/lp:templatemanage capability. * * @param stdClass $record Record containing all the data for an instance of the class. * @return template */ public static function create_template(stdClass $record) { $template = new template(0, $record); // First we do a permissions check. require_capability('tool/lp:templatemanage', $template->get_context()); // OK - all set. $id = $template->create(); return $template; } /** * Duplicate a learning plan template. * * Requires tool/lp:templatemanage capability at the template context. * * @param int $id the template id. * @return template */ public static function duplicate_template($id) { $template = new template($id); // First we do a permissions check. require_capability('tool/lp:templatemanage', $template->get_context()); // OK - all set. $competencies = template_competency::list_competencies($id, false); // Adding the suffix copy. $template->set_shortname(get_string('duplicateditemname', 'tool_lp', $template->get_shortname())); $template->set_id(0); $duplicatedtemplate = $template->create(); // Associate each competency for the duplicated template. foreach ($competencies as $competency) { self::add_competency_to_template($duplicatedtemplate->get_id(), $competency->get_id()); } return $duplicatedtemplate; } /** * Delete a learning plan template by id. * * Requires tool/lp:templatemanage capability. * * @param int $id The record to delete. * @return boolean */ public static function delete_template($id) { $template = new template($id); // First we do a permissions check. require_capability('tool/lp:templatemanage', $template->get_context()); // OK - all set. return $template->delete(); } /** * Update the details for a learning plan template. * * Requires tool/lp:templatemanage capability. * * @param stdClass $record The new details for the template. Note - must contain an id that points to the template to update. * @return boolean */ public static function update_template($record) { $template = new template($record->id); // First we do a permissions check. require_capability('tool/lp:templatemanage', $template->get_context()); if (isset($record->contextid) && $record->contextid != $template->get_contextid()) { throw new coding_exception('Changing the context of an existing tempalte is forbidden.'); } $template->from_record($record); return $template->update(); } /** * Read a the details for a single learning plan template and return a record. * * Requires tool/lp:templateread capability at the system context. * * @param int $id The id of the template to read. * @return template */ public static function read_template($id) { $template = new template($id); $context = $template->get_context(); // First we do a permissions check. $caps = array('tool/lp:templateread', 'tool/lp:templatemanage'); if (!has_any_capability($caps, $context)) { throw new required_capability_exception($context, 'tool/lp:templateread', 'nopermissions', ''); } // OK - all set. return $template; } /** * Perform a search based on the provided filters and return a paginated list of records. * * Requires tool/lp:templateread 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) * @param context $context The parent context of the frameworks. * @param string $includes Defines what other contexts to fetch frameworks from. * Accepted values are: * - children: All descendants * - parents: All parents, grand parents, etc... * - self: Context passed only. * @return array of competency_framework */ public static function list_templates($sort, $order, $skip, $limit, $context, $includes = 'children') { global $DB; // Get all the relevant contexts. $contexts = self::get_related_contexts($context, $includes, array('tool/lp:templateread', 'tool/lp:templatemanage')); // First we do a permissions check. if (empty($contexts)) { throw new required_capability_exception($context, 'tool/lp:templateread', 'nopermissions', ''); } // Make the order by. $orderby = ''; if (!empty($sort)) { $orderby = $sort . ' ' . $order; } // OK - all set. $template = new template(); list($insql, $inparams) = $DB->get_in_or_equal(array_keys($contexts), SQL_PARAMS_NAMED); return $template->get_records_select("contextid $insql", $inparams, $orderby, '*', $skip, $limit); } /** * Perform a search based on the provided filters and return how many results there are. * * Requires tool/lp:templateread capability at the system context. * * @param context $context The parent context of the frameworks. * @param string $includes Defines what other contexts to fetch frameworks from. * Accepted values are: * - children: All descendants * - parents: All parents, grand parents, etc... * - self: Context passed only. * @return int */ public static function count_templates($context, $includes) { global $DB; // First we do a permissions check. $contexts = self::get_related_contexts($context, $includes, array('tool/lp:templateread', 'tool/lp:templatemanage')); if (empty($contexts)) { throw new required_capability_exception($context, 'tool/lp:templateread', 'nopermissions', ''); } // OK - all set. $template = new template(); list($insql, $inparams) = $DB->get_in_or_equal(array_keys($contexts), SQL_PARAMS_NAMED); return $template->count_records_select("contextid $insql", $inparams); } /** * Count all the templates using a competency. * * @param int $competencyid The id of the competency to check. * @return int */ public static function count_templates_using_competency($competencyid) { // First we do a permissions check. $context = context_system::instance(); $onlyvisible = 1; $capabilities = array('tool/lp:templateread', 'tool/lp:templatemanage'); if (!has_any_capability($capabilities, $context)) { throw new required_capability_exception($context, 'tool/lp:templateread', 'nopermissions', ''); } if (has_capability('tool/lp:templatemanage', $context)) { $onlyvisible = 0; } // OK - all set. return template_competency::count_templates($competencyid, $onlyvisible); } /** * List all the learning plan templatesd using a competency. * * @param int $competencyid The id of the competency to check. * @return array[stdClass] Array of stdClass containing id and shortname. */ public static function list_templates_using_competency($competencyid) { // First we do a permissions check. $context = context_system::instance(); $onlyvisible = 1; $capabilities = array('tool/lp:templateread', 'tool/lp:templatemanage'); if (!has_any_capability($capabilities, $context)) { throw new required_capability_exception($context, 'tool/lp:templateread', 'nopermissions', ''); } if (has_capability('tool/lp:templatemanage', $context)) { $onlyvisible = 0; } // OK - all set. return template_competency::list_templates($competencyid, $onlyvisible); } /** * Count all the competencies in a learning plan template. * * @param int $templateid The id of the template to check. * @return int */ public static function count_competencies_in_template($templateid) { // First we do a permissions check. $template = new template($templateid); $context = $template->get_context(); $onlyvisible = 1; $capabilities = array('tool/lp:templateread', 'tool/lp:templatemanage'); if (!has_any_capability($capabilities, $context)) { throw new required_capability_exception($context, 'tool/lp:templateread', 'nopermissions', ''); } if (has_capability('tool/lp:templatemanage', $context)) { $onlyvisible = 0; } // OK - all set. return template_competency::count_competencies($templateid, $onlyvisible); } /** * List all the competencies in a template. * * @param int $templateid The id of the template to check. * @return array of competencies */ public static function list_competencies_in_template($templateid) { // First we do a permissions check. $template = new template($templateid); $context = $template->get_context(); $onlyvisible = 1; $capabilities = array('tool/lp:templateread', 'tool/lp:templatemanage'); if (!has_any_capability($capabilities, $context)) { throw new required_capability_exception($context, 'tool/lp:templateread', 'nopermissions', ''); } if (has_capability('tool/lp:templatemanage', $context)) { $onlyvisible = 0; } // OK - all set. return template_competency::list_competencies($templateid, $onlyvisible); } /** * Add a competency to this template. * * @param int $templateid The id of the template * @param int $competencyid The id of the competency * @return bool */ public static function add_competency_to_template($templateid, $competencyid) { // First we do a permissions check. $template = new template($templateid); require_capability('tool/lp:templatemanage', $template->get_context()); $record = new stdClass(); $record->templateid = $templateid; $record->competencyid = $competencyid; $competency = new competency(); $competency->set_id($competencyid); if (!$competency->read()) { throw new coding_exception('The competency does not exist'); } $exists = template_competency::get_records(array('templateid' => $templateid, 'competencyid' => $competencyid)); if (!$exists) { $templatecompetency = new template_competency(0, $record); $templatecompetency->create(); return true; } return false; } /** * Remove a competency from this template. * * @param int $templateid The id of the template * @param int $competencyid The id of the competency * @return bool */ public static function remove_competency_from_template($templateid, $competencyid) { // First we do a permissions check. $template = new template($templateid); require_capability('tool/lp:templatemanage', $template->get_context()); $record = new stdClass(); $record->templateid = $templateid; $record->competencyid = $competencyid; $competency = new competency($competencyid); $competency->set_id($competencyid); if (!$competency->read()) { throw new coding_exception('The competency does not exist'); } $exists = template_competency::get_records(array('templateid' => $templateid, 'competencyid' => $competencyid)); if ($exists) { $link = array_pop($exists); return $link->delete(); } return false; } /** * Move the template competency up or down in the display list. * * Requires tool/lp:templatemanage capability at the system context. * * @param int $templateid The template id * @param int $competencyidfrom The id of the competency we are moving. * @param int $competencyidto The id of the competency we are moving to. * @return boolean */ public static function reorder_template_competency($templateid, $competencyidfrom, $competencyidto) { // First we do a permissions check. $context = context_system::instance(); require_capability('tool/lp:templatemanage', $context); $down = true; $matches = template_competency::get_records(array('templateid' => $templateid, 'competencyid' => $competencyidfrom)); if (count($matches) == 0) { throw new coding_exception('The link does not exist'); } $competencyfrom = array_pop($matches); $matches = template_competency::get_records(array('templateid' => $templateid, 'competencyid' => $competencyidto)); if (count($matches) == 0) { throw new coding_exception('The link does not exist'); } $competencyto = array_pop($matches); $all = template_competency::get_records(array('templateid' => $templateid), 'sortorder', 'ASC', 0, 0); if ($competencyfrom->get_sortorder() > $competencyto->get_sortorder()) { // We are moving up, so put it before the "to" item. $down = false; } foreach ($all as $id => $templatecompetency) { $sort = $templatecompetency->get_sortorder(); if ($down && $sort > $competencyfrom->get_sortorder() && $sort <= $competencyto->get_sortorder()) { $templatecompetency->set_sortorder($templatecompetency->get_sortorder() - 1); $templatecompetency->update(); } else if (!$down && $sort >= $competencyto->get_sortorder() && $sort < $competencyfrom->get_sortorder()) { $templatecompetency->set_sortorder($templatecompetency->get_sortorder() + 1); $templatecompetency->update(); } } $competencyfrom->set_sortorder($competencyto->get_sortorder()); return $competencyfrom->update(); } /** * Lists user plans. * * @param int $userid * @return \tool_lp\plan[] */ public static function list_user_plans($userid) { global $USER; $select = 'userid = :userid'; $params = array('userid' => $userid); $context = context_user::instance($userid); // Check that we can read something here. if (!plan::can_read_user($userid) && !plan::can_read_user_draft($userid)) { throw new required_capability_exception($context, 'tool/lp:planview', 'nopermissions', ''); } // The user cannot view the drafts. if (!plan::can_read_user_draft($userid)) { $select .= ' AND status != :statusdraft'; $params['statusdraft'] = plan::STATUS_DRAFT; } // The user cannot view the non-drafts. if (!plan::can_read_user($userid)) { $select .= ' AND status = :statusdraft'; $params['statusdraft'] = plan::STATUS_DRAFT; } return plan::get_records_select($select, $params, 'timemodified DESC'); } /** * Creates a learning plan based on the provided data. * * @param stdClass $record * @return \tool_lp\plan */ public static function create_plan(stdClass $record) { global $USER; $plan = new plan(0, $record); if (!$plan->can_manage()) { $context = context_user::instance($plan->get_userid()); throw new required_capability_exception($context, 'tool/lp:planmanage', 'nopermissions', ''); } $plan->create(); return $plan; } /** * Updates a plan. * * @param stdClass $record * @return \tool_lp\plan */ public static function update_plan(stdClass $record) { global $DB; $plan = new plan($record->id); // Validate that the plan as it is can be managed. if (!$plan->can_manage()) { $context = context_user::instance($plan->get_userid()); throw new required_capability_exception($context, 'tool/lp:planmanage', 'nopermissions', ''); } // Prevent change of ownership as the capabilities are checked against that. if (isset($record->userid) && $plan->get_userid() != $record->userid) { throw new coding_exception('A plan cannot be transfered to another user'); } $beforestatus = $plan->get_status(); $plan->from_record($record); // Revalidate after the data has be injected. This handles status change, etc... if (!$plan->can_manage()) { $context = context_user::instance($plan->get_userid()); throw new required_capability_exception($context, 'tool/lp:planmanage', 'nopermissions', ''); } // Wrap the updates in a DB transaction. $transaction = $DB->start_delegated_transaction(); // Archive user competencies if the status of the plan is changed to complete. $mustarchivecompetencies = ($plan->get_status() == plan::STATUS_COMPLETE && $beforestatus != plan::STATUS_COMPLETE); if ($mustarchivecompetencies) { self::archive_user_competencies_in_plan($plan); } // Delete archived user competencies if the status of the plan is changed from complete to another status. $mustremovearchivedcompetencies = ($beforestatus == plan::STATUS_COMPLETE && $plan->get_status() != plan::STATUS_COMPLETE); if ($mustremovearchivedcompetencies) { self::remove_archived_user_competencies_in_plan($plan); } $plan->update(); $transaction->allow_commit(); return $plan; } /** * Returns a plan data. * * @param int $id * @return \tool_lp\plan */ public static function read_plan($id) { $plan = new plan($id); if (!$plan->can_read()) { $context = context_user::instance($plan->get_userid()); throw new required_capability_exception($context, 'tool/lp:planview', 'nopermissions', ''); } return $plan; } /** * Deletes a plan. * * @param int $id * @return bool Success? */ public static function delete_plan($id) { global $DB; $plan = new plan($id); if (!$plan->can_manage()) { $context = context_user::instance($plan->get_userid()); throw new required_capability_exception($context, 'tool/lp:planmanage', 'nopermissions', ''); } // Wrap the suppression in a DB transaction. $transaction = $DB->start_delegated_transaction(); // Delete archive user competencies if the status of the plan is complete. if ($plan->get_status() == plan::STATUS_COMPLETE) { self::remove_archived_user_competencies_in_plan($plan); } $success = $plan->delete(); $transaction->allow_commit(); return $success; } /** * List the competencies in a user plan. * * @param int $planorid The plan, or its ID. * @return array((object) array( * 'competency' => \tool_lp\competency, * 'usercompetency' => \tool_lp\user_competency * 'usercompetencyplan' => \tool_lp\user_competency_plan * )) * The values of of keys usercompetency and usercompetencyplan cannot be defined at the same time. */ public static function list_plan_competencies($planorid) { $plan = $planorid; if (!is_object($planorid)) { $plan = new plan($planorid); } if (!$plan->can_read()) { $context = context_user::instance($plan->get_userid()); throw new required_capability_exception($context, 'tool/lp:planview', 'nopermissions', ''); } $result = array(); $competencies = $plan->get_competencies(); // Get user competencies from user_competency_plan if the plan status is set to complete. $iscompletedplan = $plan->get_status() == plan::STATUS_COMPLETE; if ($iscompletedplan) { $usercompetencies = user_competency_plan::get_multiple($plan->get_userid(), $plan->get_id(), $competencies); $ucresultkey = 'usercompetencyplan'; } else { $usercompetencies = user_competency::get_multiple($plan->get_userid(), $competencies); $ucresultkey = 'usercompetency'; } // Build the return values. foreach ($competencies as $key => $competency) { $found = false; foreach ($usercompetencies as $uckey => $uc) { if ($uc->get_competencyid() == $competency->get_id()) { $found = true; unset($usercompetencies[$uckey]); break; } } if (!$found) { if ($iscompletedplan) { throw new coding_exception('A user competency plan is missing'); } else { $uc = user_competency::create_relation($plan->get_userid(), $competency->get_id()); } } $plancompetency = (object) array( 'competency' => $competency, 'usercompetency' => null, 'usercompetencyplan' => null ); $plancompetency->$ucresultkey = $uc; $result[] = $plancompetency; } return $result; } /** * Add a competency to a plan. * * @param int $planid The id of the plan * @param int $competencyid The id of the competency * @return bool */ public static function add_competency_to_plan($planid, $competencyid) { $plan = new plan($planid); // First we do a permissions check. if (!$plan->can_manage()) { throw new required_capability_exception($plan->get_context(), 'tool/lp:planmanage', 'nopermissions', ''); } else if ($plan->is_based_on_template()) { throw new coding_exception('A competency can not be added to a learning plan based on a template'); } $exists = plan_competency::get_record(array('planid' => $planid, 'competencyid' => $competencyid)); if (!$exists) { $record = new stdClass(); $record->planid = $planid; $record->competencyid = $competencyid; $plancompetency = new plan_competency(0, $record); $plancompetency->create(); } return true; } /** * Remove a competency from a plan. * * @param int $planid The plan id * @param int $competencyid The id of the competency * @return bool */ public static function remove_competency_from_plan($planid, $competencyid) { $plan = new plan($planid); // First we do a permissions check. if (!$plan->can_manage()) { $context = context_user::instance($plan->get_userid()); throw new required_capability_exception($context, 'tool/lp:planmanage', 'nopermissions', ''); } else if ($plan->is_based_on_template()) { throw new coding_exception('A competency can not be removed from a learning plan based on a template'); } $link = plan_competency::get_record(array('planid' => $planid, 'competencyid' => $competencyid)); if ($link) { return $link->delete(); } return false; } /** * Move the plan competency up or down in the display list. * * Requires tool/lp:planmanage capability at the system context. * * @param int $planid The plan id * @param int $competencyidfrom The id of the competency we are moving. * @param int $competencyidto The id of the competency we are moving to. * @return boolean */ public static function reorder_plan_competency($planid, $competencyidfrom, $competencyidto) { $plan = new plan($planid); // First we do a permissions check. if (!$plan->can_manage()) { $context = context_user::instance($plan->get_userid()); throw new required_capability_exception($context, 'tool/lp:planmanage', 'nopermissions', ''); } else if ($plan->is_based_on_template()) { throw new coding_exception('A competency can not be reordered in a learning plan based on a template'); } $down = true; $matches = plan_competency::get_records(array('planid' => $planid, 'competencyid' => $competencyidfrom)); if (count($matches) == 0) { throw new coding_exception('The link does not exist'); } $competencyfrom = array_pop($matches); $matches = plan_competency::get_records(array('planid' => $planid, 'competencyid' => $competencyidto)); if (count($matches) == 0) { throw new coding_exception('The link does not exist'); } $competencyto = array_pop($matches); $all = plan_competency::get_records(array('planid' => $planid), 'sortorder', 'ASC', 0, 0); if ($competencyfrom->get_sortorder() > $competencyto->get_sortorder()) { // We are moving up, so put it before the "to" item. $down = false; } foreach ($all as $id => $plancompetency) { $sort = $plancompetency->get_sortorder(); if ($down && $sort > $competencyfrom->get_sortorder() && $sort <= $competencyto->get_sortorder()) { $plancompetency->set_sortorder($plancompetency->get_sortorder() - 1); $plancompetency->update(); } else if (!$down && $sort >= $competencyto->get_sortorder() && $sort < $competencyfrom->get_sortorder()) { $plancompetency->set_sortorder($plancompetency->get_sortorder() + 1); $plancompetency->update(); } } $competencyfrom->set_sortorder($competencyto->get_sortorder()); return $competencyfrom->update(); } /** * List all the related competencies. * * @param int $competencyid The id of the competency to check. * @return competency[] */ public static function list_related_competencies($competencyid) { $competency = new competency($competencyid); require_capability('tool/lp:competencymanage', $competency->get_context()); return $competency->get_related_competencies(); } /** * Add a related competency. * * @param int $competencyid The id of the competency * @param int $relatedcompetencyid The id of the related competency. * @return bool False when create failed, true on success, or if the relation already existed. */ public static function add_related_competency($competencyid, $relatedcompetencyid) { $competency1 = new competency($competencyid); $competency2 = new competency($relatedcompetencyid); require_capability('tool/lp:competencymanage', $competency1->get_context()); $relatedcompetency = related_competency::get_relation($competency1->get_id(), $competency2->get_id()); if (!$relatedcompetency->get_id()) { $relatedcompetency->create(); return true; } return true; } /** * Remove a related competency. * * @param int $competencyid The id of the competency. * @param int $relatedcompetencyid The id of the related competency. * @return bool True when it was deleted, false when it wasn't or the relation doesn't exist. */ public static function remove_related_competency($competencyid, $relatedcompetencyid) { $competency = new competency($competencyid); // This only check if we have the permission in either competency because both competencies // should belong to the same framework. require_capability('tool/lp:competencymanage', $competency->get_context()); $relatedcompetency = related_competency::get_relation($competencyid, $relatedcompetencyid); if ($relatedcompetency->get_id()) { return $relatedcompetency->delete(); } return false; } /** * Recursively duplicate competencies from a tree, we start duplicating from parents to children to have a correct path. * This method does not copy the related competencies. * * @param int $frameworkid - framework id * @param competency[] $tree - array of competencies object * @param int $oldparent - old parent id * @param int $newparent - new parent id * @return array $matchids - List of old competencies ids matched with new ids. */ protected static function duplicate_competency_tree($frameworkid, $tree, $oldparent = 0, $newparent = 0) { $matchids = array(); foreach ($tree as $node) { if ($node->competency->get_parentid() == $oldparent) { $parentid = $node->competency->get_id(); // Create the competency. $competency = $node->competency; $competency->set_competencyframeworkid($frameworkid); $competency->set_parentid($newparent); $competency->set_path(''); $competency->set_id(0); $competency->create(); // Match the old id with the new one. $matchids[$parentid] = $competency->get_id(); if (!empty($node->children)) { // Duplicate children competency. $childrenids = self::duplicate_competency_tree($frameworkid, $node->children, $parentid, $competency->get_id()); // Array_merge does not keep keys when merging so we use the + operator. $matchids = $matchids + $childrenids; } } } return $matchids; } /** * Archive user competencies in a plan. * * @param int $plan The plan object. * @return void */ protected static function archive_user_competencies_in_plan($plan) { $competencies = $plan->get_competencies(); $usercompetencies = user_competency::get_multiple($plan->get_userid(), $competencies); foreach ($competencies as $competency) { $found = false; foreach ($usercompetencies as $uckey => $uc) { if ($uc->get_competencyid() == $competency->get_id()) { $found = true; $ucprecord = $uc->to_record(); $ucprecord->planid = $plan->get_id(); unset($ucprecord->id); unset($ucprecord->status); unset($ucprecord->reviewerid); $usercompetencyplan = new user_competency_plan(0, $ucprecord); $usercompetencyplan->create(); unset($usercompetencies[$uckey]); break; } } // If the user competency doesn't exist, we create a new relation in user_competency_plan. if (!$found) { $usercompetencyplan = user_competency_plan::create_relation($plan->get_userid(), $competency->get_id(), $plan->get_id()); $usercompetencyplan->create(); } } } /** * Delete archived user competencies in a plan. * * @param int $plan The plan object. * @return void */ protected static function remove_archived_user_competencies_in_plan($plan) { $competencies = $plan->get_competencies(); $usercompetenciesplan = user_competency_plan::get_multiple($plan->get_userid(), $plan->get_id(), $competencies); foreach ($usercompetenciesplan as $ucpkey => $ucp) { $ucp->delete(); } } }