first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+885
View File
@@ -0,0 +1,885 @@
<?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 loading/storing competencies from the DB.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use coding_exception;
use context_system;
use lang_string;
use stdClass;
require_once($CFG->libdir . '/grade/grade_scale.php');
/**
* Class for loading/storing competencies from the DB.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class competency extends persistent {
const TABLE = 'competency';
/** Outcome none. */
const OUTCOME_NONE = 0;
/** Outcome evidence. */
const OUTCOME_EVIDENCE = 1;
/** Outcome complete. */
const OUTCOME_COMPLETE = 2;
/** Outcome recommend. */
const OUTCOME_RECOMMEND = 3;
/** @var competency Object before update. */
protected $beforeupdate = null;
/** @var competency|null To store new parent. */
protected $newparent;
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'shortname' => array(
'type' => PARAM_TEXT
),
'idnumber' => array(
'type' => PARAM_RAW
),
'description' => array(
'default' => '',
'type' => PARAM_CLEANHTML
),
'descriptionformat' => array(
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
'type' => PARAM_INT,
'default' => FORMAT_HTML
),
'sortorder' => array(
'default' => 0,
'type' => PARAM_INT
),
'parentid' => array(
'default' => 0,
'type' => PARAM_INT
),
'path' => array(
'default' => '/0/',
'type' => PARAM_RAW
),
'ruleoutcome' => array(
'choices' => array(self::OUTCOME_NONE, self::OUTCOME_EVIDENCE, self::OUTCOME_COMPLETE, self::OUTCOME_RECOMMEND),
'default' => self::OUTCOME_NONE,
'type' => PARAM_INT
),
'ruletype' => array(
'type' => PARAM_RAW,
'default' => null,
'null' => NULL_ALLOWED
),
'ruleconfig' => array(
'default' => null,
'type' => PARAM_RAW,
'null' => NULL_ALLOWED
),
'scaleid' => array(
'default' => null,
'type' => PARAM_INT,
'null' => NULL_ALLOWED
),
'scaleconfiguration' => array(
'default' => null,
'type' => PARAM_RAW,
'null' => NULL_ALLOWED
),
'competencyframeworkid' => array(
'default' => 0,
'type' => PARAM_INT
),
);
}
/**
* Hook to execute before validate.
*
* @return void
*/
protected function before_validate() {
$this->beforeupdate = null;
$this->newparent = null;
// During update.
if ($this->get('id')) {
$this->beforeupdate = new competency($this->get('id'));
// The parent ID has changed.
if ($this->beforeupdate->get('parentid') != $this->get('parentid')) {
$this->newparent = $this->get_parent();
// Update path and sortorder.
$this->set_new_path($this->newparent);
$this->set_new_sortorder();
}
} else {
// During create.
$this->set_new_path();
// Always generate new sortorder when we create new competency.
$this->set_new_sortorder();
}
}
/**
* Hook to execute after an update.
*
* @param bool $result Whether or not the update was successful.
* @return void
*/
protected function after_update($result) {
global $DB;
if (!$result) {
$this->beforeupdate = null;
return;
}
// The parent ID has changed, we need to fix all the paths of the children.
if ($this->beforeupdate->get('parentid') != $this->get('parentid')) {
$beforepath = $this->beforeupdate->get('path') . $this->get('id') . '/';
$like = $DB->sql_like('path', '?');
$likesearch = $DB->sql_like_escape($beforepath) . '%';
$table = '{' . self::TABLE . '}';
$sql = "UPDATE $table SET path = REPLACE(path, ?, ?) WHERE " . $like;
$DB->execute($sql, array(
$beforepath,
$this->get('path') . $this->get('id') . '/',
$likesearch
));
// Resolving sortorder holes left after changing parent.
$table = '{' . self::TABLE . '}';
$sql = "UPDATE $table SET sortorder = sortorder -1 "
. " WHERE competencyframeworkid = ? AND parentid = ? AND sortorder > ?";
$DB->execute($sql, array($this->get('competencyframeworkid'),
$this->beforeupdate->get('parentid'),
$this->beforeupdate->get('sortorder')
));
}
$this->beforeupdate = null;
}
/**
* Hook to execute after a delete.
*
* @param bool $result Whether or not the delete was successful.
* @return void
*/
protected function after_delete($result) {
global $DB;
if (!$result) {
return;
}
// Resolving sortorder holes left after delete.
$table = '{' . self::TABLE . '}';
$sql = "UPDATE $table SET sortorder = sortorder -1 WHERE competencyframeworkid = ? AND parentid = ? AND sortorder > ?";
$DB->execute($sql, array($this->get('competencyframeworkid'), $this->get('parentid'), $this->get('sortorder')));
}
/**
* Extracts the default grade from the scale configuration.
*
* Returns an array where the first element is the grade, and the second
* is a boolean representing whether or not this grade is considered 'proficient'.
*
* @return array(int grade, bool proficient)
*/
public function get_default_grade() {
$scaleid = $this->get('scaleid');
$scaleconfig = $this->get('scaleconfiguration');
if ($scaleid === null) {
$scaleconfig = $this->get_framework()->get('scaleconfiguration');
}
return competency_framework::get_default_grade_from_scale_configuration($scaleconfig);
}
/**
* Get the competency framework.
*
* @return competency_framework
*/
public function get_framework() {
return new competency_framework($this->get('competencyframeworkid'));
}
/**
* Get the competency level.
*
* @return int
*/
public function get_level() {
$path = $this->get('path');
$path = trim($path, '/');
return substr_count($path, '/') + 1;
}
/**
* Return the parent competency.
*
* @return null|competency
*/
public function get_parent() {
$parentid = $this->get('parentid');
if (!$parentid) {
return null;
}
return new competency($parentid);
}
/**
* Extracts the proficiency of a grade from the scale configuration.
*
* @param int $grade The grade (scale item ID).
* @return array(int grade, bool proficient)
*/
public function get_proficiency_of_grade($grade) {
$scaleid = $this->get('scaleid');
$scaleconfig = $this->get('scaleconfiguration');
if ($scaleid === null) {
$scaleconfig = $this->get_framework()->get('scaleconfiguration');
}
return competency_framework::get_proficiency_of_grade_from_scale_configuration($scaleconfig, $grade);
}
/**
* Return the related competencies.
*
* @return competency[]
*/
public function get_related_competencies() {
return related_competency::get_related_competencies($this->get('id'));
}
/**
* Get the rule object.
*
* @return null|competency_rule
*/
public function get_rule_object() {
$rule = $this->get('ruletype');
if (!$rule || !is_subclass_of($rule, 'core_competency\\competency_rule')) {
// Double check that the rule is extending the right class to avoid bad surprises.
return null;
}
return new $rule($this);
}
/**
* Return the scale.
*
* @return \grade_scale
*/
public function get_scale() {
$scaleid = $this->get('scaleid');
if ($scaleid === null) {
return $this->get_framework()->get_scale();
}
$scale = \grade_scale::fetch(array('id' => $scaleid));
$scale->load_items();
return $scale;
}
/**
* Returns true when the competency has user competencies.
*
* This is useful to determine if the competency, or part of it, should be locked down.
*
* @return boolean
*/
public function has_user_competencies() {
return user_competency::has_records_for_competency($this->get('id')) ||
user_competency_plan::has_records_for_competency($this->get('id'));
}
/**
* Check if the competency is the parent of passed competencies.
*
* @param array $ids IDs of supposedly direct children.
* @return boolean
*/
public function is_parent_of(array $ids) {
global $DB;
list($insql, $params) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
$params['parentid'] = $this->get('id');
return $DB->count_records_select(self::TABLE, "id $insql AND parentid = :parentid", $params) == count($ids);
}
/**
* Reset the rule.
*
* @return void
*/
public function reset_rule() {
$this->raw_set('ruleoutcome', static::OUTCOME_NONE);
$this->raw_set('ruletype', null);
$this->raw_set('ruleconfig', null);
}
/**
* Helper method to set the path.
*
* @param competency $parent The parent competency object.
* @return void
*/
protected function set_new_path(competency $parent = null) {
$path = '/0/';
if ($this->get('parentid')) {
$parent = $parent !== null ? $parent : $this->get_parent();
$path = $parent->get('path') . $this->get('parentid') . '/';
}
$this->raw_set('path', $path);
}
/**
* Helper method to set the sortorder.
*
* @return void
*/
protected function set_new_sortorder() {
$search = array('parentid' => $this->get('parentid'), 'competencyframeworkid' => $this->get('competencyframeworkid'));
$this->raw_set('sortorder', $this->count_records($search));
}
/**
* This does a specialised search that finds all nodes in the tree with matching text on any text like field,
* and returns this node and all its parents in a displayable sort order.
*
* @param string $searchtext The text to search for.
* @param int $competencyframeworkid The competency framework to limit the search.
* @return persistent[]
*/
public static function search($searchtext, $competencyframeworkid) {
global $DB;
$like1 = $DB->sql_like('shortname', ':like1', false);
$like2 = $DB->sql_like('idnumber', ':like2', false);
$like3 = $DB->sql_like('description', ':like3', false);
$params = array(
'like1' => '%' . $DB->sql_like_escape($searchtext) . '%',
'like2' => '%' . $DB->sql_like_escape($searchtext) . '%',
'like3' => '%' . $DB->sql_like_escape($searchtext) . '%',
'frameworkid' => $competencyframeworkid
);
$sql = 'competencyframeworkid = :frameworkid AND ((' . $like1 . ') OR (' . $like2 . ') OR (' . $like3 . '))';
$records = $DB->get_records_select(self::TABLE, $sql, $params, 'path, sortorder ASC', '*');
// Now get all the parents.
$parents = array();
foreach ($records as $record) {
$split = explode('/', trim($record->path, '/'));
foreach ($split as $parent) {
$parents[intval($parent)] = true;
}
}
$parents = array_keys($parents);
// Skip ones we already fetched.
foreach ($parents as $idx => $parent) {
if ($parent == 0 || isset($records[$parent])) {
unset($parents[$idx]);
}
}
if (count($parents)) {
list($parentsql, $parentparams) = $DB->get_in_or_equal($parents, SQL_PARAMS_NAMED);
$parentrecords = $DB->get_records_select(self::TABLE, 'id ' . $parentsql,
$parentparams, 'path, sortorder ASC', '*');
foreach ($parentrecords as $id => $record) {
$records[$id] = $record;
}
}
$instances = array();
// Convert to instances of this class.
foreach ($records as $record) {
$newrecord = new static(0, $record);
$instances[$newrecord->get('id')] = $newrecord;
}
return $instances;
}
/**
* Validate the competency framework ID.
*
* @param int $value The framework ID.
* @return true|lang_string
*/
protected function validate_competencyframeworkid($value) {
// During update.
if ($this->get('id')) {
// Ensure that we are not trying to move the competency across frameworks.
if ($this->beforeupdate->get('competencyframeworkid') != $value) {
return new lang_string('invaliddata', 'error');
}
} else {
// During create.
// Check that the framework exists.
if (!competency_framework::record_exists($value)) {
return new lang_string('invaliddata', 'error');
}
}
return true;
}
/**
* Validate the ID number.
*
* @param string $value The ID number.
* @return true|lang_string
*/
protected function validate_idnumber($value) {
global $DB;
$sql = 'idnumber = :idnumber AND competencyframeworkid = :competencyframeworkid AND id <> :id';
$params = array(
'id' => $this->get('id'),
'idnumber' => $value,
'competencyframeworkid' => $this->get('competencyframeworkid')
);
if ($DB->record_exists_select(self::TABLE, $sql, $params)) {
return new lang_string('idnumbertaken', 'error');
}
return true;
}
/**
* Validate the path.
*
* @param string $value The path.
* @return true|lang_string
*/
protected function validate_path($value) {
// The last item should be the parent ID.
$id = $this->get('parentid');
if (substr($value, -(strlen($id) + 2)) != '/' . $id . '/') {
return new lang_string('invaliddata', 'error');
} else if (!preg_match('@/([0-9]+/)+@', $value)) {
// The format of the path is not correct.
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate the parent ID.
*
* @param string $value The ID.
* @return true|lang_string
*/
protected function validate_parentid($value) {
// Check that the parent exists. But only if we don't have it already, and we actually have a parent.
if (!empty($value) && !$this->newparent && !self::record_exists($value)) {
return new lang_string('invaliddata', 'error');
}
// During update.
if ($this->get('id')) {
// If there is a new parent.
if ($this->beforeupdate->get('parentid') != $value && $this->newparent) {
// Check that the new parent belongs to the same framework.
if ($this->newparent->get('competencyframeworkid') != $this->get('competencyframeworkid')) {
return new lang_string('invaliddata', 'error');
}
}
}
return true;
}
/**
* Validate the rule.
*
* @param string $value The ID.
* @return true|lang_string
*/
protected function validate_ruletype($value) {
if ($value === null) {
return true;
}
if (!class_exists($value) || !is_subclass_of($value, 'core_competency\\competency_rule')) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate the rule config.
*
* @param string $value The ID.
* @return true|lang_string
*/
protected function validate_ruleconfig($value) {
$rule = $this->get_rule_object();
// We don't have a rule.
if (empty($rule)) {
if ($value === null) {
// No config, perfect.
return true;
}
// Config but no rules, whoops!
return new lang_string('invaliddata', 'error');
}
$valid = $rule->validate_config($value);
if ($valid !== true) {
// Whoops!
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate the scale ID.
*
* Note that the value for a scale can never be 0, null has to be used when
* the framework's scale has to be used.
*
* @param int $value
* @return true|lang_string
*/
protected function validate_scaleid($value) {
global $DB;
if ($value === null) {
return true;
}
// Always validate that the scale exists.
if (!$DB->record_exists_select('scale', 'id = :id', array('id' => $value))) {
return new lang_string('invalidscaleid', 'error');
}
// During update.
if ($this->get('id')) {
// Validate that we can only change the scale when it is not used yet.
if ($this->beforeupdate->get('scaleid') != $value) {
if ($this->has_user_competencies()) {
return new lang_string('errorscalealreadyused', 'core_competency');
}
}
}
return true;
}
/**
* Validate the scale configuration.
*
* This logic is adapted from {@link \core_competency\competency_framework::validate_scaleconfiguration()}.
*
* @param string $value The scale configuration.
* @return bool|lang_string
*/
protected function validate_scaleconfiguration($value) {
$scaleid = $this->get('scaleid');
if ($scaleid === null && $value === null) {
return true;
}
$scaledefaultselected = false;
$proficientselected = false;
$scaleconfigurations = json_decode($value);
if (is_array($scaleconfigurations)) {
// The first element of the array contains the scale ID.
$scaleinfo = array_shift($scaleconfigurations);
if (empty($scaleinfo) || !isset($scaleinfo->scaleid) || $scaleinfo->scaleid != $scaleid) {
// This should never happen.
return new lang_string('errorscaleconfiguration', 'core_competency');
}
// Walk through the array to find proficient and default values.
foreach ($scaleconfigurations as $scaleconfiguration) {
if (isset($scaleconfiguration->scaledefault) && $scaleconfiguration->scaledefault) {
$scaledefaultselected = true;
}
if (isset($scaleconfiguration->proficient) && $scaleconfiguration->proficient) {
$proficientselected = true;
}
}
}
if (!$scaledefaultselected || !$proficientselected) {
return new lang_string('errorscaleconfiguration', 'core_competency');
}
return true;
}
/**
* Return whether or not the competency IDs share the same framework.
*
* @param array $ids Competency IDs
* @return bool
*/
public static function share_same_framework(array $ids) {
global $DB;
list($insql, $params) = $DB->get_in_or_equal($ids);
$sql = "SELECT COUNT('x') FROM (SELECT DISTINCT(competencyframeworkid) FROM {" . self::TABLE . "} WHERE id {$insql}) f";
return $DB->count_records_sql($sql, $params) == 1;
}
/**
* Get the available rules.
*
* @return array Keys are the class names, values are the name of the rule.
*/
public static function get_available_rules() {
// Fully qualified class names without leading slashes because get_class() does not add them either.
$rules = array(
'core_competency\\competency_rule_all' => competency_rule_all::get_name(),
'core_competency\\competency_rule_points' => competency_rule_points::get_name(),
);
return $rules;
}
/**
* Return the current depth of a competency framework.
*
* @param int $frameworkid The framework ID.
* @return int
*/
public static function get_framework_depth($frameworkid) {
global $DB;
$totallength = $DB->sql_length('path');
$trimmedlength = $DB->sql_length("REPLACE(path, '/', '')");
$sql = "SELECT ($totallength - $trimmedlength - 1) AS depth
FROM {" . self::TABLE . "}
WHERE competencyframeworkid = :id
ORDER BY depth DESC";
$record = $DB->get_record_sql($sql, array('id' => $frameworkid), IGNORE_MULTIPLE);
if (!$record) {
$depth = 0;
} else {
$depth = $record->depth;
}
return $depth;
}
/**
* Build a framework tree with competency nodes.
*
* @param int $frameworkid the framework id
* @return stdClass[] tree of framework competency nodes
*/
public static function get_framework_tree($frameworkid) {
$competencies = self::search('', $frameworkid);
return self::build_tree($competencies, 0);
}
/**
* Get the context from the framework.
*
* @return \context
*/
public function get_context() {
return $this->get_framework()->get_context();
}
/**
* Recursively build up the tree of nodes.
*
* @param array $all - List of all competency classes.
* @param int $parentid - The current parent ID. Pass 0 to build the tree from the top.
* @return stdClass[] $tree tree of nodes
*/
protected static function build_tree($all, $parentid) {
$tree = array();
foreach ($all as $one) {
if ($one->get('parentid') == $parentid) {
$node = new stdClass();
$node->competency = $one;
$node->children = self::build_tree($all, $one->get('id'));
$tree[] = $node;
}
}
return $tree;
}
/**
* Check if we can delete competencies safely.
*
* This moethod does not check any capablities.
* Check if competency is used in a plan and user competency.
* Check if competency is used in a template.
* Check if competency is linked to a course.
*
* @param array $ids Array of competencies ids.
* @return bool True if we can delete the competencies.
*/
public static function can_all_be_deleted($ids) {
global $CFG;
if (empty($ids)) {
return true;
}
// Check if competency is used in template.
if (template_competency::has_records_for_competencies($ids)) {
return false;
}
// Check if competency is used in plan.
if (plan_competency::has_records_for_competencies($ids)) {
return false;
}
// Check if competency is used in course.
if (course_competency::has_records_for_competencies($ids)) {
return false;
}
// Check if competency is used in user_competency.
if (user_competency::has_records_for_competencies($ids)) {
return false;
}
// Check if competency is used in user_competency_plan.
if (user_competency_plan::has_records_for_competencies($ids)) {
return false;
}
require_once($CFG->libdir . '/badgeslib.php');
// Check if competency is used in a badge.
if (badge_award_criteria_competency_has_records_for_competencies($ids)) {
return false;
}
return true;
}
/**
* Delete the competencies.
*
* This method is reserved to core usage.
* This method does not trigger the after_delete event.
* This method does not delete related objects such as related competencies and evidences.
*
* @param array $ids The competencies ids.
* @return bool True if the competencies were deleted successfully.
*/
public static function delete_multiple($ids) {
global $DB;
list($insql, $params) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
return $DB->delete_records_select(self::TABLE, "id $insql", $params);
}
/**
* Get descendant ids.
*
* @param competency $competency The competency.
* @return array Array of competencies ids.
*/
public static function get_descendants_ids($competency) {
global $DB;
$path = $DB->sql_like_escape($competency->get('path') . $competency->get('id') . '/') . '%';
$like = $DB->sql_like('path', ':likepath');
return $DB->get_fieldset_select(self::TABLE, 'id', $like, array('likepath' => $path));
}
/**
* Get competencyids by frameworkid.
*
* @param int $frameworkid The competency framework ID.
* @return array Array of competency ids.
*/
public static function get_ids_by_frameworkid($frameworkid) {
global $DB;
return $DB->get_fieldset_select(self::TABLE, 'id', 'competencyframeworkid = :frmid', array('frmid' => $frameworkid));
}
/**
* Delete competencies by framework ID.
*
* This method is reserved to core usage.
* This method does not trigger the after_delete event.
* This method does not delete related objects such as related competencies and evidences.
*
* @param int $id the framework ID
* @return bool Return true if delete was successful.
*/
public static function delete_by_frameworkid($id) {
global $DB;
return $DB->delete_records(self::TABLE, array('competencyframeworkid' => $id));
}
/**
* Get competency ancestors.
*
* @return competency[] Return array of ancestors.
*/
public function get_ancestors() {
global $DB;
$ancestors = array();
$ancestorsids = explode('/', trim($this->get('path'), '/'));
// Drop the root item from the array /0/.
array_shift($ancestorsids);
if (!empty($ancestorsids)) {
list($insql, $params) = $DB->get_in_or_equal($ancestorsids, SQL_PARAMS_NAMED);
$ancestors = self::get_records_select("id $insql", $params);
}
return $ancestors;
}
}
+507
View File
@@ -0,0 +1,507 @@
<?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 loading/storing competency frameworks from the DB.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use coding_exception;
use context;
use lang_string;
use stdClass;
require_once($CFG->libdir . '/grade/grade_scale.php');
/**
* Class for loading/storing competency frameworks from the DB.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class competency_framework extends persistent {
const TABLE = 'competency_framework';
/** Taxonomy constant. */
const TAXONOMY_BEHAVIOUR = 'behaviour';
/** Taxonomy constant. */
const TAXONOMY_COMPETENCY = 'competency';
/** Taxonomy constant. */
const TAXONOMY_CONCEPT = 'concept';
/** Taxonomy constant. */
const TAXONOMY_DOMAIN = 'domain';
/** Taxonomy constant. */
const TAXONOMY_INDICATOR = 'indicator';
/** Taxonomy constant. */
const TAXONOMY_LEVEL = 'level';
/** Taxonomy constant. */
const TAXONOMY_OUTCOME = 'outcome';
/** Taxonomy constant. */
const TAXONOMY_PRACTICE = 'practice';
/** Taxonomy constant. */
const TAXONOMY_PROFICIENCY = 'proficiency';
/** Taxonomy constant. */
const TAXONOMY_SKILL = 'skill';
/** Taxonomy constant. */
const TAXONOMY_VALUE = 'value';
/** @var static The object before it was updated. */
protected $beforeupdate;
/**
* Get the context.
*
* @return \context The context
*/
public function get_context() {
return context::instance_by_id($this->get('contextid'));
}
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'shortname' => array(
'type' => PARAM_TEXT
),
'idnumber' => array(
'type' => PARAM_RAW
),
'description' => array(
'type' => PARAM_CLEANHTML,
'default' => ''
),
'descriptionformat' => array(
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
'type' => PARAM_INT,
'default' => FORMAT_HTML
),
'visible' => array(
'type' => PARAM_BOOL,
'default' => 1
),
'scaleid' => array(
'type' => PARAM_INT
),
'scaleconfiguration' => array(
'type' => PARAM_RAW
),
'contextid' => array(
'type' => PARAM_INT
),
'taxonomies' => array(
'type' => PARAM_RAW,
'default' => ''
)
);
}
/**
* Hook to execute before validate.
*
* @return void
*/
protected function before_validate() {
$this->beforeupdate = null;
// During update.
if ($this->get('id')) {
$this->beforeupdate = new competency_framework($this->get('id'));
}
}
/**
* Return the current depth of a competency framework.
*
* @see competency::get_framework_depth()
* @return int
*/
public function get_depth() {
return competency::get_framework_depth($this->get('id'));
}
/**
* Return the scale.
*
* @return \grade_scale
*/
public function get_scale() {
$scale = \grade_scale::fetch(array('id' => $this->get('scaleid')));
$scale->load_items();
return $scale;
}
/**
* Get the constant name for a level.
*
* @param int $level The level of the term.
* @return string
*/
public function get_taxonomy($level) {
$taxonomies = $this->get_taxonomies();
if (empty($taxonomies[$level])) {
// If for some reason we cannot find the level, we fallback onto competency.
$constant = self::TAXONOMY_COMPETENCY;
} else {
$constant = $taxonomies[$level];
}
return $constant;
}
/**
* Return the taxonomy constants indexed by level.
*
* @return array Contains the list of taxonomy constants indexed by level.
*/
protected function get_taxonomies() {
$taxonomies = explode(',', $this->raw_get('taxonomies'));
// Indexing first level at 1.
array_unshift($taxonomies, null);
unset($taxonomies[0]);
// Ensure that we do not return empty levels.
foreach ($taxonomies as $i => $taxonomy) {
if (empty($taxonomy)) {
$taxonomies[$i] = self::TAXONOMY_COMPETENCY;
}
}
return $taxonomies;
}
/**
* Returns true when some competencies of the framework have user competencies.
*
* This is useful to determine if the framework, or part of it, should be locked down.
*
* @return boolean
*/
public function has_user_competencies() {
return user_competency::has_records_for_framework($this->get('id')) ||
user_competency_plan::has_records_for_framework($this->get('id'));
}
/**
* Convenience method to set taxonomies from an array or string.
*
* @param string|array $taxonomies A string, or an array where the values are the term constants.
*/
protected function set_taxonomies($taxonomies) {
if (is_array($taxonomies)) {
$taxonomies = implode(',', $taxonomies);
}
$this->raw_set('taxonomies', $taxonomies);
}
/**
* Validate the context ID.
*
* @param int $value The context ID.
* @return bool|lang_string
*/
protected function validate_contextid($value) {
global $DB;
$context = context::instance_by_id($value, IGNORE_MISSING);
if (!$context) {
return new lang_string('invalidcontext', 'error');
} else if ($context->contextlevel != CONTEXT_SYSTEM && $context->contextlevel != CONTEXT_COURSECAT) {
return new lang_string('invalidcontext', 'error');
}
// During update.
if ($this->get('id')) {
// The context must never change.
$oldcontextid = $DB->get_field(self::TABLE, 'contextid', array('id' => $this->get('id')), MUST_EXIST);
if ($this->get('contextid') != $oldcontextid) {
return new lang_string('invalidcontext', 'error');
}
}
return true;
}
/**
* Validate the id number.
*
* @param string $value The id number.
* @return bool|lang_string
*/
protected function validate_idnumber($value) {
global $DB;
$params = array(
'id' => $this->get('id'),
'idnumber' => $value,
);
if ($DB->record_exists_select(self::TABLE, 'idnumber = :idnumber AND id <> :id', $params)) {
return new lang_string('idnumbertaken', 'error');
}
return true;
}
/**
* Validate the scale ID.
*
* @param string $value The scale ID.
* @return bool|lang_string
*/
protected function validate_scaleid($value) {
global $DB;
// Always validate that the scale exists.
if (!$DB->record_exists_select('scale', 'id = :id', array('id' => $value))) {
return new lang_string('invalidscaleid', 'error');
}
// During update.
if ($this->get('id')) {
// Validate that we can only change the scale when it is not used yet.
if ($this->beforeupdate->get('scaleid') != $value) {
if ($this->beforeupdate->has_user_competencies()) {
return new lang_string('errorscalealreadyused', 'core_competency');
}
}
}
return true;
}
/**
* Validate the scale configuration.
*
* @param string $value The scale configuration.
* @return bool|lang_string
*/
protected function validate_scaleconfiguration($value) {
$scaledefaultselected = false;
$proficientselected = false;
$scaleconfigurations = json_decode($value);
if (is_array($scaleconfigurations)) {
// The first element of the array contains the scale ID.
$scaleinfo = array_shift($scaleconfigurations);
if (empty($scaleinfo) || !isset($scaleinfo->scaleid) || $scaleinfo->scaleid != $this->get('scaleid')) {
// This should never happen.
return new lang_string('errorscaleconfiguration', 'core_competency');
}
// Walk through the array to find proficient and default values.
foreach ($scaleconfigurations as $scaleconfiguration) {
if (isset($scaleconfiguration->scaledefault) && $scaleconfiguration->scaledefault) {
$scaledefaultselected = true;
}
if (isset($scaleconfiguration->proficient) && $scaleconfiguration->proficient) {
$proficientselected = true;
}
}
}
if (!$scaledefaultselected || !$proficientselected) {
return new lang_string('errorscaleconfiguration', 'core_competency');
}
return true;
}
/**
* Validate taxonomies.
*
* @param mixed $value The taxonomies.
* @return true|lang_string
*/
protected function validate_taxonomies($value) {
$terms = explode(',', $value);
foreach ($terms as $term) {
if (!empty($term) && !array_key_exists($term, self::get_taxonomies_list())) {
return new lang_string('invalidtaxonomy', 'core_competency', $term);
}
}
return true;
}
/**
* Extract the default grade from a scale configuration.
*
* Returns an array where the first element is the grade, and the second
* is a boolean representing whether or not this grade is considered 'proficient'.
*
* @param string $config JSON encoded config.
* @return array(int grade, int proficient)
*/
public static function get_default_grade_from_scale_configuration($config) {
$config = json_decode($config);
if (!is_array($config)) {
throw new coding_exception('Unexpected scale configuration.');
}
// Remove the scale ID from the config.
array_shift($config);
foreach ($config as $part) {
if ($part->scaledefault) {
return array((int) $part->id, (int) $part->proficient);
}
}
throw new coding_exception('Invalid scale configuration, default not found.');
}
/**
* Extract the proficiency of a grade from a scale configuration.
*
* @param string $config JSON encoded config.
* @param int $grade The grade.
* @return int Representing a boolean
*/
public static function get_proficiency_of_grade_from_scale_configuration($config, $grade) {
$config = json_decode($config);
if (!is_array($config)) {
throw new coding_exception('Unexpected scale configuration.');
}
// Remove the scale ID from the config.
array_shift($config);
foreach ($config as $part) {
if ($part->id == $grade) {
return (int) $part->proficient;
}
}
return 0;
}
/**
* Get the string of a taxonomy from a constant
*
* @param string $constant The taxonomy constant.
* @return lang_string
*/
public static function get_taxonomy_from_constant($constant) {
return self::get_taxonomies_list()[$constant];
}
/**
* Get the list of all taxonomies.
*
* @return array Where the key is the taxonomy constant, and the value its translation.
*/
public static function get_taxonomies_list() {
static $list = null;
// At some point we'll have to switch to not using static cache, mainly for Unit Tests in case we
// decide to allow more taxonomies to be added dynamically from a CFG variable for instance.
if ($list === null) {
$list = array(
self::TAXONOMY_BEHAVIOUR => new lang_string('taxonomy_' . self::TAXONOMY_BEHAVIOUR, 'core_competency'),
self::TAXONOMY_COMPETENCY => new lang_string('taxonomy_' . self::TAXONOMY_COMPETENCY, 'core_competency'),
self::TAXONOMY_CONCEPT => new lang_string('taxonomy_' . self::TAXONOMY_CONCEPT, 'core_competency'),
self::TAXONOMY_DOMAIN => new lang_string('taxonomy_' . self::TAXONOMY_DOMAIN, 'core_competency'),
self::TAXONOMY_INDICATOR => new lang_string('taxonomy_' . self::TAXONOMY_INDICATOR, 'core_competency'),
self::TAXONOMY_LEVEL => new lang_string('taxonomy_' . self::TAXONOMY_LEVEL, 'core_competency'),
self::TAXONOMY_OUTCOME => new lang_string('taxonomy_' . self::TAXONOMY_OUTCOME, 'core_competency'),
self::TAXONOMY_PRACTICE => new lang_string('taxonomy_' . self::TAXONOMY_PRACTICE, 'core_competency'),
self::TAXONOMY_PROFICIENCY => new lang_string('taxonomy_' . self::TAXONOMY_PROFICIENCY, 'core_competency'),
self::TAXONOMY_SKILL => new lang_string('taxonomy_' . self::TAXONOMY_SKILL, 'core_competency'),
self::TAXONOMY_VALUE => new lang_string('taxonomy_' . self::TAXONOMY_VALUE, 'core_competency'),
);
}
return $list;
}
/**
* Get a uniq idnumber.
*
* @param string $idnumber the framework idnumber
* @return string
*/
public static function get_unused_idnumber($idnumber) {
global $DB;
$currentidnumber = $idnumber;
$counter = 0;
// Iteratere while the idnumber exists.
while ($DB->record_exists_select(static::TABLE, 'idnumber = ?', array($currentidnumber))) {
$suffixidnumber = '_' . ++$counter;
$currentidnumber = substr($idnumber, 0, 100 - strlen($suffixidnumber)).$suffixidnumber;
}
// Return the uniq idnumber.
return $currentidnumber;
}
/**
* Whether or not the current user can manage the framework.
*
* @return bool
*/
public function can_manage() {
return self::can_manage_context($this->get_context());
}
/**
* Whether or not the current user can manage the framework.
*
* @param context $context
* @return bool
*/
public static function can_manage_context($context) {
return has_capability('moodle/competency:competencymanage', $context);
}
/**
* Whether or not the current user can read the framework.
*
* @return bool
*/
public function can_read() {
return self::can_read_context($this->get_context());
}
/**
* Whether or not the current user can read the framework.
*
* @param context $context
* @return bool
*/
public static function can_read_context($context) {
return has_capability('moodle/competency:competencyview', $context) || self::can_manage_context($context);
}
}
+107
View File
@@ -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/>.
/**
* Competency rule base.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use coding_exception;
/**
* Competency rule base abstract class.
*
* Rules are attached to a competency and then tested against a user competency
* to determine whether or not it matches.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class competency_rule {
/** @var competency The competency. */
protected $competency;
/**
* Constructor.
*
* @param competency $competency The competency.
*/
public function __construct(competency $competency) {
$class = $competency->get('ruletype');
if (!$class || !($this instanceof $class)) {
throw new coding_exception('This competency does not use this rule.');
}
$this->competency = $competency;
}
/**
* Get the rule config.
*
* @return mixed
*/
protected function get_config() {
return $this->competency->get('ruleconfig');
}
/**
* Whether or not the rule is matched.
*
* @param user_competency $usercompetency The user competency to test against.
* @return bool
*/
abstract public function matches(user_competency $usercompetency);
/**
* Validate the rule config.
*
* @param string $value The value to validate.
* @return bool
*/
abstract public function validate_config($value);
/**
* The name of the rule.
*
* @return lang_string
*/
public static function get_name() {
throw new coding_exception('Method not implemented.');
}
/**
* Migrate rule config from one set of competencies to another.
*
* Exceptions should be thrown when the migration can not be performed.
*
* @param string $config Original config rule of a competency.
* @param array $mappings Array that matches the original competency IDs with the new competencies objects.
* @return string New configuration.
* @throws Exception
*/
public static function migrate_config($config, $mappings) {
return $config;
}
}
@@ -0,0 +1,92 @@
<?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/>.
/**
* Competency rule all.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use lang_string;
/**
* Competency rule all class.
*
* This rule is considered matched when all the children of a competency are completed.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class competency_rule_all extends competency_rule {
/**
* Whether or not the rule is matched.
*
* @param user_competency $usercompetency The user competency.
* @return bool
*/
public function matches(user_competency $usercompetency) {
global $DB;
// TODO Improve performance here, perhaps the caller could already provide records.
$children = competency::get_records(array('parentid' => $this->competency->get('id')));
if (empty($children)) {
// Leaves are not compatible with this rule.
return false;
}
$ids = array();
foreach ($children as $child) {
$ids[] = $child->get('id');
}
list($insql, $params) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
$sql = "userid = :userid
AND proficiency = :proficiency
AND competencyid $insql";
$params['userid'] = $usercompetency->get('userid');
$params['proficiency'] = 1;
// Is the user is marked as proficient in all children?
return user_competency::count_records_select($sql, $params) === count($ids);
}
/**
* Validate the rule config.
*
* @param string $value The value to validate.
* @return bool
*/
public function validate_config($value) {
return $value === null;
}
/**
* The name of the rule.
*
* @return lang_string
*/
public static function get_name() {
return new lang_string('allchildrenarecomplete', 'core_competency');
}
}
@@ -0,0 +1,218 @@
<?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/>.
/**
* Competency rule points based.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use coding_exception;
use lang_string;
/**
* Competency rule points based class.
*
* This rule matches when related competencies contribute for a required number of points.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class competency_rule_points extends competency_rule {
/**
* Get the rule config.
*
* @return mixed
*/
protected function get_config() {
$config = parent::get_config();
return json_decode($config);
}
/**
* Whether or not the rule is matched.
*
* @param user_competency $usercompetency The user competency.
* @return bool
*/
public function matches(user_competency $usercompetency) {
global $DB;
$config = $this->get_config();
$pointsrequired = $config->base->points;
// Index by competency ID and extract required.
$compsrules = array();
$requiredids = array();
foreach ($config->competencies as $comp) {
$compsrules[$comp->id] = $comp;
if ($comp->required) {
$requiredids[$comp->id] = $comp->id;
}
}
// Find all the user competency records.
list($insql, $params) = $DB->get_in_or_equal(array_keys($compsrules), SQL_PARAMS_NAMED);
$sql = "userid = :userid
AND proficiency = :proficiency
AND competencyid $insql";
$params['userid'] = $usercompetency->get('userid');
$params['proficiency'] = 1;
$ucs = user_competency::get_records_select($sql, $params, '', 'competencyid');
// Check that all the required are found.
if (!empty($requiredids)) {
$unmetrequired = array_diff_key($requiredids, $ucs);
if (!empty($unmetrequired)) {
return false;
}
}
// Check that we have enough points.
$points = 0;
foreach ($compsrules as $compid => $comp) {
if (array_key_exists($compid, $ucs)) {
$points += $comp->points;
}
}
return $points >= $pointsrequired;
}
/**
* Validate the rule config.
*
* @param string $value The value to validate.
* @return bool
*/
public function validate_config($value) {
$compids = array();
$config = json_decode($value);
if ($config === null || !isset($config->base) || !isset($config->competencies)) {
return false;
}
if (!isset($config->base->points)) {
return false;
}
try {
$requiredpoints = validate_param($config->base->points, PARAM_INT);
} catch (\invalid_parameter_exception $e) {
return false;
}
if ($requiredpoints < 1) {
return false;
}
$totalpoints = 0;
// Validate the competency info.
foreach ($config->competencies as $competency) {
// Cannot include self.
if ($competency->id == $this->competency->get('id')) {
return false;
}
// Check for duplicates.
if (in_array($competency->id, $compids)) {
return false;
}
// Check for required fields.
if (!isset($competency->id)
|| !isset($competency->points)
|| !isset($competency->required)) {
return false;
}
// Validate the parameters.
try {
validate_param($competency->id, PARAM_INT);
$points = validate_param($competency->points, PARAM_INT);
validate_param($competency->required, PARAM_BOOL);
} catch (\invalid_parameter_exception $e) {
return false;
}
$totalpoints += $points;
if ($points < 0) {
return false;
}
$compids[] = $competency->id;
}
// No competencies, that's strange.
if (empty($compids)) {
return false;
}
// Impossible to reach the points required.
if ($requiredpoints > $totalpoints) {
return false;
}
// Check that all the competencies are children of the competency.
// We may want to relax this check at a later stage if we want to allow competencies
// to be linked throughout the whole framework.
return $this->competency->is_parent_of($compids);
}
/**
* The name of the rule.
*
* @return lang_string
*/
public static function get_name() {
return new lang_string('pointsrequiredaremet', 'core_competency');
}
/**
* Migrate rule config when duplicate competency based on mapping competencies ids.
*
* @param string $config the config rule of a competency
* @param array $mappings array that match the old competency ids with the new competencies
* @return string
*/
public static function migrate_config($config, $mappings) {
$ruleconfig = json_decode($config, true);
if (is_array($ruleconfig)) {
foreach ($ruleconfig['competencies'] as $key => $rulecomp) {
$rulecmpid = $rulecomp['id'];
if (array_key_exists($rulecmpid, $mappings)) {
$ruleconfig['competencies'][$key]['id'] = $mappings[$rulecmpid]->get('id');
} else {
throw new coding_exception("The competency id is not found in the matchids.");
}
}
} else {
throw new coding_exception("Invalid JSON config rule.");
}
return json_encode($ruleconfig);
}
}
+413
View File
@@ -0,0 +1,413 @@
<?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 loading/storing competencies from the DB.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
use coding_exception;
use lang_string;
use core_course\external\course_summary_exporter;
/**
* Class for loading/storing course_competencies from the DB.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_competency extends persistent {
const TABLE = 'competency_coursecomp';
/** Course competency ruleoutcome constant. */
const OUTCOME_NONE = 0;
/** Course competency ruleoutcome constant. */
const OUTCOME_EVIDENCE = 1;
/** Course competency ruleoutcome constant. */
const OUTCOME_RECOMMEND = 2;
/** Course competency ruleoutcome constant. */
const OUTCOME_COMPLETE = 3;
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'courseid' => array(
'type' => PARAM_INT
),
'competencyid' => array(
'type' => PARAM_INT
),
'sortorder' => array(
'type' => PARAM_INT
),
'ruleoutcome' => array(
'choices' => array(self::OUTCOME_NONE,
self::OUTCOME_EVIDENCE,
self::OUTCOME_RECOMMEND,
self::OUTCOME_COMPLETE
),
'default' => self::OUTCOME_EVIDENCE,
'type' => PARAM_INT,
),
);
}
/**
* Hook to execute before validate.
*
* @return void
*/
protected function before_validate() {
if (($this->get('id') && $this->get('sortorder') === null) || !$this->get('id')) {
$this->set('sortorder', $this->count_records(array('courseid' => $this->get('courseid'))));
}
}
/**
* Return the courses where both competency and user are.
*
* A user is considered being in a course when they are enrolled, the enrolment is valid,
* the enrolment instance is enabled, and the enrolment plugin is enabled..
*
* @param int $competencyid The competency ID.
* @param int $userid The user ID.
* @return array Indexed by course ID.
*/
public static function get_courses_with_competency_and_user($competencyid, $userid) {
global $CFG, $DB;
if (!$plugins = explode(',', $CFG->enrol_plugins_enabled)) {
return array();
}
$ctxfields = \context_helper::get_preload_record_columns_sql('ctx');
list($plugins, $params) = $DB->get_in_or_equal($plugins, SQL_PARAMS_NAMED, 'ee');
$params['competencyid'] = $competencyid;
$params['userid'] = $userid;
$params['enabled'] = ENROL_INSTANCE_ENABLED;
$params['active'] = ENROL_USER_ACTIVE;
$params['contextlevel'] = CONTEXT_COURSE;
// Heavily based on enrol_get_shared_courses().
$sql = "SELECT c.*, $ctxfields
FROM {course} c
JOIN {" . static::TABLE . "} cc
ON cc.courseid = c.id
AND cc.competencyid = :competencyid
JOIN (
SELECT DISTINCT c.id
FROM {enrol} e
JOIN {user_enrolments} ue
ON ue.enrolid = e.id
AND ue.status = :active
AND ue.userid = :userid
JOIN {course} c
ON c.id = e.courseid
WHERE e.status = :enabled
AND e.enrol $plugins
) ec ON ec.id = c.id
LEFT JOIN {context} ctx
ON ctx.instanceid = c.id
AND ctx.contextlevel = :contextlevel
ORDER BY c.id";
$courses = $DB->get_records_sql($sql, $params);
array_map('context_helper::preload_from_record', $courses);
return $courses;
}
/**
* Return a list of rules.
*
* @return array Indexed by outcome value.
*/
public static function get_ruleoutcome_list() {
static $list = null;
if ($list === null) {
$list = array(
self::OUTCOME_NONE => self::get_ruleoutcome_name(self::OUTCOME_NONE),
self::OUTCOME_EVIDENCE => self::get_ruleoutcome_name(self::OUTCOME_EVIDENCE),
self::OUTCOME_RECOMMEND => self::get_ruleoutcome_name(self::OUTCOME_RECOMMEND),
self::OUTCOME_COMPLETE => self::get_ruleoutcome_name(self::OUTCOME_COMPLETE));
}
return $list;
}
/**
* Human readable rule name.
*
* @param int $ruleoutcome The value of ruleoutcome.
* @return lang_string
*/
public static function get_ruleoutcome_name($ruleoutcome) {
switch ($ruleoutcome) {
case self::OUTCOME_NONE:
$strname = 'none';
break;
case self::OUTCOME_EVIDENCE:
$strname = 'evidence';
break;
case self::OUTCOME_RECOMMEND:
$strname = 'recommend';
break;
case self::OUTCOME_COMPLETE:
$strname = 'complete';
break;
default:
throw new \moodle_exception('errorcoursecompetencyrule', 'core_competency', '', $ruleoutcome);
break;
}
return new lang_string('coursecompetencyoutcome_' . $strname, 'core_competency');
}
/**
* Validate course ID.
*
* @param int $data The course ID.
* @return true|lang_string
*/
protected function validate_courseid($data) {
global $DB;
if (!$DB->record_exists('course', array('id' => $data))) {
return new lang_string('invalidcourseid', 'error');
}
return true;
}
/**
* Validate competency ID.
*
* @param int $data The competency ID.
* @return true|lang_string
*/
protected function validate_competencyid($data) {
if (!competency::record_exists($data)) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Return the course IDs and visible flags that include this competency.
*
* Only the ids and visible flag are returned, for the full records use list_courses.
*
* @param int $competencyid The competency id
* @return array containing courseid and visible.
*/
public static function list_courses_min($competencyid) {
global $DB;
$results = $DB->get_records_sql('SELECT course.id as id, course.visible as visible
FROM {' . self::TABLE . '} coursecomp
JOIN {course} course
ON coursecomp.courseid = course.id
WHERE coursecomp.competencyid = ? ', array($competencyid));
return $results;
}
/**
* Return partial course records foreach course that contains this competency.
*
* @param int $competencyid The competency id
* @return array[stdClass] Array of course records containg id, visible, shortname, idnumber, fullname
*/
public static function list_courses($competencyid) {
global $DB;
// We need all the course summary exporter properties, plus category.
$coursefields = course_summary_exporter::properties_definition();
$coursefields = array_map(function(string $field): string {
return "course.{$field}";
}, array_keys($coursefields));
$results = $DB->get_records_sql('SELECT ' . implode(',', $coursefields) . ', course.category
FROM {course} course
JOIN {' . self::TABLE . '} coursecomp
ON coursecomp.courseid = course.id
WHERE coursecomp.competencyid = ? ', array($competencyid));
return $results;
}
/**
* Count the competencies in this course.
*
* @param int $courseid The course id
* @return int
*/
public static function count_competencies($courseid) {
global $DB;
$sql = 'SELECT COUNT(comp.id)
FROM {' . self::TABLE . '} coursecomp
JOIN {' . competency::TABLE . '} comp
ON coursecomp.competencyid = comp.id
WHERE coursecomp.courseid = ? ';
$params = array($courseid);
$results = $DB->count_records_sql($sql, $params);
return $results;
}
/**
* List the competencies in this course.
*
* @param int $courseid The course id
* @return competency[] Indexed by competency ID.
*/
public static function list_competencies($courseid) {
global $DB;
$sql = 'SELECT comp.*
FROM {' . competency::TABLE . '} comp
JOIN {' . self::TABLE . '} coursecomp
ON coursecomp.competencyid = comp.id
WHERE coursecomp.courseid = ?';
$params = array($courseid);
$sql .= ' ORDER BY coursecomp.sortorder ASC';
$results = $DB->get_recordset_sql($sql, $params);
$instances = array();
foreach ($results as $result) {
$comp = new competency(0, $result);
$instances[$comp->get('id')] = $comp;
}
$results->close();
return $instances;
}
/**
* Get a single competency from the course (only if it is really in the course).
*
* @param int $courseid The course id
* @param int $competencyid The competency id
* @return competency
*/
public static function get_competency($courseid, $competencyid) {
global $DB;
$sql = 'SELECT comp.*
FROM {' . competency::TABLE . '} comp
JOIN {' . self::TABLE . '} crscomp
ON crscomp.competencyid = comp.id
WHERE crscomp.courseid = ? AND crscomp.competencyid = ?';
$params = array($courseid, $competencyid);
$result = $DB->get_record_sql($sql, $params);
if (!$result) {
throw new coding_exception('The competency does not belong to this course: ' . $competencyid . ', ' . $courseid);
}
return new competency(0, $result);
}
/**
* Hook to execute after delete.
*
* @param bool $result Whether or not the delete was successful.
* @return void
*/
protected function after_delete($result) {
global $DB;
if (!$result) {
return;
}
$table = '{' . self::TABLE . '}';
$sql = "UPDATE $table SET sortorder = sortorder -1 WHERE courseid = ? AND sortorder > ?";
$DB->execute($sql, array($this->get('courseid'), $this->get('sortorder')));
}
/**
* Get the specified course_competency in this course.
*
* @param int $courseid The course id
* @param int $competencyid The competency id
* @return course_competency
*/
public static function get_course_competency($courseid, $competencyid) {
global $DB;
$sql = 'SELECT crscomp.*
FROM {' . self::TABLE . '} crscomp
WHERE crscomp.courseid = ? AND crscomp.competencyid = ?';
$params = array($courseid, $competencyid);
$result = $DB->get_record_sql($sql, $params);
if (!$result) {
throw new coding_exception('The competency does not belong to this course: ' . $competencyid . ', ' . $courseid);
}
return new course_competency(0, $result);
}
/**
* List the course_competencies in this course.
*
* @param int $courseid The course id
* @return course_competency[]
*/
public static function list_course_competencies($courseid) {
global $DB;
$sql = 'SELECT coursecomp.*
FROM {' . self::TABLE . '} coursecomp
JOIN {' . competency::TABLE . '} comp
ON coursecomp.competencyid = comp.id
WHERE coursecomp.courseid = ?';
$params = array($courseid);
$sql .= ' ORDER BY coursecomp.sortorder ASC';
$results = $DB->get_recordset_sql($sql, $params);
$instances = array();
foreach ($results as $result) {
array_push($instances, new course_competency(0, $result));
}
$results->close();
return $instances;
}
/**
* Check if course competency has records for competencies.
*
* @param array $competencyids Array of competencies ids.
* @return boolean Return true if one or more than a competency was found in a course.
*/
public static function has_records_for_competencies($competencyids) {
global $DB;
list($insql, $params) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED);
return self::record_exists_select("competencyid $insql", $params);
}
}
@@ -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 course_competency_settings persistence.
*
* @package core_competency
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
use lang_string;
use context_course;
defined('MOODLE_INTERNAL') || die();
/**
* Class for course_competency_settings persistence.
*
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_competency_settings extends persistent {
/** Table name for plan_competency persistency */
const TABLE = 'competency_coursecompsetting';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'courseid' => array(
'type' => PARAM_INT,
),
'pushratingstouserplans' => array(
'type' => PARAM_BOOL,
'default' => function() {
return get_config('core_competency', 'pushcourseratingstouserplans');
}
),
);
}
/**
* Get a the course settings for a single course.
*
* @param int $courseid The course id
* @return course_competency_settings
*/
public static function get_by_courseid($courseid) {
global $DB;
$params = array(
'courseid' => $courseid
);
$settings = new static(null, (object) $params);
if ($record = $DB->get_record(self::TABLE, $params)) {
$settings->from_record($record);
}
return $settings;
}
/**
* Can the current user view competency settings for this course.
*
* @param int $courseid The course ID.
* @return bool
*/
public static function can_read($courseid) {
$context = context_course::instance($courseid);
$capabilities = array('moodle/competency:coursecompetencyview', 'moodle/competency:coursecompetencymanage');
return has_any_capability($capabilities, $context);
}
/**
* Can the current user change competency settings for this course.
*
* @param int $courseid The course ID.
* @return bool
*/
public static function can_manage_course($courseid) {
$context = context_course::instance($courseid);
$capabilities = array('moodle/competency:coursecompetencyconfigure');
return has_any_capability($capabilities, $context);
}
/**
* Can the current user change competency settings for this course.
*
* @return bool
*/
public function can_manage() {
return static::can_manage_course($this->get('courseid'));
}
/**
* Validate course ID.
*
* @param int $data The course ID.
* @return true|lang_string
*/
protected function validate_courseid($data) {
global $DB;
if (!$DB->record_exists('course', array('id' => $data))) {
return new lang_string('invalidcourseid', 'error');
}
return true;
}
/**
* Get the context.
*
* @return context The context
*/
public function get_context() {
return context_course::instance($this->get('courseid'));
}
}
@@ -0,0 +1,334 @@
<?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 loading/storing competencies from the DB.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use stdClass;
use lang_string;
/**
* Class for loading/storing course_module_competencies from the DB.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_competency extends persistent {
const TABLE = 'competency_modulecomp';
/** Course competency ruleoutcome constant. */
const OUTCOME_NONE = 0;
/** Course competency ruleoutcome constant. */
const OUTCOME_EVIDENCE = 1;
/** Course competency ruleoutcome constant. */
const OUTCOME_RECOMMEND = 2;
/** Course competency ruleoutcome constant. */
const OUTCOME_COMPLETE = 3;
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'cmid' => array(
'type' => PARAM_INT
),
'competencyid' => array(
'type' => PARAM_INT
),
'sortorder' => array(
'type' => PARAM_INT
),
'ruleoutcome' => array(
'choices' => array(self::OUTCOME_NONE,
self::OUTCOME_EVIDENCE,
self::OUTCOME_RECOMMEND,
self::OUTCOME_COMPLETE
),
'default' => self::OUTCOME_EVIDENCE,
'type' => PARAM_INT,
),
'overridegrade' => array(
'default' => false,
'type' => PARAM_BOOL
),
);
}
/**
* Hook to execute before validate.
*
* @return void
*/
protected function before_validate() {
if (($this->get('id') && $this->get('sortorder') === null) || !$this->get('id')) {
$this->set('sortorder', $this->count_records(array('cmid' => $this->get('cmid'))));
}
}
/**
* Return a list of rules.
*
* @return array Indexed by outcome value.
*/
public static function get_ruleoutcome_list() {
static $list = null;
if ($list === null) {
$list = array(
self::OUTCOME_NONE => self::get_ruleoutcome_name(self::OUTCOME_NONE),
self::OUTCOME_EVIDENCE => self::get_ruleoutcome_name(self::OUTCOME_EVIDENCE),
self::OUTCOME_RECOMMEND => self::get_ruleoutcome_name(self::OUTCOME_RECOMMEND),
self::OUTCOME_COMPLETE => self::get_ruleoutcome_name(self::OUTCOME_COMPLETE));
}
return $list;
}
/**
* Human readable rule name.
*
* @param int $ruleoutcome The value of ruleoutcome.
* @return lang_string
*/
public static function get_ruleoutcome_name($ruleoutcome) {
switch ($ruleoutcome) {
case self::OUTCOME_NONE:
$strname = 'none';
break;
case self::OUTCOME_EVIDENCE:
$strname = 'evidence';
break;
case self::OUTCOME_RECOMMEND:
$strname = 'recommend';
break;
case self::OUTCOME_COMPLETE:
$strname = 'complete';
break;
default:
throw new \moodle_exception('errorcompetencyrule', 'core_competency', '', $ruleoutcome);
break;
}
return new lang_string('coursemodulecompetencyoutcome_' . $strname, 'core_competency');
}
/**
* Validate cmid ID.
*
* @param int $data The CM ID.
* @return true|lang_string
*/
protected function validate_cmid($data) {
global $DB;
if (!$DB->record_exists('course_modules', array('id' => $data))) {
return new lang_string('invalidmodule', 'error');
}
return true;
}
/**
* Validate competency ID.
*
* @param int $data The competency ID.
* @return true|lang_string
*/
protected function validate_competencyid($data) {
if (!competency::record_exists($data)) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Return the module IDs and visible flags that include this competency in a single course.
*
* @param int $competencyid The competency id
* @param int $courseid The course ID.
* @return array of ints (cmids)
*/
public static function list_course_modules($competencyid, $courseid) {
global $DB;
$results = $DB->get_records_sql('SELECT coursemodules.id as id
FROM {' . self::TABLE . '} modcomp
JOIN {course_modules} coursemodules
ON modcomp.cmid = coursemodules.id
WHERE modcomp.competencyid = ? AND coursemodules.course = ?',
array($competencyid, $courseid));
return array_keys($results);
}
/**
* Count the competencies in this course module.
*
* @param int $cmid The course module id.
* @return int
*/
public static function count_competencies($cmid) {
global $DB;
$sql = 'SELECT COUNT(comp.id)
FROM {' . self::TABLE . '} coursemodulecomp
JOIN {' . competency::TABLE . '} comp
ON coursemodulecomp.competencyid = comp.id
WHERE coursemodulecomp.cmid = ? ';
$params = array($cmid);
$results = $DB->count_records_sql($sql, $params);
return $results;
}
/**
* List the competencies in this course module.
*
* @param int $cmid The course module id
* @return competency[] Indexed by competency ID.
*/
public static function list_competencies($cmid) {
global $DB;
$sql = 'SELECT comp.*
FROM {' . competency::TABLE . '} comp
JOIN {' . self::TABLE . '} coursemodulecomp
ON coursemodulecomp.competencyid = comp.id
WHERE coursemodulecomp.cmid = ?
ORDER BY coursemodulecomp.sortorder ASC';
$params = array($cmid);
$results = $DB->get_recordset_sql($sql, $params);
$instances = array();
foreach ($results as $result) {
$comp = new competency(0, $result);
$instances[$comp->get('id')] = $comp;
}
$results->close();
return $instances;
}
/**
* Get a single competency from the course module (only if it is really in the course module).
*
* @param int $cmid The course module id
* @param int $competencyid The competency id
* @return competency
*/
public static function get_competency($cmid, $competencyid) {
global $DB;
$sql = 'SELECT comp.*
FROM {' . competency::TABLE . '} comp
JOIN {' . self::TABLE . '} crsmodcomp
ON crsmodcomp.competencyid = comp.id
WHERE crsmodcomp.cmid = ? AND crsmodcomp.competencyid = ?';
$params = array($cmid, $competencyid);
$result = $DB->get_record_sql($sql, $params);
if (!$result) {
throw new \coding_exception('The competency does not belong to this course module: ' . $competencyid . ', ' . $cmid);
}
return new competency(0, $result);
}
/**
* Hook to execute after delete.
*
* @param bool $result Whether or not the delete was successful.
* @return void
*/
protected function after_delete($result) {
global $DB;
if (!$result) {
return;
}
$table = '{' . self::TABLE . '}';
$sql = "UPDATE $table SET sortorder = sortorder -1 WHERE cmid = ? AND sortorder > ?";
$DB->execute($sql, array($this->get('cmid'), $this->get('sortorder')));
}
/**
* List the course_module_competencies in this course module.
*
* @param int $cmid The course module id
* @return course_module_competency[]
*/
public static function list_course_module_competencies($cmid) {
global $DB;
$sql = 'SELECT coursemodcomp.*
FROM {' . self::TABLE . '} coursemodcomp
JOIN {' . competency::TABLE . '} comp
ON coursemodcomp.competencyid = comp.id
WHERE coursemodcomp.cmid = ?
ORDER BY coursemodcomp.sortorder ASC';
$params = array($cmid);
$results = $DB->get_recordset_sql($sql, $params);
$instances = array();
foreach ($results as $result) {
array_push($instances, new course_module_competency(0, $result));
}
$results->close();
return $instances;
}
/**
* List the relationship objects for a competency in a course.
*
* @param int $competencyid The competency ID.
* @param int $courseid The course ID.
* @return course_module_competency[]
*/
public static function get_records_by_competencyid_in_course($competencyid, $courseid) {
global $DB;
$sql = 'SELECT cmc.*
FROM {' . self::TABLE . '} cmc
JOIN {course_modules} cm
ON cm.course = ?
AND cmc.cmid = cm.id
WHERE cmc.competencyid = ?
ORDER BY cmc.sortorder ASC';
$params = array($courseid, $competencyid);
$results = $DB->get_recordset_sql($sql, $params);
$instances = array();
foreach ($results as $result) {
$instances[$result->id] = new course_module_competency(0, $result);
}
$results->close();
return $instances;
}
}
+344
View File
@@ -0,0 +1,344 @@
<?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/>.
/**
* Evidence persistent file.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use coding_exception;
use context;
use context_user;
use lang_string;
use moodle_exception;
use stdClass;
/**
* Evidence persistent class.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class evidence extends persistent {
const TABLE = 'competency_evidence';
/** Action logging. */
const ACTION_LOG = 0;
/** Action rating a competency when no rating is set. */
const ACTION_COMPLETE = 2;
/** Action rating a competency. */
const ACTION_OVERRIDE = 3;
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'usercompetencyid' => array(
'type' => PARAM_INT
),
'contextid' => array(
'type' => PARAM_INT
),
'action' => array(
'type' => PARAM_INT,
'choices' => array(self::ACTION_LOG, self::ACTION_COMPLETE, self::ACTION_OVERRIDE)
),
'actionuserid' => array(
'type' => PARAM_INT,
'default' => null,
'null' => NULL_ALLOWED
),
'descidentifier' => array(
'type' => PARAM_STRINGID
),
'desccomponent' => array(
'type' => PARAM_COMPONENT
),
'desca' => array(
'type' => PARAM_RAW,
'default' => null,
'null' => NULL_ALLOWED
),
'url' => array(
'type' => PARAM_URL,
'default' => null,
'null' => NULL_ALLOWED
),
'grade' => array(
'type' => PARAM_INT,
'default' => null,
'null' => NULL_ALLOWED
),
'note' => array(
'type' => PARAM_NOTAGS,
'default' => null,
'null' => NULL_ALLOWED
)
);
}
/**
* Return the competency linked to this.
*
* @return competency
*/
public function get_competency() {
return user_competency::get_competency_by_usercompetencyid($this->get('usercompetencyid'));
}
/**
* Return the evidence's context.
*
* @return context
*/
public function get_context() {
return context::instance_by_id($this->get('contextid'));
}
/**
* Convenience method to get the description $a.
*
* @return mixed
*/
protected function get_desca() {
$value = $this->raw_get('desca');
if ($value !== null) {
$value = json_decode($value);
}
return $value;
}
/**
* Convenience method to get the description.
*
* @return lang_string
*/
public function get_description() {
return new lang_string($this->get('descidentifier'), $this->get('desccomponent'), $this->get_desca());
}
/**
* Convenience method to set the description $a.
*
* @param mixed $value
* @return mixed
*/
protected function set_desca($value) {
if ($value !== null) {
if (!is_scalar($value) && !is_array($value) && !($value instanceof stdClass)) {
throw new coding_exception('$a format not supported.');
}
$value = json_encode($value);
}
$this->raw_set('desca', $value);
}
/**
* Convenience method handling moodle_urls.
*
* @param null|string|moodle_url $url The URL.
*/
protected function set_url($url) {
if ($url instanceof \moodle_url) {
$url = $url->out(false);
}
$this->raw_set('url', $url);
}
/**
* Validate the action user ID.
*
* @param int $value A user ID.
* @return true|lang_string
*/
protected function validate_actionuserid($value) {
if ($value !== null && !\core_user::is_real_user($value)) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate the context ID.
*
* @param int $value
* @return true|lang_string
*/
protected function validate_contextid($value) {
try {
context::instance_by_id($value);
} catch (moodle_exception $e) {
// That does not look good...
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate the description $a.
*
* @param string $value
* @return true|lang_string
*/
protected function validate_desca($value) {
if ($value === null) {
return true;
}
$desc = json_decode($value);
if ($desc === null && json_last_error() !== JSON_ERROR_NONE) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate the description identifier.
*
* Only validate string existence during create. If the string is removed later on we should
* not prevent this model from being updated. Alternatively we could check if the string has
* changed before performing the check but this overhead is not required for now.
* An evidence should usually never be updated anyway.
*
* @param string $value
* @return true|lang_string
*/
protected function validate_descidentifier($value) {
if (!$this->get('id') && !get_string_manager()->string_exists($value, $this->get('desccomponent'))) {
return new lang_string('invalidevidencedesc', 'core_competency');
}
return true;
}
/**
* Validate the grade.
*
* For performance reason we do not validate that the grade is a valid item of the
* scale associated with the competency or framework.
*
* @param int $value The value.
* @return true|lang_string
*/
protected function validate_grade($value) {
if ($value !== null && $value <= 0) {
return new lang_string('invalidgrade', 'core_competency');
}
$action = $this->get('action');
if ($value === null && $action == self::ACTION_COMPLETE) {
return new lang_string('invalidgrade', 'core_competency');
} else if ($value !== null && $action == self::ACTION_LOG) {
return new lang_string('invalidgrade', 'core_competency');
}
if ($value !== null) {
// TODO MDL-52243 Use a core method to validate the grade_scale item.
// Check if grade exist in the scale item values.
$competency = $this->get_competency();
if (!array_key_exists($value - 1, $competency->get_scale()->scale_items)) {
return new lang_string('invalidgrade', 'core_competency');
}
}
return true;
}
/**
* Validate the user competency.
*
* @param int $value
* @return true|lang_string
*/
protected function validate_usercompetencyid($value) {
if (!user_competency::record_exists($value)) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Whether the current user can delete an evidence in the context of a user.
*
* @param int $userid The user ID the evidence belongs to.
* @return bool
*/
public static function can_delete_user($userid) {
return has_capability('moodle/competency:evidencedelete', context_user::instance($userid));
}
/**
* Load a list of records in a context for a user competency.
*
* @param int $usercompetencyid The id of the user competency.
* @param context $context Context to filter the evidence list.
* @param string $sort The field from the evidence table to sort on.
* @param string $order The sort direction
* @param int $skip Limitstart.
* @param int $limit Number of rows to return.
*
* @return \core_competency\persistent[]
*/
public static function get_records_for_usercompetency($usercompetencyid,
\context $context,
$sort = '',
$order = 'ASC',
$skip = 0,
$limit = 0) {
global $DB;
$params = array(
'usercompid' => $usercompetencyid,
'path' => $context->path . '/%',
'contextid' => $context->id
);
if (!empty($sort)) {
$sort = ' ORDER BY e.' . $sort . ' ' . $order . ', e.id ASC';
} else {
$sort = ' ORDER BY e.id ASC';
}
$sql = 'SELECT e.*
FROM {' . static::TABLE . '} e
JOIN {context} c ON c.id = e.contextid
WHERE (c.path LIKE :path OR c.id = :contextid)
AND e.usercompetencyid = :usercompid
' . $sort;
$records = $DB->get_records_sql($sql, $params, $skip, $limit);
$instances = array();
foreach ($records as $record) {
$newrecord = new static(0, $record);
array_push($instances, $newrecord);
}
return $instances;
}
}
File diff suppressed because it is too large Load Diff
+43
View File
@@ -0,0 +1,43 @@
<?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.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
/**
* Class for exporting competency data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class competency_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\competency::class;
}
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');
}
}
@@ -0,0 +1,95 @@
<?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_framework data.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
use core_competency\api;
use renderer_base;
/**
* Class for exporting competency_framework data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class competency_framework_exporter extends \core\external\persistent_exporter {
/**
* Define the name of persistent class.
*
* @return string
*/
protected static function define_class() {
return \core_competency\competency_framework::class;
}
/**
* Get other values that do not belong to the basic persisent.
*
* @param renderer_base $output
* @return Array
*/
protected function get_other_values(renderer_base $output) {
$filters = array('competencyframeworkid' => $this->persistent->get('id'));
$context = $this->persistent->get_context();
$competenciescount = 0;
try {
$competenciescount = api::count_competencies($filters);
} catch (\required_capability_exception $re) {
$competenciescount = 0;
}
return array(
'canmanage' => has_capability('moodle/competency:competencymanage', $context),
'competenciescount' => $competenciescount,
'contextname' => $context->get_context_name(),
'contextnamenoprefix' => $context->get_context_name(false)
);
}
/**
* Define other properties that do not belong to the basic persisent.
*
* @return Array
*/
protected static function define_other_properties() {
return array(
'canmanage' => array(
'type' => PARAM_BOOL
),
'competenciescount' => array(
'type' => PARAM_INT
),
// Both contexts need to be PARAM_RAW because the method context::get_context_name()
// already applies the formatting and thus could return HTML content.
'contextname' => array(
'type' => PARAM_RAW
),
'contextnamenoprefix' => array(
'type' => PARAM_RAW
)
);
}
}
@@ -0,0 +1,38 @@
<?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 course competency data.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
/**
* Class for exporting course competency data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_competency_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\course_competency::class;
}
}
@@ -0,0 +1,40 @@
<?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 course_competency_settings data.
*
* @package core_competency
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
/**
* Class for exporting course_competency_settings data.
*
* @package core_competency
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_competency_settings_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\course_competency_settings::class;
}
}
@@ -0,0 +1,38 @@
<?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 course module competency data.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
/**
* Class for exporting course module competency data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_competency_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\course_module_competency::class;
}
}
+138
View File
@@ -0,0 +1,138 @@
<?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 evidence data.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
use context_system;
use renderer_base;
use core_competency\evidence;
use core_competency\user_competency;
use core_user\external\user_summary_exporter;
/**
* Class for exporting evidence data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class evidence_exporter extends \core\external\persistent_exporter {
/**
* Constructor.
*
* @param mixed $data The data.
* @param array $related Array of relateds.
*/
public function __construct($data, $related = array()) {
if (!isset($related['context'])) {
// Previous code was automatically using the system context which was not correct.
// We let developers know that they must fix their code without breaking anything, and
// fallback on the previous behaviour. This should be removed at a later stage: Moodle 3.5.
debugging('Missing related context in evidence_exporter.', DEBUG_DEVELOPER);
$related['context'] = context_system::instance();
}
parent::__construct($data, $related);
}
protected static function define_related() {
return array(
'actionuser' => 'stdClass?',
'context' => 'context',
'scale' => 'grade_scale',
'usercompetency' => 'core_competency\\user_competency?',
'usercompetencyplan' => 'core_competency\\user_competency_plan?',
);
}
protected static function define_class() {
return evidence::class;
}
protected function get_other_values(renderer_base $output) {
$other = array();
if (!empty($this->related['actionuser'])) {
$exporter = new user_summary_exporter($this->related['actionuser']);
$actionuser = $exporter->export($output);
$other['actionuser'] = $actionuser;
}
$other['description'] = $this->persistent->get_description();
$other['userdate'] = userdate($this->persistent->get('timecreated'));
if ($this->persistent->get('grade') === null) {
$gradename = '-';
} else {
$gradename = $this->related['scale']->scale_items[$this->persistent->get('grade') - 1];
}
$other['gradename'] = $gradename;
// Try to guess the user from the user competency.
$userid = null;
if ($this->related['usercompetency']) {
$userid = $this->related['usercompetency']->get('userid');
} else if ($this->related['usercompetencyplan']) {
$userid = $this->related['usercompetencyplan']->get('userid');
} else {
$uc = user_competency::get_record(['id' => $this->persistent->get('usercompetencyid')]);
$userid = $uc->get('userid');
}
$other['candelete'] = evidence::can_delete_user($userid);
return $other;
}
/**
* Get the format parameters for gradename.
*
* @return array
*/
protected function get_format_parameters_for_gradename() {
return [
'context' => context_system::instance(), // The system context is cached, so we can get it right away.
];
}
public static function define_other_properties() {
return array(
'actionuser' => array(
'type' => user_summary_exporter::read_properties_definition(),
'optional' => true
),
'description' => array(
'type' => PARAM_TEXT, // The description may contain course names, etc.. which may need filtering.
),
'gradename' => array(
'type' => PARAM_TEXT,
),
'userdate' => array(
'type' => PARAM_NOTAGS
),
'candelete' => array(
'type' => PARAM_BOOL
)
);
}
}
+123
View File
@@ -0,0 +1,123 @@
<?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/>.
/**
* Performance helper.
*
* @package core_competency
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
use core_competency\competency;
use core_competency\competency_framework;
/**
* Performance helper class.
*
* This tool keeps a local cache of certain items, which means that subsequent
* calls to get the resource will not query the database. You will want to use
* this when many resources could be shared and need to be queried in a loop.
*
* Note that some of these improvements can only be achieved by knowing the
* logic deeper in other modules. For instance we know that a competency's context
* is the one of its framework. This tool must be kept in sync with those APIs.
*
* @package core_competency
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class performance_helper {
/** @var \context Cache of contexts by framework ID. */
protected $frameworkscontexts = [];
/** @var competency_framework Cache of frameworks by framework ID. */
protected $frameworks = [];
/** @var \grade_scale[] Cache of scales by scale ID. */
protected $scales = [];
/**
* Get the context of a competency.
*
* @param competency $competency The competency.
* @return \context
*/
public function get_context_from_competency(competency $competency) {
$frameworkid = $competency->get('competencyframeworkid');
if (!isset($this->frameworkscontexts[$frameworkid])) {
$framework = $this->get_framework_from_competency($competency);
$this->frameworkscontexts[$frameworkid] = $framework->get_context();
}
return $this->frameworkscontexts[$frameworkid];
}
/**
* Get the framework of a competency.
*
* @param competency $competency The competency.
* @return competency_framework
*/
public function get_framework_from_competency(competency $competency) {
$frameworkid = $competency->get('competencyframeworkid');
if (!isset($this->frameworks[$frameworkid])) {
$this->frameworks[$frameworkid] = $competency->get_framework();
}
return $this->frameworks[$frameworkid];
}
/**
* Get the scale of a competency.
*
* /!\ Make sure that this is always kept in sync with:
* - core_competency\competency::get_scale()
* - core_competency\competency_framework::get_scale()
*
* @param competency $competency The competency.
* @return \grade_scale
*/
public function get_scale_from_competency(competency $competency) {
$scaleid = $competency->get('scaleid');
if ($scaleid !== null && !isset($this->scales[$scaleid])) {
$this->scales[$scaleid] = $competency->get_scale();
} else if ($scaleid === null) {
$framework = $this->get_framework_from_competency($competency);
$scaleid = $framework->get('scaleid');
if (!isset($this->scales[$scaleid])) {
$this->scales[$scaleid] = $framework->get_scale();
}
}
return $this->scales[$scaleid];
}
/**
* Ingest a framework to avoid additional fetching.
*
* @param competency_framework $framework The framework.
* @return void
*/
public function ingest_framework(competency_framework $framework) {
$id = $framework->get('id');
$this->frameworks[$id] = $framework;
}
}
@@ -0,0 +1,38 @@
<?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 plan competency data.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
/**
* Class for exporting plan competency data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class plan_competency_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\plan_competency::class;
}
}
+191
View File
@@ -0,0 +1,191 @@
<?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 plan data.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
use core_user;
use renderer_base;
use stdClass;
use moodle_url;
use core_competency\url;
use core_comment\external\comment_area_exporter;
use core_user\external\user_summary_exporter;
/**
* Class for exporting plan data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class plan_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\plan::class;
}
protected static function define_related() {
return array('template' => 'core_competency\\template?');
}
protected function get_other_values(renderer_base $output) {
$classname = static::define_class();
$status = $this->persistent->get('status');
$values = new stdClass();
$values->statusname = $this->persistent->get_statusname();
$values->isbasedontemplate = $this->persistent->is_based_on_template();
$values->canmanage = $this->persistent->can_manage();
$values->canrequestreview = $this->persistent->can_request_review();
$values->canreview = $this->persistent->can_review();
$values->canbeedited = $this->persistent->can_be_edited();
$values->isactive = $status == $classname::STATUS_ACTIVE;
$values->isdraft = $status == $classname::STATUS_DRAFT;
$values->iscompleted = $status == $classname::STATUS_COMPLETE;
$values->isinreview = $status == $classname::STATUS_IN_REVIEW;
$values->iswaitingforreview = $status == $classname::STATUS_WAITING_FOR_REVIEW;
$values->isreopenallowed = $values->canmanage && $values->iscompleted;
$values->iscompleteallowed = $values->canmanage && $values->isactive;
$values->isunlinkallowed = $values->canmanage && !$values->iscompleted && $values->isbasedontemplate;
$values->isrequestreviewallowed = false;
$values->iscancelreviewrequestallowed = false;
$values->isstartreviewallowed = false;
$values->isstopreviewallowed = false;
$values->isapproveallowed = false;
$values->isunapproveallowed = false;
if (!$values->isbasedontemplate) {
$values->isrequestreviewallowed = $values->canrequestreview && $values->isdraft;
$values->iscancelreviewrequestallowed = $values->canrequestreview && $values->iswaitingforreview;
$values->isstartreviewallowed = $values->canreview && $values->iswaitingforreview;
$values->isstopreviewallowed = $values->canreview && $values->isinreview;
$values->isapproveallowed = $values->canreview && !$values->iscompleted && !$values->isactive;
$values->isunapproveallowed = $values->canreview && $values->isactive;
}
$values->duedateformatted = userdate($this->persistent->get('duedate'));
if ($this->persistent->is_based_on_template()) {
$exporter = new template_exporter($this->related['template']);
$values->template = $exporter->export($output);
}
if (!empty($values->isinreview)) {
// TODO Make this more efficient.
$userexporter = new user_summary_exporter(core_user::get_user($this->persistent->get('reviewerid'), '*', MUST_EXIST));
$values->reviewer = $userexporter->export($output);
}
$commentareaexporter = new comment_area_exporter($this->persistent->get_comment_object());
$values->commentarea = $commentareaexporter->export($output);
$values->url = url::plan($this->persistent->get('id'))->out(false);
return (array) $values;
}
public static function define_other_properties() {
return array(
'statusname' => array(
'type' => PARAM_RAW,
),
'isbasedontemplate' => array(
'type' => PARAM_BOOL,
),
'canmanage' => array(
'type' => PARAM_BOOL,
),
'canrequestreview' => array(
'type' => PARAM_BOOL,
),
'canreview' => array(
'type' => PARAM_BOOL,
),
'canbeedited' => array(
'type' => PARAM_BOOL,
),
'isactive' => array(
'type' => PARAM_BOOL
),
'isdraft' => array(
'type' => PARAM_BOOL
),
'iscompleted' => array(
'type' => PARAM_BOOL
),
'isinreview' => array(
'type' => PARAM_BOOL
),
'iswaitingforreview' => array(
'type' => PARAM_BOOL
),
'isreopenallowed' => array(
'type' => PARAM_BOOL
),
'iscompleteallowed' => array(
'type' => PARAM_BOOL
),
'isunlinkallowed' => array(
'type' => PARAM_BOOL
),
'isrequestreviewallowed' => array(
'type' => PARAM_BOOL
),
'iscancelreviewrequestallowed' => array(
'type' => PARAM_BOOL
),
'isstartreviewallowed' => array(
'type' => PARAM_BOOL
),
'isstopreviewallowed' => array(
'type' => PARAM_BOOL
),
'isapproveallowed' => array(
'type' => PARAM_BOOL
),
'isunapproveallowed' => array(
'type' => PARAM_BOOL
),
'duedateformatted' => array(
'type' => PARAM_TEXT
),
'commentarea' => array(
'type' => comment_area_exporter::read_properties_definition(),
),
'reviewer' => array(
'type' => user_summary_exporter::read_properties_definition(),
'optional' => true
),
'template' => array(
'type' => template_exporter::read_properties_definition(),
'optional' => true,
),
'url' => array(
'type' => PARAM_URL
)
);
}
}
@@ -0,0 +1,38 @@
<?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 plan data.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
/**
* Class for exporting related competency data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class related_competency_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\related_competency::class;
}
}
@@ -0,0 +1,38 @@
<?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 template competency data.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
/**
* Class for exporting template competency data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_competency_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\template_competency::class;
}
}
+82
View File
@@ -0,0 +1,82 @@
<?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 template data.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
use moodle_url;
use renderer_base;
use core_competency\plan;
use core_competency\template_cohort;
/**
* Class for exporting template data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\template::class;
}
protected function get_other_values(renderer_base $output) {
$context = $this->persistent->get_context();
return array(
'duedateformatted' => userdate($this->persistent->get('duedate')),
'cohortscount' => template_cohort::count_records(array('templateid' => $this->persistent->get('id'))),
'planscount' => plan::count_records(array('templateid' => $this->persistent->get('id'))),
'canmanage' => $this->persistent->can_manage(),
'canread' => $this->persistent->can_read(),
'contextname' => $context->get_context_name(),
'contextnamenoprefix' => $context->get_context_name(false)
);
}
protected static function define_other_properties() {
return array(
'duedateformatted' => array(
'type' => PARAM_RAW
),
'cohortscount' => array(
'type' => PARAM_INT
),
'planscount' => array(
'type' => PARAM_INT
),
'canmanage' => array(
'type' => PARAM_BOOL
),
'canread' => array(
'type' => PARAM_BOOL
),
'contextname' => array(
'type' => PARAM_TEXT,
),
'contextnamenoprefix' => array(
'type' => PARAM_TEXT,
)
);
}
}
@@ -0,0 +1,89 @@
<?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 user competency course data.
*
* @package core_competency
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
use context_system;
use renderer_base;
use stdClass;
/**
* Class for exporting user competency course data.
*
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_competency_course_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\user_competency_course::class;
}
protected static function define_related() {
// We cache the scale so it does not need to be retrieved from the framework every time.
return array('scale' => 'grade_scale');
}
protected function get_other_values(renderer_base $output) {
$result = new stdClass();
if ($this->persistent->get('grade') === null) {
$gradename = '-';
} else {
$gradename = $this->related['scale']->scale_items[$this->persistent->get('grade') - 1];
}
$result->gradename = $gradename;
if ($this->persistent->get('proficiency') === null) {
$proficiencyname = get_string('no');
} else {
$proficiencyname = get_string($this->persistent->get('proficiency') ? 'yes' : 'no');
}
$result->proficiencyname = $proficiencyname;
return (array) $result;
}
/**
* Get the format parameters for gradename.
*
* @return array
*/
protected function get_format_parameters_for_gradename() {
return [
'context' => context_system::instance(), // The system context is cached, so we can get it right away.
];
}
protected static function define_other_properties() {
return array(
'gradename' => array(
'type' => PARAM_TEXT
),
'proficiencyname' => array(
'type' => PARAM_RAW
)
);
}
}
+156
View File
@@ -0,0 +1,156 @@
<?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 user competency data.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
use context_system;
use core_user;
use renderer_base;
use stdClass;
use core_competency\url;
use core_competency\user_competency;
use core_user\external\user_summary_exporter;
/**
* Class for exporting user competency data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_competency_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return user_competency::class;
}
protected static function define_related() {
// We cache the scale so it does not need to be retrieved from the framework every time.
return array('scale' => 'grade_scale');
}
protected function get_other_values(renderer_base $output) {
$result = new stdClass();
if ($this->persistent->get('grade') === null) {
$gradename = '-';
} else {
$gradename = $this->related['scale']->scale_items[$this->persistent->get('grade') - 1];
}
$result->gradename = $gradename;
if ($this->persistent->get('proficiency') === null) {
$proficiencyname = get_string('no');
} else {
$proficiencyname = get_string($this->persistent->get('proficiency') ? 'yes' : 'no');
}
$result->proficiencyname = $proficiencyname;
$statusname = '-';
if ($this->persistent->get('status') != user_competency::STATUS_IDLE) {
$statusname = (string) user_competency::get_status_name($this->persistent->get('status'));
}
$result->statusname = $statusname;
$result->canrequestreview = $this->persistent->can_request_review();
$result->canreview = $this->persistent->can_review();
$result->isstatusidle = $this->persistent->get('status') == user_competency::STATUS_IDLE;
$result->isstatusinreview = $this->persistent->get('status') == user_competency::STATUS_IN_REVIEW;
$result->isstatuswaitingforreview = $this->persistent->get('status') == user_competency::STATUS_WAITING_FOR_REVIEW;
$result->isrequestreviewallowed = $result->canrequestreview && $result->isstatusidle;
$result->iscancelreviewrequestallowed = $result->canrequestreview && $result->isstatuswaitingforreview;
$result->isstartreviewallowed = $result->canreview && $result->isstatuswaitingforreview;
$result->isstopreviewallowed = $result->canreview && $result->isstatusinreview;
if (!empty($result->isstatusinreview)) {
// TODO Make this more efficient.
$userexporter = new user_summary_exporter(core_user::get_user($this->persistent->get('reviewerid'), '*', MUST_EXIST));
$result->reviewer = $userexporter->export($output);
}
$result->url = url::user_competency($this->persistent->get('id'))->out(false);
return (array) $result;
}
/**
* Get the format parameters for gradename.
*
* @return array
*/
protected function get_format_parameters_for_gradename() {
return [
'context' => context_system::instance(), // The system context is cached, so we can get it right away.
];
}
protected static function define_other_properties() {
return array(
'canrequestreview' => array(
'type' => PARAM_BOOL,
),
'canreview' => array(
'type' => PARAM_BOOL,
),
'gradename' => array(
'type' => PARAM_TEXT
),
'isrequestreviewallowed' => array(
'type' => PARAM_BOOL,
),
'iscancelreviewrequestallowed' => array(
'type' => PARAM_BOOL,
),
'isstartreviewallowed' => array(
'type' => PARAM_BOOL,
),
'isstopreviewallowed' => array(
'type' => PARAM_BOOL,
),
'isstatusidle' => array(
'type' => PARAM_BOOL,
),
'isstatusinreview' => array(
'type' => PARAM_BOOL,
),
'isstatuswaitingforreview' => array(
'type' => PARAM_BOOL,
),
'proficiencyname' => array(
'type' => PARAM_RAW
),
'reviewer' => array(
'type' => user_summary_exporter::read_properties_definition(),
'optional' => true
),
'statusname' => array(
'type' => PARAM_RAW
),
'url' => array(
'type' => PARAM_URL
),
);
}
}
@@ -0,0 +1,89 @@
<?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 plan competency data.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
use context_system;
use renderer_base;
use stdClass;
/**
* Class for exporting plan competency data.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_competency_plan_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\user_competency_plan::class;
}
protected static function define_related() {
// We cache the scale so it does not need to be retrieved from the framework every time.
return array('scale' => 'grade_scale');
}
protected function get_other_values(renderer_base $output) {
$result = new stdClass();
if ($this->persistent->get('grade') === null) {
$gradename = '-';
} else {
$gradename = $this->related['scale']->scale_items[$this->persistent->get('grade') - 1];
}
$result->gradename = $gradename;
if ($this->persistent->get('proficiency') === null) {
$proficiencyname = get_string('no');
} else {
$proficiencyname = get_string($this->persistent->get('proficiency') ? 'yes' : 'no');
}
$result->proficiencyname = $proficiencyname;
return (array) $result;
}
/**
* Get the format parameters for gradename.
*
* @return array
*/
protected function get_format_parameters_for_gradename() {
return [
'context' => context_system::instance(), // The system context is cached, so we can get it right away.
];
}
protected static function define_other_properties() {
return array(
'gradename' => array(
'type' => PARAM_TEXT
),
'proficiencyname' => array(
'type' => PARAM_RAW
),
);
}
}
@@ -0,0 +1,41 @@
<?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/>.
/**
* User evidence competency exporter.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
/**
* User evidence competency exporter class.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_evidence_competency_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\user_evidence_competency::class;
}
}
+120
View File
@@ -0,0 +1,120 @@
<?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 user_evidence data.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency\external;
defined('MOODLE_INTERNAL') || die();
use moodle_url;
use renderer_base;
use core_competency\external\performance_helper;
use core_files\external\stored_file_exporter;
/**
* Class for exporting user_evidence data.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_evidence_exporter extends \core\external\persistent_exporter {
protected static function define_class() {
return \core_competency\user_evidence::class;
}
protected static function define_other_properties() {
return array(
'canmanage' => array(
'type' => PARAM_BOOL
),
'competencycount' => array(
'type' => PARAM_INT
),
'competencies' => array(
'type' => competency_exporter::read_properties_definition(),
'multiple' => true
),
'filecount' => array(
'type' => PARAM_INT
),
'files' => array(
'type' => stored_file_exporter::read_properties_definition(),
'multiple' => true
),
'hasurlorfiles' => array(
'type' => PARAM_BOOL
),
'urlshort' => array(
'type' => PARAM_TEXT
),
);
}
protected static function define_related() {
return array(
'context' => 'context',
'competencies' => 'core_competency\\competency[]'
);
}
protected function get_other_values(renderer_base $output) {
$helper = new performance_helper();
$competencies = array();
foreach ($this->related['competencies'] as $competency) {
$context = $helper->get_context_from_competency($competency);
$compexporter = new competency_exporter($competency, array('context' => $context));
$competencies[] = $compexporter->export($output);
}
$urlshort = '';
$url = $this->persistent->get('url');
if (!empty($url)) {
$murl = new moodle_url($url);
$shorturl = preg_replace('@^https?://(www\.)?@', '', $murl->out(false));
$urlshort = shorten_text($shorturl, 30, true);
}
$files = array();
$storedfiles = $this->persistent->get_files();
if (!empty($storedfiles)) {
foreach ($storedfiles as $storedfile) {
$fileexporter = new stored_file_exporter($storedfile, array('context' => $this->related['context']));
$files[] = $fileexporter->export($output);
}
}
$values = array(
'canmanage' => $this->persistent->can_manage(),
'competencycount' => count($competencies),
'competencies' => $competencies,
'filecount' => count($files),
'files' => $files,
'hasurlorfiles' => !empty($files) || !empty($url),
'urlshort' => $urlshort
);
return $values;
}
}
@@ -0,0 +1,41 @@
<?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/>.
/**
* Invalid persistent exception.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
debugging('The class core_competency\\invalid_persistent_exception is deprecated. ' .
'Please use core\\invalid_persistent_exception instead.');
/**
* Invalid persistent exception class.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @deprecated since Moodle 3.3
*/
class invalid_persistent_exception extends \core\invalid_persistent_exception {
}
+66
View File
@@ -0,0 +1,66 @@
<?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/>.
/**
* Abstract class for core_competency objects saved to the DB.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
// We need to alias the invalid_persistent_exception, because the persistent classes from
// core_competency used to throw a \core_competency\invalid_persistent_exception. They now
// fully inherit from \core\persistent which throws a core exception. Using class_alias
// ensures that previous try/catch statements still work. Also note that we always need
// need to alias, we cannot do it passively in the classloader because try/catch statements
// do not trigger a class loading. Note that for this trick to work, all the classes
// which were extending \core_competency\persistent still need to extend it or the alias
// won't be effective.
class_alias('core\\invalid_persistent_exception', 'core_competency\\invalid_persistent_exception');
/**
* Abstract class for core_competency objects saved to the DB.
*
* This is a legacy class which all core_competency persistent classes created prior
* to 3.3 must extend.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class persistent extends \core\persistent {
/**
* Magic method to capture getters and setters.
* This is only available for competency persistents for backwards compatibility.
* It is recommended to use get('propertyname') and set('propertyname', 'value') directly.
*
* @param string $method Callee.
* @param array $arguments List of arguments.
* @return mixed
*/
final public function __call($method, $arguments) {
debugging('Use of magic setters and getters is deprecated. Use get() and set().', DEBUG_DEVELOPER);
if (strpos($method, 'get_') === 0) {
return $this->get(substr($method, 4));
} else if (strpos($method, 'set_') === 0) {
return $this->set(substr($method, 4), $arguments[0]);
}
throw new \coding_exception('Unexpected method call: ' . $method);
}
}
+714
View File
@@ -0,0 +1,714 @@
<?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 plans persistence.
*
* @package core_competency
* @copyright 2015 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use comment;
use context_user;
use dml_missing_record_exception;
use lang_string;
/**
* Class for loading/storing plans from the DB.
*
* @copyright 2015 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class plan extends persistent {
const TABLE = 'competency_plan';
/** Draft status. */
const STATUS_DRAFT = 0;
/** Active status. */
const STATUS_ACTIVE = 1;
/** Complete status. */
const STATUS_COMPLETE = 2;
/** Waiting for review. */
const STATUS_WAITING_FOR_REVIEW = 3;
/** In review. */
const STATUS_IN_REVIEW = 4;
/** 10 minutes threshold **/
const DUEDATE_THRESHOLD = 600;
/** @var plan object before update. */
protected $beforeupdate = null;
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'name' => array(
'type' => PARAM_TEXT,
),
'description' => array(
'type' => PARAM_CLEANHTML,
'default' => ''
),
'descriptionformat' => array(
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
'type' => PARAM_INT,
'default' => FORMAT_HTML,
),
'userid' => array(
'type' => PARAM_INT,
),
'templateid' => array(
'type' => PARAM_INT,
'default' => null,
'null' => NULL_ALLOWED,
),
'origtemplateid' => array(
'type' => PARAM_INT,
'default' => null,
'null' => NULL_ALLOWED,
),
'status' => array(
'choices' => array(self::STATUS_DRAFT, self::STATUS_COMPLETE, self::STATUS_ACTIVE,
self::STATUS_WAITING_FOR_REVIEW, self::STATUS_IN_REVIEW),
'type' => PARAM_INT,
'default' => self::STATUS_DRAFT,
),
'duedate' => array(
'type' => PARAM_INT,
'default' => 0,
),
'reviewerid' => array(
'type' => PARAM_INT,
'default' => null,
'null' => NULL_ALLOWED,
)
);
}
/**
* Hook to execute before validate.
*
* @return void
*/
protected function before_validate() {
$this->beforeupdate = null;
// During update.
if ($this->get('id')) {
$this->beforeupdate = new self($this->get('id'));
}
}
/**
* Whether the current user can comment on this plan.
*
* @return bool
*/
public function can_comment() {
return static::can_comment_user($this->get('userid'));
}
/**
* Whether the current user can manage the plan.
*
* @return bool
*/
public function can_manage() {
if ($this->is_draft()) {
return self::can_manage_user_draft($this->get('userid'));
}
return self::can_manage_user($this->get('userid'));
}
/**
* Whether the current user can read the plan.
*
* @return bool
*/
public function can_read() {
if ($this->is_draft()) {
return self::can_read_user_draft($this->get('userid'));
}
return self::can_read_user($this->get('userid'));
}
/**
* Whether the current user can read comments on this plan.
*
* @return bool
*/
public function can_read_comments() {
return $this->can_read();
}
/**
* Whether the current user can request a review of the plan.
*
* @return bool
*/
public function can_request_review() {
return self::can_request_review_user($this->get('userid'));
}
/**
* Whether the current user can review the plan.
*
* @return bool
*/
public function can_review() {
return self::can_review_user($this->get('userid'));
}
/**
* Get the comment object.
*
* @return comment
*/
public function get_comment_object() {
global $CFG;
require_once($CFG->dirroot . '/comment/lib.php');
if (!$this->get('id')) {
throw new \coding_exception('The plan must exist.');
}
$comment = new comment((object) array(
'client_id' => 'plancommentarea' . $this->get('id'),
'context' => $this->get_context(),
'component' => 'competency', // This cannot be named 'core_competency'.
'itemid' => $this->get('id'),
'area' => 'plan',
'showcount' => true,
));
$comment->set_fullwidth(true);
return $comment;
}
/**
* Get the competencies in this plan.
*
* @return competency[]
*/
public function get_competencies() {
$competencies = array();
if ($this->get('status') == self::STATUS_COMPLETE) {
// Get the competencies from the archive of the plan.
$competencies = user_competency_plan::list_competencies($this->get('id'), $this->get('userid'));
} else if ($this->is_based_on_template()) {
// Get the competencies from the template.
$competencies = template_competency::list_competencies($this->get('templateid'));
} else {
// Get the competencies from the plan.
$competencies = plan_competency::list_competencies($this->get('id'));
}
return $competencies;
}
/**
* Get a single competency from this plan.
*
* This will throw an exception if the competency does not belong to the plan.
*
* @param int $competencyid The competency ID.
* @return competency
*/
public function get_competency($competencyid) {
$competency = null;
if ($this->get('status') == self::STATUS_COMPLETE) {
// Get the competency from the archive of the plan.
$competency = user_competency_plan::get_competency_by_planid($this->get('id'), $competencyid);
} else if ($this->is_based_on_template()) {
// Get the competency from the template.
$competency = template_competency::get_competency($this->get('templateid'), $competencyid);
} else {
// Get the competency from the plan.
$competency = plan_competency::get_competency($this->get('id'), $competencyid);
}
return $competency;
}
/**
* Get the context in which the plan is attached.
*
* @return context_user
*/
public function get_context() {
return context_user::instance($this->get('userid'));
}
/**
* Human readable status name.
*
* @return string
*/
public function get_statusname() {
$status = $this->get('status');
switch ($status) {
case self::STATUS_DRAFT:
$strname = 'draft';
break;
case self::STATUS_IN_REVIEW:
$strname = 'inreview';
break;
case self::STATUS_WAITING_FOR_REVIEW:
$strname = 'waitingforreview';
break;
case self::STATUS_ACTIVE:
$strname = 'active';
break;
case self::STATUS_COMPLETE:
$strname = 'complete';
break;
default:
throw new \moodle_exception('errorplanstatus', 'core_competency', '', $status);
break;
}
return get_string('planstatus' . $strname, 'core_competency');
}
/**
* Get the plan template.
*
* @return template|null
*/
public function get_template() {
$templateid = $this->get('templateid');
if ($templateid === null) {
return null;
}
return new template($templateid);
}
/**
* Is the plan in draft mode?
*
* This method is convenient to know if the plan is a draft because whilst a draft
* is being reviewed its status is not "draft" any more, but it still is a draft nonetheless.
*
* @return boolean
*/
public function is_draft() {
return in_array($this->get('status'), static::get_draft_statuses());
}
/**
* Validate the template ID.
*
* @param mixed $value The value.
* @return true|lang_string
*/
protected function validate_templateid($value) {
// Checks that the template exists.
if (!empty($value) && !template::record_exists($value)) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate the user ID.
*
* @param int $value
* @return true|lang_string
*/
protected function validate_userid($value) {
global $DB;
// During create.
if (!$this->get('id')) {
// Check that the user exists. We do not need to do that on update because
// the userid of a plan should never change.
if (!$DB->record_exists('user', array('id' => $value))) {
return new lang_string('invaliddata', 'error');
}
}
return true;
}
/**
* Can the current user comment on a user's plan?
*
* @param int $planuserid The user ID the plan belongs to.
* @return bool
*/
public static function can_comment_user($planuserid) {
global $USER;
$capabilities = array('moodle/competency:plancomment');
if ($USER->id == $planuserid) {
$capabilities[] = 'moodle/competency:plancommentown';
}
return has_any_capability($capabilities, context_user::instance($planuserid));
}
/**
* Can the current user manage a user's plan?
*
* @param int $planuserid The user to whom the plan would belong.
* @return bool
*/
public static function can_manage_user($planuserid) {
global $USER;
$context = context_user::instance($planuserid);
$capabilities = array('moodle/competency:planmanage');
if ($context->instanceid == $USER->id) {
$capabilities[] = 'moodle/competency:planmanageown';
}
return has_any_capability($capabilities, $context);
}
/**
* Can the current user manage a user's draft plan?
*
* @param int $planuserid The user to whom the plan would belong.
* @return bool
*/
public static function can_manage_user_draft($planuserid) {
global $USER;
$context = context_user::instance($planuserid);
$capabilities = array('moodle/competency:planmanagedraft');
if ($context->instanceid == $USER->id) {
$capabilities[] = 'moodle/competency:planmanageowndraft';
}
return has_any_capability($capabilities, $context);
}
/**
* Can the current user read the comments on a user's plan?
*
* @param int $planuserid The user ID the plan belongs to.
* @return bool
*/
public static function can_read_comments_user($planuserid) {
// Everyone who can read the plan can read the comments.
return static::can_read_user($planuserid);
}
/**
* Can the current user view a user's plan?
*
* @param int $planuserid The user to whom the plan would belong.
* @return bool
*/
public static function can_read_user($planuserid) {
global $USER;
$context = context_user::instance($planuserid);
$capabilities = array('moodle/competency:planview');
if ($context->instanceid == $USER->id) {
$capabilities[] = 'moodle/competency:planviewown';
}
return has_any_capability($capabilities, $context)
|| self::can_manage_user($planuserid);
}
/**
* Can the current user view a user's draft plan?
*
* @param int $planuserid The user to whom the plan would belong.
* @return bool
*/
public static function can_read_user_draft($planuserid) {
global $USER;
$context = context_user::instance($planuserid);
$capabilities = array('moodle/competency:planviewdraft');
if ($context->instanceid == $USER->id) {
$capabilities[] = 'moodle/competency:planviewowndraft';
}
return has_any_capability($capabilities, $context)
|| self::can_manage_user_draft($planuserid);
}
/**
* Can the current user request the draft to be reviewed.
*
* @param int $planuserid The user to whom the plan would belong.
* @return bool
*/
public static function can_request_review_user($planuserid) {
global $USER;
$capabilities = array('moodle/competency:planrequestreview');
if ($USER->id == $planuserid) {
$capabilities[] = 'moodle/competency:planrequestreviewown';
}
return has_any_capability($capabilities, context_user::instance($planuserid));
}
/**
* Can the current user review the plan.
*
* This means being able to send the plan from draft to active, and vice versa.
*
* @param int $planuserid The user to whom the plan would belong.
* @return bool
*/
public static function can_review_user($planuserid) {
return has_capability('moodle/competency:planreview', context_user::instance($planuserid))
|| self::can_manage_user($planuserid);
}
/**
* Get the plans of a user containing a specific competency.
*
* @param int $userid The user ID.
* @param int $competencyid The competency ID.
* @return plans[]
*/
public static function get_by_user_and_competency($userid, $competencyid) {
global $DB;
$sql = 'SELECT p.*
FROM {' . self::TABLE . '} p
LEFT JOIN {' . plan_competency::TABLE . '} pc
ON pc.planid = p.id
AND pc.competencyid = :competencyid1
LEFT JOIN {' . user_competency_plan::TABLE . '} ucp
ON ucp.planid = p.id
AND ucp.competencyid = :competencyid2
LEFT JOIN {' . template_competency::TABLE . '} tc
ON tc.templateid = p.templateid
AND tc.competencyid = :competencyid3
WHERE p.userid = :userid
AND (pc.id IS NOT NULL
OR ucp.id IS NOT NULL
OR tc.id IS NOT NULL)
ORDER BY p.id ASC';
$params = array(
'competencyid1' => $competencyid,
'competencyid2' => $competencyid,
'competencyid3' => $competencyid,
'userid' => $userid
);
$plans = array();
$records = $DB->get_records_sql($sql, $params);
foreach ($records as $record) {
$plans[$record->id] = new plan(0, $record);
}
return $plans;
}
/**
* Get the list of draft statuses.
*
* @return array Contains the status constants.
*/
public static function get_draft_statuses() {
return array(self::STATUS_DRAFT, self::STATUS_WAITING_FOR_REVIEW, self::STATUS_IN_REVIEW);
}
/**
* Get the recordset of the plans that are due, incomplete and not draft.
*
* @return \moodle_recordset
*/
public static function get_recordset_for_due_and_incomplete() {
global $DB;
$sql = "duedate > 0 AND duedate < :now AND status = :status";
$params = array('now' => time(), 'status' => self::STATUS_ACTIVE);
return $DB->get_recordset_select(self::TABLE, $sql, $params);
}
/**
* Return a list of status depending on capabilities.
*
* @param int $userid The user to whom the plan would belong.
* @return array
*/
public static function get_status_list($userid) {
$status = array();
if (self::can_manage_user_draft($userid)) {
$status[self::STATUS_DRAFT] = get_string('planstatusdraft', 'core_competency');
}
if (self::can_manage_user($userid)) {
$status[self::STATUS_ACTIVE] = get_string('planstatusactive', 'core_competency');
}
return $status;
}
/**
* Update from template.
*
* Bulk update a lot of plans from a template
*
* @param template $template
* @return bool
*/
public static function update_multiple_from_template(template $template) {
global $DB;
if (!$template->is_valid()) {
// As we will bypass this model's validation we rely on the template being validated.
throw new \coding_exception('The template must be validated before updating plans.');
}
$params = array(
'templateid' => $template->get('id'),
'status' => self::STATUS_COMPLETE,
'name' => $template->get('shortname'),
'description' => $template->get('description'),
'descriptionformat' => $template->get('descriptionformat'),
'duedate' => $template->get('duedate'),
);
$sql = "UPDATE {" . self::TABLE . "}
SET name = :name,
description = :description,
descriptionformat = :descriptionformat,
duedate = :duedate
WHERE templateid = :templateid
AND status != :status";
return $DB->execute($sql, $params);
}
/**
* Check if a template is associated to the plan.
*
* @return bool
*/
public function is_based_on_template() {
return $this->get('templateid') !== null;
}
/**
* Check if plan can be edited.
*
* @return bool
*/
public function can_be_edited() {
return !$this->is_based_on_template() && $this->get('status') != self::STATUS_COMPLETE && $this->can_manage();
}
/**
* Validate the due date.
* When setting a due date it must not exceed the DUEDATE_THRESHOLD.
*
* @param int $value The due date.
* @return bool|lang_string
*/
protected function validate_duedate($value) {
// We do not check duedate when plan is draft, complete, unset, or based on a template.
if ($this->is_based_on_template()
|| $this->is_draft()
|| $this->get('status') == self::STATUS_COMPLETE
|| empty($value)) {
return true;
}
// During update.
if ($this->get('id')) {
$before = $this->beforeupdate->get('duedate');
$beforestatus = $this->beforeupdate->get('status');
// The value has not changed, then it's always OK. Though if we're going
// from draft to active it has to has to be validated.
if ($before == $value && !in_array($beforestatus, self::get_draft_statuses())) {
return true;
}
}
if ($value <= time()) {
// We cannot set the date in the past.
return new lang_string('errorcannotsetduedateinthepast', 'core_competency');
}
if ($value <= time() + self::DUEDATE_THRESHOLD) {
// We cannot set the date too soon, but we can leave it empty.
return new lang_string('errorcannotsetduedatetoosoon', 'core_competency');
}
return true;
}
/**
* Checks if a template has user plan records.
*
* @param int $templateid The template ID
* @return boolean
*/
public static function has_records_for_template($templateid) {
return self::record_exists_select('templateid = ?', array($templateid));
}
/**
* Count the number of plans for a template, optionally filtering by status.
*
* @param int $templateid The template ID
* @param int $status The plan status. 0 means all statuses.
* @return int
*/
public static function count_records_for_template($templateid, $status) {
$filters = array('templateid' => $templateid);
if ($status > 0) {
$filters['status'] = $status;
}
return self::count_records($filters);
}
/**
* Get the plans for a template, optionally filtering by status.
*
* @param int $templateid The template ID
* @param int $status The plan status. 0 means all statuses.
* @param int $skip The number of plans to skip
* @param int $limit The max number of plans to return
* @return int
*/
public static function get_records_for_template($templateid, $status = 0, $skip = 0, $limit = 100) {
$filters = array('templateid' => $templateid);
if ($status > 0) {
$filters['status'] = $status;
}
return self::get_records($filters, $skip, $limit);
}
}
+180
View File
@@ -0,0 +1,180 @@
<?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 plan_competency persistence.
*
* @package core_competency
* @copyright 2015 Issam Taboubi <issam.taboubi@umontreal.ca>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use lang_string;
/**
* Class for managing competencies in the plan (add/remove competencies for given plan).
*
* @copyright 2015 Issam Taboubi <issam.taboubi@umontreal.ca>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class plan_competency extends persistent {
/** Table name for plan_competency persistency */
const TABLE = 'competency_plancomp';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'planid' => array(
'type' => PARAM_INT,
),
'competencyid' => array(
'type' => PARAM_INT,
),
'sortorder' => array(
'type' => PARAM_INT,
'default' => null,
),
);
}
/**
* List the competencies in this plan.
*
* @param int $planid The plan id
* @return array[competency]
*/
public static function list_competencies($planid) {
global $DB;
$sql = 'SELECT comp.*
FROM {' . competency::TABLE . '} comp
JOIN {' . self::TABLE . '} plancomp
ON plancomp.competencyid = comp.id
WHERE plancomp.planid = ?
ORDER BY plancomp.sortorder ASC,
plancomp.id ASC';
$params = array($planid);
// TODO MDL-52229 Handle hidden competencies.
$results = $DB->get_records_sql($sql, $params);
$instances = array();
foreach ($results as $result) {
array_push($instances, new competency(0, $result));
}
return $instances;
}
/**
* Get a single competency from the plan (only if it is really in the plan).
*
* @param int $planid The plan id
* @param int $competencyid The competency id
* @return competency
*/
public static function get_competency($planid, $competencyid) {
global $DB;
$sql = 'SELECT comp.*
FROM {' . competency::TABLE . '} comp
JOIN {' . self::TABLE . '} plncomp
ON plncomp.competencyid = comp.id
WHERE plncomp.planid = ? AND plncomp.competencyid = ?';
$params = array($planid, $competencyid);
$result = $DB->get_record_sql($sql, $params);
if (!$result) {
throw new \coding_exception('The competency does not belong to this plan: ' . $competencyid . ', ' . $planid);
}
return new competency(0, $result);
}
/**
* Hook to execute before validate.
*
* @return void
*/
protected function before_validate() {
if (($this->get('id') && $this->get('sortorder') === null) || !$this->get('id')) {
$this->set('sortorder', $this->count_records(array('planid' => $this->get('planid'))));
}
}
/**
* Validate competencyid.
*
* @param int $value ID.
* @return true|lang_string
*/
protected function validate_competencyid($value) {
if (!competency::record_exists($value)) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate planid.
*
* @param int $value ID.
* @return true|lang_string
*/
protected function validate_planid($value) {
if (!plan::record_exists($value)) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Hook to execute after delete.
*
* @param bool $result Whether or not the delete was successful.
* @return void
*/
protected function after_delete($result) {
global $DB;
if (!$result) {
return;
}
$table = '{' . self::TABLE . '}';
$sql = "UPDATE $table SET sortorder = sortorder -1 WHERE planid = ? AND sortorder > ?";
$DB->execute($sql, array($this->get('planid'), $this->get('sortorder')));
}
/**
* Check if plan competency has records for competencies.
*
* @param array $competencyids The competences IDs
* @return boolean
*/
public static function has_records_for_competencies($competencyids) {
global $DB;
list($insql, $params) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED);
return self::record_exists_select("competencyid $insql", $params);
}
}
File diff suppressed because it is too large Load Diff
+213
View File
@@ -0,0 +1,213 @@
<?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 loading/storing related competencies from the DB.
*
* @package core_competency
* @copyright 2015 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use lang_string;
use stdClass;
/**
* Class for loading/storing related_competencies from the DB.
*
* @package core_competency
* @copyright 2015 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class related_competency extends persistent {
const TABLE = 'competency_relatedcomp';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'competencyid' => array(
'type' => PARAM_INT
),
'relatedcompetencyid' => array(
'type' => PARAM_INT
),
);
}
/**
* Validate competency ID.
*
* @param int $data The competency ID.
* @return true|lang_string
*/
protected function validate_competencyid($data) {
if (!competency::record_exists($data)) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate related competency ID.
*
* @param int $data The related competency ID.
* @return true|lang_string
*/
protected function validate_relatedcompetencyid($data) {
if ($this->get('competencyid') == $data) {
// A competency cannot be related to itself.
return new lang_string('invaliddata', 'error');
} if ($this->get('competencyid') > $data) {
// The competency ID must be lower than the related competency ID.
return new lang_string('invaliddata', 'error');
} else if (!competency::record_exists($data)) {
return new lang_string('invaliddata', 'error');
} else if (!competency::share_same_framework(array($data, $this->get('competencyid')))) {
// The competencies must belong to the same framework.
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Get relation specifying both competencies.
*
* This does not perform any validation on the data passed. If the relation exists in the database
* then it is loaded in a the model, if not then it is up to the developer to save the model.
*
* @param int $competencyid
* @param int $relatedcompetencyid
* @return related_competency
*/
public static function get_relation($competencyid, $relatedcompetencyid) {
global $DB;
// Lower id always as competencyid so we know which one is competencyid and which one relatedcompetencyid.
$relation = new static();
if ($competencyid > $relatedcompetencyid) {
$relation->set('competencyid', $relatedcompetencyid);
$relation->set('relatedcompetencyid', $competencyid);
} else {
$relation->set('competencyid', $competencyid);
$relation->set('relatedcompetencyid', $relatedcompetencyid);
}
// We can do it because we have bidirectional relations in the DB.
$params = array(
'competencyid' => $relation->get('competencyid'),
'relatedcompetencyid' => $relation->get('relatedcompetencyid')
);
if ($record = $DB->get_record(self::TABLE, $params)) {
$relation->from_record($record);
}
return $relation;
}
/**
* Get the competencies related to a competency.
*
* @param int $competencyid The competency ID.
* @return competency[]
*/
public static function get_related_competencies($competencyid) {
global $DB;
$fields = competency::get_sql_fields('c', 'c_');
$sql = "(SELECT $fields, " . $DB->sql_concat('rc.relatedcompetencyid', "'_'", 'rc.competencyid') . " AS rid
FROM {" . self::TABLE . "} rc
JOIN {" . competency::TABLE . "} c
ON c.id = rc.relatedcompetencyid
WHERE rc.competencyid = :cid)
UNION ALL
(SELECT $fields, " . $DB->sql_concat('rc.competencyid', "'_'", 'rc.relatedcompetencyid') . " AS rid
FROM {" . self::TABLE . "} rc
JOIN {" . competency::TABLE . "} c
ON c.id = rc.competencyid
WHERE rc.relatedcompetencyid = :cid2)
ORDER BY c_path ASC, c_sortorder ASC";
$competencies = array();
$records = $DB->get_recordset_sql($sql, array('cid' => $competencyid, 'cid2' => $competencyid));
foreach ($records as $record) {
unset($record->rid);
$competencies[$record->c_id] = new competency(null, competency::extract_record($record, 'c_'));
}
$records->close();
return $competencies;
}
/**
* Get the related competencies from competency ids.
*
* @param int[] $competencyids Array of competency ids.
* @return related_competency[]
*/
public static function get_multiple_relations($competencyids) {
global $DB;
if (empty($competencyids)) {
return array();
}
list($insql, $params) = $DB->get_in_or_equal($competencyids);
$records = $DB->get_records_select(self::TABLE,
"competencyid $insql OR relatedcompetencyid $insql",
array_merge($params, $params)
);
$relatedcompetencies = array();
foreach ($records as $record) {
unset($record->id);
$relatedcompetencies[] = new related_competency(null, $record);
}
return $relatedcompetencies;
}
/**
* Delete relations using competencies.
*
* @param array $competencyids Array of competencies ids.
* @return bool True if relations were deleted successfully.
*/
public static function delete_multiple_relations($competencyids) {
global $DB;
if (empty($competencyids)) {
return true;
}
list($insql, $params) = $DB->get_in_or_equal($competencyids);
return $DB->delete_records_select(self::TABLE,
"competencyid $insql OR relatedcompetencyid $insql",
array_merge($params, $params)
);
}
}
+205
View File
@@ -0,0 +1,205 @@
<?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 loading/storing learning plan templates from the DB.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use context;
use lang_string;
use stdClass;
/**
* Class for loading/storing learning plan templates from the DB.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template extends persistent {
const TABLE = 'competency_template';
/** @var template object before update. */
protected $beforeupdate = null;
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'shortname' => array(
'type' => PARAM_TEXT,
),
'description' => array(
'default' => '',
'type' => PARAM_CLEANHTML,
),
'descriptionformat' => array(
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
'type' => PARAM_INT,
'default' => FORMAT_HTML
),
'duedate' => array(
'default' => 0,
'type' => PARAM_INT,
),
'visible' => array(
'default' => 1,
'type' => PARAM_BOOL,
),
'contextid' => array(
'type' => PARAM_INT
),
);
}
/**
* Hook to execute after an update.
*
* @param bool $result Whether or not the update was successful.
* @return void
*/
protected function after_update($result) {
$this->beforeupdate = null;
}
/**
* Hook to execute before validate.
*
* @return void
*/
protected function before_validate() {
$this->beforeupdate = null;
// During update.
if ($this->get('id')) {
$this->beforeupdate = new self($this->get('id'));
}
}
/**
* Whether or not the current user can read the template.
*
* @return bool
*/
public function can_manage() {
return self::can_manage_context($this->get_context());
}
/**
* Whether or not the current user can manage the template.
*
* @param context $context
* @return bool
*/
public static function can_manage_context($context) {
return has_capability('moodle/competency:templatemanage', $context);
}
/**
* Whether or not the current user can read the template.
*
* @return bool
*/
public function can_read() {
return self::can_read_context($this->get_context());
}
/**
* Whether or not the current user can read the template.
*
* @param context $context
* @return bool
*/
public static function can_read_context($context) {
return has_capability('moodle/competency:templateview', $context) || self::can_manage_context($context);
}
/**
* Get the context.
*
* @return context The context
*/
public function get_context() {
return context::instance_by_id($this->get('contextid'));
}
/**
* Validate the context ID.
*
* @param int $value The context ID.
* @return bool|lang_string
*/
protected function validate_contextid($value) {
$context = context::instance_by_id($value, IGNORE_MISSING);
if (!$context) {
return new lang_string('invalidcontext', 'error');
} else if ($context->contextlevel != CONTEXT_SYSTEM && $context->contextlevel != CONTEXT_COURSECAT) {
return new lang_string('invalidcontext', 'error');
}
return true;
}
/**
* Validate the due date.
*
* The due date can always be changed, but when it is it must be:
* - unset
* - set in the future.
*
* @param int $value The due date.
* @return bool|lang_string
*/
protected function validate_duedate($value) {
// During update.
if ($this->get('id')) {
$before = $this->beforeupdate->get('duedate');
// The value has not changed, then it's always OK.
if ($before == $value) {
return true;
}
}
// During create and update, the date must be set in the future, or not set.
if (!empty($value) && $value <= time() - 600) {
// We cannot set the date in the past. But we allow for 10 minutes of margin so that
// a user can set the due date to "now" without risking to hit a validation error.
return new lang_string('errorcannotsetduedateinthepast', 'core_competency');
}
return true;
}
/**
* Returns true when the template has user learning plans.
*
* @return boolean
*/
public function has_plans() {
return plan::has_records_for_template($this->get('id'));
}
}
+234
View File
@@ -0,0 +1,234 @@
<?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/>.
/**
* Template cohort persistent.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use lang_string;
use core_competency\template;
/**
* Template cohort persistent.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_cohort extends persistent {
const TABLE = 'competency_templatecohort';
/** 2 hours of threshold to prevent expired plans **/
const DUEDATE_THRESHOLD = 7200;
/**
* Return the custom definition of the properties of this model.
*
* @return array Where keys are the property names.
*/
protected static function define_properties() {
return array(
'templateid' => array(
'type' => PARAM_INT
),
'cohortid' => array(
'type' => PARAM_INT
)
);
}
/**
* Validate the cohort ID.
*
* @param int $value The cohort ID.
* @return true|lang_string
*/
protected function validate_cohortid($value) {
global $DB;
if (!$DB->record_exists('cohort', array('id' => $value))) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate the template ID.
*
* @param int $value The template ID.
* @return true|lang_string
*/
protected function validate_templateid($value) {
if (!template::record_exists($value)) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Return an array of user IDs for which the plans are missing.
*
* Plans are considered as missing when a member of a cohort does not have a plan created.
* When the parameter $unlinkedaremissing is set to false, plans that were unlinked from
* their template will be ignored so that we do not recreate unlinked plans endlessly.
*
* This method ignores the due date of the template.
*
* @param int $templateid The template ID.
* @param int $cohortid The cohort ID.
* @param boolean $unlinkedaremissing When true, unlinked plans are considered as missing.
* @return int[] User IDs.
*/
public static function get_missing_plans($templateid, $cohortid, $unlinkedaremissing = false) {
global $DB;
$skipsql = '';
$skipparams = array();
if (!$unlinkedaremissing) {
$skipsql = 'OR p.origtemplateid = :origtemplateid';
$skipparams = array('origtemplateid' => $templateid);
}
$sql = "SELECT cm.userid
FROM {cohort_members} cm
LEFT JOIN {" . plan::TABLE . "} p
ON p.userid = cm.userid
AND (p.templateid = :templateid
$skipsql)
WHERE cm.cohortid = :cohortid
AND p.id IS NULL";
$params = array('templateid' => $templateid, 'cohortid' => $cohortid) + $skipparams;
return $DB->get_fieldset_sql($sql, $params);
}
/**
* Get a relation.
*
* This does not perform any validation on the data passed. If the relation exists in the database
* then it is loaded in a the model, if not then it is up to the developer to save the model.
*
* @param int $templateid
* @param int $cohortid
* @return template_cohort
*/
public static function get_relation($templateid, $cohortid) {
global $DB;
$params = array(
'templateid' => $templateid,
'cohortid' => $cohortid
);
$relation = new static(null, (object) $params);
if ($record = $DB->get_record(self::TABLE, $params)) {
$relation->from_record($record);
}
return $relation;
}
/**
* Get a relations by templateid.
*
* This does not perform any validation on the data passed. If the relation exists in the database
* then it is loaded in a the model, if not then it is up to the developer to save the model.
*
* @param int $templateid
* @return template_cohort[] array of template cohort
*/
public static function get_relations_by_templateid($templateid) {
global $DB;
$params = array(
'templateid' => $templateid
);
$relations = array();
$records = $DB->get_records(self::TABLE, $params);
foreach ($records as $record) {
$relations[] = new template_cohort(0, $record);
}
return $relations;
}
/**
* Return an array of templates persistent with their missing userids.
*
* Note that only cohorts associated with visible templates are considered,
* as well as only templates with a due date in the future, or no due date.
*
* @param int $lastruntime The last time the Cohort ssync task ran.
* @param bool $unlinkedaremissing When true, unlinked plans are considered as missing.
* @return array( array(
* 'template' => \core_competency\template,
* 'userids' => array
* ))
*/
public static function get_all_missing_plans($lastruntime = 0, $unlinkedaremissing = false) {
global $DB;
$planwhereclause = " WHERE (p.id is NULL
AND (cm.timeadded >= :lastruntime1
OR tc.timecreated >= :lastruntime3
OR t.timemodified >= :lastruntime4))";
if ($unlinkedaremissing) {
$planwhereclause .= " OR (p.origtemplateid IS NOT NULL AND cm.timeadded < :lastruntime2)";
}
$sql = "SELECT " . $DB->sql_concat('cm.userid', 'tc.templateid') . " as uniqueid, cm.userid, t.*
FROM {cohort_members} cm
JOIN {" . self::TABLE . "} tc ON cm.cohortid = tc.cohortid
JOIN {" . template::TABLE . "} t
ON (tc.templateid = t.id AND t.visible = 1)
AND (t.duedate = 0 OR t.duedate > :time1)
LEFT JOIN {" . plan::TABLE . "} p ON (cm.userid = p.userid AND (t.id = p.templateid OR t.id = p.origtemplateid))
$planwhereclause
ORDER BY t.id";
$params = array('time1' => time(), 'time2' => time(),
'lastruntime1' => $lastruntime, 'lastruntime2' => $lastruntime,
'lastruntime3' => $lastruntime, 'lastruntime4' => $lastruntime);
$results = $DB->get_records_sql($sql, $params);
$missingplans = array();
foreach ($results as $usertemplate) {
$userid = $usertemplate->userid;
// Check if template already exist in the array.
if (isset($missingplans[$usertemplate->id])) {
$missingplans[$usertemplate->id]['userids'][] = $userid;
} else {
unset($usertemplate->userid);
unset($usertemplate->uniqueid);
$template = new template(0, $usertemplate);
$missingplans[$template->get('id')]['template'] = $template;
$missingplans[$template->get('id')]['userids'][] = $userid;
}
}
return array_values($missingplans);
}
}
+297
View File
@@ -0,0 +1,297 @@
<?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 loading/storing competencies from the DB.
*
* @package core_competency
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use stdClass;
/**
* Class for loading/storing template_competencies from the DB.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_competency extends persistent {
const TABLE = 'competency_templatecomp';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'templateid' => array(
'type' => PARAM_INT,
'default' => 0,
),
'competencyid' => array(
'type' => PARAM_INT,
'default' => 0,
),
'sortorder' => array(
'type' => PARAM_INT,
'default' => 0,
),
);
}
/**
* Count the templates using a competency.
*
* @param int $competencyid The competency id
* @param bool $onlyvisible If true, only count visible templates using this competency.
* @return int
*/
public static function count_templates($competencyid, $onlyvisible) {
global $DB;
$sql = 'SELECT COUNT(tpl.id)
FROM {' . self::TABLE . '} tplcomp
JOIN {' . template::TABLE . '} tpl
ON tplcomp.templateid = tpl.id
WHERE tplcomp.competencyid = ? ';
$params = array($competencyid);
if ($onlyvisible) {
$sql .= ' AND tpl.visible = ?';
$params[] = 1;
}
$results = $DB->count_records_sql($sql, $params);
return $results;
}
/**
* List the templates using a competency.
*
* @param int $competencyid The competency id
* @param bool $onlyvisible If true, only count visible templates using this competency.
* @return array[competency]
*/
public static function list_templates($competencyid, $onlyvisible) {
global $DB;
$sql = 'SELECT tpl.*
FROM {' . template::TABLE . '} tpl
JOIN {' . self::TABLE . '} tplcomp
ON tplcomp.templateid = tpl.id
WHERE tplcomp.competencyid = ? ';
$params = array($competencyid);
if ($onlyvisible) {
$sql .= ' AND tpl.visible = ?';
$params[] = 1;
}
$sql .= ' ORDER BY tpl.id ASC';
$results = $DB->get_records_sql($sql, $params);
$instances = array();
foreach ($results as $result) {
array_push($instances, new template(0, $result));
}
return $instances;
}
/**
* Count the competencies in a template.
*
* @param int $templateid The template id
* @return int
*/
public static function count_competencies($templateid) {
global $DB;
$sql = 'SELECT COUNT(comp.id)
FROM {' . self::TABLE . '} tplcomp
JOIN {' . competency::TABLE . '} comp
ON tplcomp.competencyid = comp.id
WHERE tplcomp.templateid = ? ';
$params = array($templateid);
$results = $DB->count_records_sql($sql, $params);
return $results;
}
/**
* Count the competencies in a template with no links to courses.
*
* @param int $templateid The template id
* @return int
*/
public static function count_competencies_with_no_courses($templateid) {
global $DB;
$sql = 'SELECT COUNT(comp.id)
FROM {' . self::TABLE . '} tplcomp
JOIN {' . competency::TABLE . '} comp
ON tplcomp.competencyid = comp.id
LEFT JOIN {' . course_competency::TABLE . '} crscomp
ON crscomp.competencyid = comp.id
WHERE tplcomp.templateid = ? AND crscomp.id IS NULL';
$params = array($templateid);
$results = $DB->count_records_sql($sql, $params);
return $results;
}
/**
* Get a single competency from the template (only if it is really in the template).
*
* @param int $templateid The template id
* @param int $competencyid The competency id
* @return competency
*/
public static function get_competency($templateid, $competencyid) {
global $DB;
$sql = 'SELECT comp.*
FROM {' . competency::TABLE . '} comp
JOIN {' . self::TABLE . '} tplcomp
ON tplcomp.competencyid = comp.id
WHERE tplcomp.templateid = ? AND tplcomp.competencyid = ?';
$params = array($templateid, $competencyid);
$result = $DB->get_record_sql($sql, $params);
if (!$result) {
throw new \coding_exception('The competency does not belong to this template: ' . $competencyid . ', ' . $templateid);
}
return new competency(0, $result);
}
/**
* List the competencies in this template.
*
* @param int $templateid The template id
* @return array[competency]
*/
public static function list_competencies($templateid) {
global $DB;
$sql = 'SELECT comp.*
FROM {' . competency::TABLE . '} comp
JOIN {' . self::TABLE . '} tplcomp
ON tplcomp.competencyid = comp.id
WHERE tplcomp.templateid = ?
ORDER BY tplcomp.sortorder ASC,
tplcomp.id ASC';
$params = array($templateid);
$results = $DB->get_records_sql($sql, $params);
$instances = array();
foreach ($results as $result) {
array_push($instances, new competency(0, $result));
}
return $instances;
}
/**
* Remove the competencies in this template.
*
* @param int $templateid The template id
* @return boolen
*/
public static function delete_by_templateid($templateid) {
global $DB;
return $DB->delete_records(self::TABLE, array('templateid' => $templateid));
}
/**
* Hook to execute before validate.
*
* @return void
*/
protected function before_validate() {
if (($this->get('id') && $this->get('sortorder') === null) || !$this->get('id')) {
$this->set('sortorder', $this->count_records(array('templateid' => $this->get('templateid'))));
}
}
/**
* Validate competencyid.
*
* @param int $value ID.
* @return true|lang_string
*/
protected function validate_competencyid($value) {
if (!competency::record_exists($value)) {
return new \lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate templateid.
*
* @param int $value ID.
* @return true|lang_string
*/
protected function validate_templateid($value) {
if (!template::record_exists($value)) {
return new \lang_string('invaliddata', 'error');
}
return true;
}
/**
* Hook to execute after delete.
*
* @param bool $result Whether or not the delete was successful.
* @return void
*/
protected function after_delete($result) {
global $DB;
if (!$result) {
return;
}
$table = '{' . self::TABLE . '}';
$sql = "UPDATE $table SET sortorder = sortorder -1 WHERE templateid = ? AND sortorder > ?";
$DB->execute($sql, array($this->get('templateid'), $this->get('sortorder')));
}
/**
* Check if template competency has records for competencies.
*
* @param array $competencyids Array of competencies ids.
* @return boolean Return true if competencies were found in template_competency.
*/
public static function has_records_for_competencies($competencyids) {
global $DB;
list($insql, $params) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED);
return self::record_exists_select("competencyid $insql", $params);
}
}
+191
View File
@@ -0,0 +1,191 @@
<?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/>.
/**
* URL manager.
*
* @package core_competency
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use moodle_url;
/**
* URL manager class.
*
* This class has to be used to get the URL to a resource, this allows for different
* alternate frontends to be used without resorting to core hacks. Note that you
* do not have to use this when you are navigating between pages of your own plugin.
*
* To set another resolver, set the following config value in config.php:
* $CFG->core_competency_url_resolver = 'your_plugin\\your_url_resolver_class';
*
* Your URL resolver should implement the same methods as the ones listed in
* this class (except for {{@link self::get()}}) but not statically.
*
* /!\ Note, resolvers MUST NEVER assume that the resource, or the resources
* represented by the arguments, still exist.
*
* @package core_competency
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class url {
/** @var url_resolver The URL resolver instance.*/
protected static $resolver;
/**
* Defer to the resolver.
*
* @param string $resource The resource type.
* @param array $args The arguments.
* @return mixed
*/
protected static function get($resource, $args) {
global $CFG;
if (!isset(static::$resolver)) {
$klass = !empty($CFG->core_competency_url_resolver) ? $CFG->core_competency_url_resolver : 'tool_lp\\url_resolver';
static::$resolver = new $klass();
}
if (!method_exists(static::$resolver, $resource)) {
debugging("URL for '$resource' not implemented.", DEBUG_DEVELOPER);
return new moodle_url('/');
}
return call_user_func_array([static::$resolver, $resource], $args);
}
/**
* The URL where the competency can be found.
*
* @param int $competencyid The competency ID.
* @param int $pagecontextid The ID of the context we are in.
* @return moodle_url
*/
public static function competency($competencyid, $pagecontextid) {
return static::get(__FUNCTION__, func_get_args());
}
/**
* The URL where the framework can be found.
*
* @param int $frameworkid The framework ID.
* @param int $pagecontextid The ID of the context we are in.
* @return moodle_url
*/
public static function framework($frameworkid, $pagecontextid) {
return static::get(__FUNCTION__, func_get_args());
}
/**
* The URL where the frameworks can be found.
*
* @param int $pagecontextid The ID of the context that we are browsing.
* @return moodle_url
*/
public static function frameworks($pagecontextid) {
return static::get(__FUNCTION__, func_get_args());
}
/**
* The URL where the plan can be found.
*
* @param int $planid The plan ID.
* @return moodle_url
*/
public static function plan($planid) {
return static::get(__FUNCTION__, func_get_args());
}
/**
* The URL where the plans of a user can be found.
*
* @param int $userid The user ID.
* @return moodle_url
*/
public static function plans($userid) {
return static::get(__FUNCTION__, func_get_args());
}
/**
* The URL where the template can be found.
*
* @param int $templateid The template ID.
* @param int $pagecontextid The ID of the context we are in.
* @return moodle_url
*/
public static function template($templateid, $pagecontextid) {
return static::get(__FUNCTION__, func_get_args());
}
/**
* The URL where the templates can be found.
*
* @param int $pagecontextid The ID of the context that we are browsing.
* @return moodle_url
*/
public function templates($pagecontextid) {
return static::get(__FUNCTION__, func_get_args());
}
/**
* The URL where the user competency can be found.
*
* @param int $usercompetencyid The user competency ID
* @return moodle_url
*/
public static function user_competency($usercompetencyid) {
return static::get(__FUNCTION__, func_get_args());
}
/**
* The URL where the user competency can be found in the context of a course.
*
* @param int $userid The user ID
* @param int $competencyid The competency ID.
* @param int $courseid The course ID.
* @return moodle_url
*/
public static function user_competency_in_course($userid, $competencyid, $courseid) {
return static::get(__FUNCTION__, func_get_args());
}
/**
* The URL where the user competency can be found in the context of a plan.
*
* @param int $userid The user ID
* @param int $competencyid The competency ID.
* @param int $planid The plan ID.
* @return moodle_url
*/
public static function user_competency_in_plan($userid, $competencyid, $planid) {
return static::get(__FUNCTION__, func_get_args());
}
/**
* The URL where the user evidence (of prior learning) can be found.
*
* @param int $userevidenceid The user evidence ID
* @return moodle_url
*/
public static function user_evidence($userevidenceid) {
return static::get(__FUNCTION__, func_get_args());
}
}
+556
View File
@@ -0,0 +1,556 @@
<?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 user_competency persistence.
*
* @package core_competency
* @copyright 2015 Serge Gauthier
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use coding_exception;
use context_course;
use context_user;
use comment;
use lang_string;
/**
* Class for loading/storing user_competency from the DB.
*
* @copyright 2015 Serge Gauthier
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_competency extends persistent {
/** Table name for user_competency persistency */
const TABLE = 'competency_usercomp';
/** Idle status */
const STATUS_IDLE = 0;
/** Waiting for review status */
const STATUS_WAITING_FOR_REVIEW = 1;
/** In review status */
const STATUS_IN_REVIEW = 2;
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'userid' => array(
'type' => PARAM_INT,
),
'competencyid' => array(
'type' => PARAM_INT,
),
'status' => array(
'choices' => array(
self::STATUS_IDLE,
self::STATUS_WAITING_FOR_REVIEW,
self::STATUS_IN_REVIEW,
),
'type' => PARAM_INT,
'default' => self::STATUS_IDLE,
),
'reviewerid' => array(
'type' => PARAM_INT,
'default' => null,
'null' => NULL_ALLOWED,
),
'proficiency' => array(
'type' => PARAM_BOOL,
'default' => null,
'null' => NULL_ALLOWED,
),
'grade' => array(
'type' => PARAM_INT,
'default' => null,
'null' => NULL_ALLOWED,
),
);
}
/**
* Whether the current user can comment on this user competency.
*
* @return bool
*/
public function can_comment() {
return static::can_comment_user($this->get('userid'));
}
/**
* Whether the current user can read this user competency.
*
* @return bool
*/
public function can_read() {
return static::can_read_user($this->get('userid'));
}
/**
* Whether the current user can read comments on this user competency.
*
* @return bool
*/
public function can_read_comments() {
return static::can_read_comments_user($this->get('userid'));
}
/**
* Can the current user send the user competency for review?
*
* @return bool
*/
public function can_request_review() {
return static::can_request_review_user($this->get('userid'));
}
/**
* Can the current user review the user competency?
*
* @return bool
*/
public function can_review() {
return static::can_review_user($this->get('userid'));
}
/**
* Human readable status name.
*
* @param int $status The status code.
* @return lang_string
*/
public static function get_status_name($status) {
switch ($status) {
case self::STATUS_IDLE:
$strname = 'idle';
break;
case self::STATUS_WAITING_FOR_REVIEW:
$strname = 'waitingforreview';
break;
case self::STATUS_IN_REVIEW:
$strname = 'inreview';
break;
default:
throw new \moodle_exception('errorusercomptencystatus', 'core_competency', '', $status);
break;
}
return new lang_string('usercompetencystatus_' . $strname, 'core_competency');
}
/**
* Get list of competency status.
*
* @return array
*/
public static function get_status_list() {
static $list = null;
if ($list === null) {
$list = array(
self::STATUS_IDLE => self::get_status_name(self::STATUS_IDLE),
self::STATUS_WAITING_FOR_REVIEW => self::get_status_name(self::STATUS_WAITING_FOR_REVIEW),
self::STATUS_IN_REVIEW => self::get_status_name(self::STATUS_IN_REVIEW));
}
return $list;
}
/**
* Get the comment object.
*
* @return comment
*/
public function get_comment_object() {
global $CFG;
require_once($CFG->dirroot . '/comment/lib.php');
if (!$this->get('id')) {
throw new coding_exception('The user competency record must exist.');
}
$comment = new comment((object) array(
'context' => $this->get_context(),
'component' => 'competency', // This cannot be named 'core_competency'.
'itemid' => $this->get('id'),
'area' => 'user_competency',
'showcount' => true,
));
$comment->set_fullwidth(true);
return $comment;
}
/**
* Return the competency Object.
*
* @return competency Competency Object
*/
public function get_competency() {
return new competency($this->get('competencyid'));
}
/**
* Get the context.
*
* @return \context The context.
*/
public function get_context() {
return context_user::instance($this->get('userid'));
}
/**
* Find the plans for the user and this competency.
*
* Note that this:
* - does not perform any capability check.
* - may return completed plans.
* - may return an empty array.
*
* @return plans[]
*/
public function get_plans() {
return plan::get_by_user_and_competency($this->get('userid'), $this->get('competencyid'));
}
/**
* 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 competency ID.
*
* @param int $value The value.
* @return true|lang_string
*/
protected function validate_competencyid($value) {
if (!competency::record_exists($value)) {
return new lang_string('errornocompetency', 'core_competency', $value);
}
return true;
}
/**
* Validate the proficiency.
*
* @param int $value The value.
* @return true|lang_string
*/
protected function validate_proficiency($value) {
$grade = $this->get('grade');
if ($grade !== null && $value === null) {
// We must set a proficiency when we set a grade.
return new lang_string('invaliddata', 'error');
} else if ($grade === null && $value !== null) {
// We must not set a proficiency when we don't set a grade.
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate the reviewer ID.
*
* @param int $value The value.
* @return true|lang_string
*/
protected function validate_reviewerid($value) {
global $DB;
if ($value !== null && !$DB->record_exists('user', array('id' => $value))) {
return new lang_string('invaliduserid', 'error');
}
return true;
}
/**
* Validate the grade.
*
* @param int $value The value.
* @return true|lang_string
*/
protected function validate_grade($value) {
if ($value !== null) {
if ($value <= 0) {
return new lang_string('invalidgrade', 'core_competency');
}
// TODO MDL-52243 Use a core method to validate the grade_scale item.
// Check if grade exist in the scale item values.
$competency = $this->get_competency();
if (!array_key_exists($value - 1 , $competency->get_scale()->scale_items)) {
return new lang_string('invalidgrade', 'core_competency');
}
}
return true;
}
/**
* Can the current user comment on a user's competency?
*
* @param int $userid The user ID the competency belongs to.
* @return bool
*/
public static function can_comment_user($userid) {
global $USER;
$capabilities = array('moodle/competency:usercompetencycomment');
if ($USER->id == $userid) {
$capabilities[] = 'moodle/competency:usercompetencycommentown';
}
if (has_any_capability($capabilities, context_user::instance($userid))) {
return true;
}
return false;
}
/**
* Can the current user grade a user's user competency?
*
* @param int $userid The user ID the competency belongs to.
* @return bool
*/
public static function can_grade_user($userid) {
$ratecap = 'moodle/competency:competencygrade';
return has_capability($ratecap, context_user::instance($userid));
}
/**
* Can the current user grade a user's user competency in a course?
*
* @param int $userid The user ID the competency belongs to.
* @param int $courseid The course ID.
* @return bool
*/
public static function can_grade_user_in_course($userid, $courseid) {
$ratecap = 'moodle/competency:competencygrade';
return has_capability($ratecap, context_course::instance($courseid))
|| static::can_grade_user($userid);
}
/**
* Can the current user read the comments on a user's competency?
*
* @param int $userid The user ID the competency belongs to.
* @return bool
*/
public static function can_read_comments_user($userid) {
// Everyone who can read the user competency can read the comments.
return static::can_read_user($userid);
}
/**
* Can the current user read the user competencies of a user in a course?
*
* @param int $userid The user ID the competency belongs to.
* @param int $courseid The course ID.
* @return bool
*/
public static function can_read_user_in_course($userid, $courseid) {
$capability = 'moodle/competency:usercompetencyview';
return has_capability($capability, context_course::instance($courseid))
|| static::can_read_user($userid);
}
/**
* Can the current user read a user's competency?
*
* @param int $userid The user ID the competency belongs to.
* @return bool
*/
public static function can_read_user($userid) {
$capability = 'moodle/competency:usercompetencyview';
return has_capability($capability, context_user::instance($userid))
|| plan::can_read_user($userid);
}
/**
* Can the current user send a user's competency for review?
*
* Note that the status 'review' is not meant to be used for student to self-assess
* themselves, then to ask the teacher to review their assessment. It is more intended
* for a student to provide evidence of prior learning and request their review.
*
* @param int $userid The user ID the competency belongs to.
* @return bool
*/
public static function can_request_review_user($userid) {
global $USER;
$capabilities = array('moodle/competency:usercompetencyrequestreview');
if ($USER->id == $userid) {
$capabilities[] = 'moodle/competency:usercompetencyrequestreviewown';
}
if (has_any_capability($capabilities, context_user::instance($userid))) {
return true;
}
return false;
}
/**
* Can the current user review the user competency?
*
* @param int $userid The user ID the competency belongs to.
* @return bool
*/
public static function can_review_user($userid) {
$capability = 'moodle/competency:usercompetencyreview';
return has_capability($capability, context_user::instance($userid));
}
/**
* Create a new user_competency object.
*
* Note, this is intended to be used to create a blank relation, for instance when
* the record was not found in the database. This does not save the model.
*
* @param int $userid The user ID.
* @param int $competencyid The competency ID.
* @return \core_competency\user_competency
*/
public static function create_relation($userid, $competencyid) {
$relation = new user_competency(0, (object) array('userid' => $userid, 'competencyid' => $competencyid));
return $relation;
}
/**
* Fetch a competency by user competency ID.
*
* This is a convenience method to attempt to efficiently fetch a competency when
* the only information we have is the user_competency ID, in evidence for instance.
*
* @param int $id The user competency ID.
* @return competency
*/
public static function get_competency_by_usercompetencyid($id) {
global $DB;
$sql = "SELECT c.*
FROM {" . self::TABLE . "} uc
JOIN {" . competency::TABLE . "} c
ON c.id = uc.competencyid
WHERE uc.id = ?";
$record = $DB->get_record_sql($sql, array($id), MUST_EXIST);
return new competency(0, $record);
}
/**
* Get multiple user_competency for a user.
*
* @param int $userid
* @param array $competenciesorids Limit search to those competencies, or competency IDs.
* @return \core_competency\user_competency[]
*/
public static function get_multiple($userid, array $competenciesorids = null) {
global $DB;
$params = array();
$params['userid'] = $userid;
$sql = '1 = 1';
if (!empty($competenciesorids)) {
$test = reset($competenciesorids);
if (is_number($test)) {
$ids = $competenciesorids;
} else {
$ids = array();
foreach ($competenciesorids as $comp) {
$ids[] = $comp->get('id');
}
}
list($insql, $inparams) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
$params += $inparams;
$sql = "competencyid $insql";
}
// Order by ID to prevent random ordering.
return self::get_records_select("userid = :userid AND $sql", $params, 'id ASC');
}
/**
* Checks if a competency has user competency records.
*
* @param int $competencyid The competency ID
* @return boolean
*/
public static function has_records_for_competency($competencyid) {
return self::record_exists_select('competencyid = ?', array($competencyid));
}
/**
* Checks if any of the competencies of a framework has a user competency record.
*
* @param int $frameworkid The competency framework ID.
* @return boolean
*/
public static function has_records_for_framework($frameworkid) {
global $DB;
$sql = "SELECT 'x'
FROM {" . self::TABLE . "} uc
JOIN {" . competency::TABLE . "} c
ON uc.competencyid = c.id
WHERE c.competencyframeworkid = ?";
$params = array($frameworkid);
return $DB->record_exists_sql($sql, $params);
}
/**
* Check if user competency has records for competencies.
*
* @param array $competencyids The competencies ids.
* @return boolean Return true if the delete was successfull.
*/
public static function has_records_for_competencies($competencyids) {
global $DB;
list($insql, $params) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED);
return self::record_exists_select("competencyid $insql", $params);
}
}
@@ -0,0 +1,293 @@
<?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 user_competency_course persistence.
*
* @package core_competency
* @copyright 2016 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use context_course;
use context_user;
use lang_string;
/**
* Class for loading/storing user_competency_course from the DB.
*
* @copyright 2016 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_competency_course extends persistent {
/** Table name for user_competency persistency */
const TABLE = 'competency_usercompcourse';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'userid' => array(
'type' => PARAM_INT,
),
'courseid' => array(
'type' => PARAM_INT
),
'competencyid' => array(
'type' => PARAM_INT,
),
'proficiency' => array(
'type' => PARAM_BOOL,
'default' => null,
'null' => NULL_ALLOWED,
),
'grade' => array(
'type' => PARAM_INT,
'default' => null,
'null' => NULL_ALLOWED,
),
);
}
/**
* Return the competency Object.
*
* @return competency Competency Object
*/
public function get_competency() {
return new competency($this->get('competencyid'));
}
/**
* Get the context.
*
* @return context The context.
*/
public function get_context() {
return context_user::instance($this->get('userid'));
}
/**
* Create a new user_competency_course object.
*
* Note, this is intended to be used to create a blank relation, for instance when
* the record was not found in the database. This does not save the model.
*
* @param int $userid The user ID.
* @param int $competencyid The competency ID.
* @param int $courseid The course ID.
* @return \core_competency\user_competency_course
*/
public static function create_relation($userid, $competencyid, $courseid) {
$data = new \stdClass();
$data->userid = $userid;
$data->competencyid = $competencyid;
$data->courseid = $courseid;
$relation = new user_competency_course(0, $data);
return $relation;
}
/**
* 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 competency ID.
*
* @param int $value The value.
* @return true|lang_string
*/
protected function validate_competencyid($value) {
if (!competency::record_exists($value)) {
return new lang_string('errornocompetency', 'core_competency', $value);
}
return true;
}
/**
* Validate course ID.
*
* @param int $value The course ID.
* @return true|lang_string
*/
protected function validate_courseid($value) {
if (!context_course::instance($value, IGNORE_MISSING)) {
return new lang_string('errorinvalidcourse', 'core_competency', $value);
}
return true;
}
/**
* Validate the proficiency.
*
* @param int $value The value.
* @return true|lang_string
*/
protected function validate_proficiency($value) {
$grade = $this->get('grade');
if ($grade !== null && $value === null) {
// We must set a proficiency when we set a grade.
return new lang_string('invaliddata', 'error');
} else if ($grade === null && $value !== null) {
// We must not set a proficiency when we don't set a grade.
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate the grade.
*
* @param int $value The value.
* @return true|lang_string
*/
protected function validate_grade($value) {
if ($value !== null) {
if ($value <= 0) {
return new lang_string('invalidgrade', 'core_competency');
}
// TODO MDL-52243 Use a core method to validate the grade_scale item.
// Check if grade exist in the scale item values.
$competency = $this->get_competency();
if (!array_key_exists($value - 1 , $competency->get_scale()->scale_items)) {
return new lang_string('invalidgrade', 'core_competency');
}
}
return true;
}
/**
* Get multiple user_competency_course for a user.
*
* @param int $userid
* @param int $courseid
* @param array $competenciesorids Limit search to those competencies, or competency IDs.
* @return \core_competency\user_competency_course[]
*/
public static function get_multiple($userid, $courseid, array $competenciesorids = null) {
global $DB;
$params = array();
$params['userid'] = $userid;
$params['courseid'] = $courseid;
$sql = '1 = 1';
if (!empty($competenciesorids)) {
$test = reset($competenciesorids);
if (is_number($test)) {
$ids = $competenciesorids;
} else {
$ids = array();
foreach ($competenciesorids as $comp) {
$ids[] = $comp->get('id');
}
}
list($insql, $inparams) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
$params += $inparams;
$sql = "competencyid $insql";
}
// Order by ID to prevent random ordering.
return self::get_records_select("userid = :userid AND courseid = :courseid AND $sql", $params, 'id ASC');
}
/**
* Count the proficient competencies in this course for one user.
*
* @param int $courseid The course id
* @param int $userid The user id
* @return int
*/
public static function count_proficient_competencies($courseid, $userid) {
global $DB;
$sql = 'SELECT COUNT(comp.id)
FROM {' . self::TABLE . '} usercoursecomp
JOIN {' . course_competency::TABLE . '} cc
ON usercoursecomp.competencyid = cc.competencyid AND cc.courseid = usercoursecomp.courseid
JOIN {' . competency::TABLE . '} comp
ON usercoursecomp.competencyid = comp.id
WHERE usercoursecomp.courseid = ? AND usercoursecomp.userid = ? AND usercoursecomp.proficiency = ?';
$params = array($courseid, $userid, true);
$results = $DB->count_records_sql($sql, $params);
return $results;
}
/**
* Get the list of competencies that were completed the least times in a course.
*
* @param int $courseid
* @param int $skip The number of competencies to skip
* @param int $limit The max number of competencies to return
* @return competency[]
*/
public static function get_least_proficient_competencies_for_course($courseid, $skip = 0, $limit = 0) {
global $DB;
$fields = competency::get_sql_fields('c', 'c_');
$params = array('courseid' => $courseid);
$sql = 'SELECT ' . $fields . '
FROM (SELECT cc.competencyid, SUM(COALESCE(ucc.proficiency, 0)) AS timesproficient
FROM {' . course_competency::TABLE . '} cc
LEFT JOIN {' . self::TABLE . '} ucc
ON ucc.competencyid = cc.competencyid
AND ucc.courseid = cc.courseid
WHERE cc.courseid = :courseid
GROUP BY cc.competencyid
) p
JOIN {' . competency::TABLE . '} c
ON c.id = p.competencyid
ORDER BY p.timesproficient ASC, c.id DESC';
$results = $DB->get_records_sql($sql, $params, $skip, $limit);
$comps = array();
foreach ($results as $r) {
$c = competency::extract_record($r, 'c_');
$comps[] = new competency(0, $c);
}
return $comps;
}
}
+369
View File
@@ -0,0 +1,369 @@
<?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 user_competency_plan persistence.
*
* @package core_competency
* @copyright 2015 Serge Gauthier
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use lang_string;
use context_user;
/**
* Class for loading/storing user_competency_plan from the DB.
*
* @copyright 2015 Serge Gauthier
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_competency_plan extends persistent {
/** Table name for user_competency_plan persistency */
const TABLE = 'competency_usercompplan';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'userid' => array(
'type' => PARAM_INT,
),
'competencyid' => array(
'type' => PARAM_INT,
),
'proficiency' => array(
'type' => PARAM_BOOL,
'default' => null,
'null' => NULL_ALLOWED,
),
'grade' => array(
'type' => PARAM_INT,
'default' => null,
'null' => NULL_ALLOWED,
),
'planid' => array(
'type' => PARAM_INT,
),
'sortorder' => array(
'type' => PARAM_INT,
'default' => 0,
),
);
}
/**
* Return the competency Object.
*
* @return competency Competency Object
*/
public function get_competency() {
return new competency($this->get('competencyid'));
}
/**
* Get the context.
*
* @return context The context.
*/
public function get_context() {
return context_user::instance($this->get('userid'));
}
/**
* 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 competency ID.
*
* @param int $value The value.
* @return true|lang_string
*/
protected function validate_competencyid($value) {
if (!competency::record_exists($value)) {
return new lang_string('errornocompetency', 'core_competency', $value);
}
return true;
}
/**
* Validate the grade.
*
* @param int $value The value.
* @return true|lang_string
*/
protected function validate_grade($value) {
if ($value !== null) {
if ($value <= 0) {
return new lang_string('invalidgrade', 'core_competency');
}
// TODO MDL-52243 Use a core method to validate the grade_scale item.
// Check if grade exist in the scale item values.
$competency = $this->get_competency();
if (!array_key_exists($value - 1, $competency->get_scale()->scale_items)) {
return new lang_string('invalidgrade', 'core_competency');
}
}
return true;
}
/**
* Validate the plan ID.
*
* @param int $value The value.
* @return true|lang_string
*/
protected function validate_planid($value) {
if (!plan::record_exists($value) ) {
return new lang_string('invalidplan', 'core_competency');
}
return true;
}
/**
* Create a new user_competency_plan object.
*
* Note, this is intended to be used to create a blank relation, for instance when
* the record was not found in the database. This does not save the model.
*
* @param int $userid The user ID.
* @param int $competencyid The competency ID.
* @param int $planid The plan ID.
* @return \core_competency\user_competency_plan
*/
public static function create_relation($userid, $competencyid, $planid) {
$relation = new user_competency_plan(0, (object) array('userid' => $userid, 'competencyid' => $competencyid,
'planid' => $planid));
return $relation;
}
/**
* List the competencies in this plan.
*
* @param int $planid The plan ID
* @param int $userid The user ID
* @return competency[]
*/
public static function list_competencies($planid, $userid) {
global $DB;
$sql = 'SELECT c.*
FROM {' . competency::TABLE . '} c
JOIN {' . self::TABLE . '} ucp
ON ucp.competencyid = c.id
AND ucp.userid = :userid
WHERE ucp.planid = :planid
ORDER BY ucp.sortorder ASC,
ucp.id ASC';
$params = array('userid' => $userid, 'planid' => $planid);
$results = $DB->get_recordset_sql($sql, $params);
$instances = array();
foreach ($results as $key => $result) {
$instances[$key] = new competency(0, $result);
}
$results->close();
return $instances;
}
/**
* Fetch a competency by plan ID.
*
* @param int $id The plan ID.
* @return competency
*/
public static function get_competency_by_planid($planid, $competencyid) {
global $DB;
$sql = "SELECT c.*
FROM {" . self::TABLE . "} ucp
JOIN {" . competency::TABLE . "} c
ON c.id = ucp.competencyid
WHERE ucp.planid = ?
AND ucp.competencyid = ?";
$record = $DB->get_record_sql($sql, array($planid, $competencyid));
if (!$record) {
throw new \coding_exception('The competency does not belong to this plan: ' . $competencyid . ', ' . $planid);
}
return new competency(0, $record);
}
/**
* Get multiple user_competency_plan for a user.
*
* @param int $userid The user ID.
* @param int $planid The plan ID.
* @param array $competenciesorids Limit search to those competencies, or competency IDs.
* @return \core_competency\user_competency_plan[]
*/
public static function get_multiple($userid, $planid, array $competenciesorids = null) {
global $DB;
$params = array();
$params['userid'] = $userid;
$params['planid'] = $planid;
$sql = '1 = 1';
if (!empty($competenciesorids)) {
$test = reset($competenciesorids);
if (is_number($test)) {
$ids = $competenciesorids;
} else {
$ids = array();
foreach ($competenciesorids as $comp) {
$ids[] = $comp->get('id');
}
}
list($insql, $inparams) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
$params += $inparams;
$sql = "competencyid $insql";
}
// Order by ID to prevent random ordering.
return static::get_records_select("userid = :userid AND planid = :planid AND $sql", $params, 'id ASC');
}
/**
* Checks if a competency has user competency plan records.
*
* @param int $competencyid The competency ID
* @return boolean
*/
public static function has_records_for_competency($competencyid) {
return self::record_exists_select('competencyid = ?', array($competencyid));
}
/**
* Checks if any of the competencies of a framework has a user competency plan record.
*
* @param int $frameworkid The competency framework ID.
* @return boolean
*/
public static function has_records_for_framework($frameworkid) {
global $DB;
$sql = "SELECT 'x'
FROM {" . self::TABLE . "} ucp
JOIN {" . competency::TABLE . "} c
ON ucp.competencyid = c.id
WHERE c.competencyframeworkid = ?";
$params = array($frameworkid);
return $DB->record_exists_sql($sql, $params);
}
/**
* Check if user competency plan has records for competencies.
*
* @param array $competencyids The competences IDs
* @return boolean
*/
public static function has_records_for_competencies($competencyids) {
global $DB;
list($insql, $params) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED);
return self::record_exists_select("competencyid $insql", $params);
}
/**
* Count the number of records matching a specific template, optionally filtered by proficient values.
*
* @param int $templateid
* @param mixed $proficiency - If true - filter by proficiency, if false filter by not proficient, if null - do not filter.
* @return int
*/
public static function count_records_for_template($templateid, $proficiency=null) {
global $DB;
$params = array('templateid' => $templateid);
$sql = 'SELECT ' . " COUNT('x') " .
'FROM {' . self::TABLE . '} ucp
JOIN {' . plan::TABLE . '} p
ON ucp.planid = p.id
WHERE p.templateid = :templateid';
if ($proficiency === true) {
$sql .= ' AND ucp.proficiency = :proficiency';
$params['proficiency'] = true;
} else if ($proficiency === false) {
$sql .= ' AND (ucp.proficiency = :proficiency OR ucp.proficiency IS NULL)';
$params['proficiency'] = false;
}
return $DB->count_records_sql($sql, $params);
}
/**
* Get the list of competencies that were completed the least times (in completed plans) from a template.
*
* @param int $templateid
* @param int $skip The number of competencies to skip
* @param int $limit The max number of competencies to return
* @return competency[]
*/
public static function get_least_proficient_competencies_for_template($templateid, $skip = 0, $limit = 0) {
global $DB;
$fields = competency::get_sql_fields('c', 'c_');
$params = array('templateid' => $templateid, 'notproficient' => false);
$sql = 'SELECT ' . $fields . '
FROM (SELECT ucp.competencyid, COUNT(ucp.competencyid) AS timesnotproficient
FROM {' . self::TABLE . '} ucp
JOIN {' . plan::TABLE . '} p
ON p.id = ucp.planid
WHERE p.templateid = :templateid
AND (ucp.proficiency = :notproficient OR ucp.proficiency IS NULL)
GROUP BY ucp.competencyid
) p
JOIN {' . competency::TABLE . '} c
ON c.id = p.competencyid
ORDER BY p.timesnotproficient DESC, c.id ASC';
$results = $DB->get_records_sql($sql, $params, $skip, $limit);
$comps = array();
foreach ($results as $r) {
$c = competency::extract_record($r, 'c_');
$comps[] = new competency(0, $c);
}
return $comps;
}
}
+205
View File
@@ -0,0 +1,205 @@
<?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/>.
/**
* User evidence persistent.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use context_user;
use lang_string;
/**
* User evidence persistent class.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_evidence extends persistent {
const TABLE = 'competency_userevidence';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'userid' => array(
'type' => PARAM_INT
),
'name' => array(
'type' => PARAM_TEXT
),
'description' => array(
'type' => PARAM_CLEANHTML,
'default' => '',
),
'descriptionformat' => array(
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
'type' => PARAM_INT,
'default' => FORMAT_HTML,
),
'url' => array(
'type' => PARAM_URL,
'default' => '',
'message' => new lang_string('invalidurl', 'core_competency')
)
);
}
/**
* Can the current user manage this user evidence?
*
* @return bool
*/
public function can_manage() {
return self::can_manage_user($this->get('userid'));
}
/**
* Can the current user view this user evidence?
*
* @return bool
*/
public function can_read() {
return self::can_read_user($this->get('userid'));
}
/**
* Get the context of this user evidence.
*
* @return context
*/
public function get_context() {
return context_user::instance($this->get('userid'));
}
/**
* Get link competencies.
*/
public function get_competencies() {
return user_evidence_competency::get_competencies_by_userevidenceid($this->get('id'));
}
/**
* Get link user competencies.
*/
public function get_user_competencies() {
return user_evidence_competency::get_user_competencies_by_userevidenceid($this->get('id'));
}
/**
* Return true if the user of the evidence has plan.
*
* @return bool
*/
public function user_has_plan() {
return plan::record_exists_select('userid = ?', array($this->get('userid')));
}
/**
* Return the files associated with this evidence.
*
* @return object[]
*/
public function get_files() {
$fs = get_file_storage();
$files = $fs->get_area_files($this->get_context()->id, 'core_competency', 'userevidence', $this->get('id'),
'filename', false);
return $files;
}
/**
* Validate the URL.
*
* @param int $value
* @return true|lang_string
*/
protected function validate_url($value) {
if (empty($value) && !is_numeric($value)) {
return true;
}
if (!preg_match('@^https?://.+@', $value)) {
return new lang_string('invalidurl', 'core_competency');
}
return true;
}
/**
* Validate the user ID.
*
* @param int $value
* @return true|lang_string
*/
protected function validate_userid($value) {
global $DB;
// During create.
if (!$this->get('id')) {
// Check that the user exists. We do not need to do that on update because
// the userid of an evidence should never change.
if (!$DB->record_exists('user', array('id' => $value))) {
return new lang_string('invaliddata', 'error');
}
}
return true;
}
/**
* Can the current user manage a user's evidence?
*
* @param int $evidenceuserid The user to whom the evidence would belong.
* @return bool
*/
public static function can_manage_user($evidenceuserid) {
global $USER;
$context = context_user::instance($evidenceuserid);
$capabilities = array('moodle/competency:userevidencemanage');
if ($context->instanceid == $USER->id) {
$capabilities[] = 'moodle/competency:userevidencemanageown';
}
return has_any_capability($capabilities, $context);
}
/**
* Can the current user view a user's evidence?
*
* @param int $evidenceuserid The user to whom the evidence would belong.
* @return bool
*/
public static function can_read_user($evidenceuserid) {
$context = context_user::instance($evidenceuserid);
$capabilities = array('moodle/competency:userevidenceview');
return has_any_capability($capabilities, $context) || self::can_manage_user($evidenceuserid);
}
}
@@ -0,0 +1,178 @@
<?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/>.
/**
* User evidence competency persistent.
*
* This represent the many to many relationship between evidence of prior
* learning and competencies.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use stdClass;
use lang_string;
/**
* User evidence competency persistent class.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_evidence_competency extends persistent {
const TABLE = 'competency_userevidencecomp';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'userevidenceid' => array(
'type' => PARAM_INT
),
'competencyid' => array(
'type' => PARAM_INT,
),
);
}
/**
* Validate competency ID.
*
* @param int $value ID.
* @return true|lang_string
*/
protected function validate_competencyid($value) {
if (!competency::record_exists($value)) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Validate user evidence ID.
*
* @param int $value ID.
* @return true|lang_string
*/
protected function validate_userevidenceid($value) {
if (!user_evidence::record_exists($value)) {
return new lang_string('invaliddata', 'error');
}
return true;
}
/**
* Get competencies by user evidence ID.
*
* @param int $userevidenceid The user evidence ID.
* @return competency[]
*/
public static function get_competencies_by_userevidenceid($userevidenceid) {
global $DB;
$sql = "SELECT c.*
FROM {" . self::TABLE . "} uec
JOIN {" . competency::TABLE . "} c
ON uec.userevidenceid = ?
AND uec.competencyid = c.id
ORDER BY c.shortname";
$competencies = array();
$records = $DB->get_recordset_sql($sql, array($userevidenceid));
foreach ($records as $record) {
$competencies[] = new competency(0, $record);
}
$records->close();
return $competencies;
}
/**
* Get user competencies by user evidence ID.
*
* @param int $userevidenceid The user evidence ID.
* @return user_competency[]
*/
public static function get_user_competencies_by_userevidenceid($userevidenceid) {
global $DB;
$sql = "SELECT uc.*
FROM {" . user_competency::TABLE . "} uc
JOIN {" . self::TABLE . "} uec
ON uc.competencyid = uec.competencyid
JOIN {" . user_evidence::TABLE . "} ue
ON uec.userevidenceid = ue.id
AND uc.userid = ue.userid
AND ue.id = ?
ORDER BY uc.id ASC";
$usercompetencies = array();
$records = $DB->get_recordset_sql($sql, array($userevidenceid));
foreach ($records as $record) {
$usercompetencies[] = new user_competency(0, $record);
}
$records->close();
return $usercompetencies;
}
/**
* Get a relation.
*
* This does not perform any validation on the data passed. If the relation exists in the database
* then it is loaded in a the model, if not then it is up to the developer to save the model.
*
* @param int $userevidenceid
* @param int $competencyid
* @return template_cohort
*/
public static function get_relation($userevidenceid, $competencyid) {
global $DB;
$params = array(
'userevidenceid' => $userevidenceid,
'competencyid' => $competencyid
);
$relation = new static(null, (object) $params);
if ($record = $DB->get_record(static::TABLE, $params)) {
$relation->from_record($record);
}
return $relation;
}
/**
* Delete evidences using competencies.
*
* @param array $competencyids Array of competencies ids.
* @return bool Return true if the delete was successful.
*/
public static function delete_by_competencyids($competencyids) {
global $DB;
if (empty($competencyids)) {
return true;
}
list($insql, $params) = $DB->get_in_or_equal($competencyids);
return $DB->delete_records_select(self::TABLE, "competencyid $insql", $params);
}
}
+316
View File
@@ -0,0 +1,316 @@
<?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/>.
/**
* Competency lib.
*
* @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();
use core_competency\api;
use core_competency\plan;
use core_competency\url;
use core_competency\user_competency;
use core_competency\user_evidence;
/**
* Hook when a comment is added.
*
* @param stdClass $comment The comment.
* @param stdClass $params The parameters.
* @return array
*/
function core_competency_comment_add($comment, $params) {
global $USER, $PAGE;
if (!get_config('core_competency', 'enabled')) {
return;
}
if ($params->commentarea == 'user_competency') {
$uc = new user_competency($params->itemid);
// Message both the user and the reviewer, except when they are the author of the message.
$recipients = array($uc->get('userid'));
if ($uc->get('reviewerid')) {
$recipients[] = $uc->get('reviewerid');
}
$recipients = array_diff($recipients, array($comment->userid));
if (empty($recipients)) {
return;
}
// Get the sender.
$user = $USER;
if ($USER->id != $comment->userid) {
$user = core_user::get_user($comment->userid);
}
$fullname = fullname($user);
// Get the competency.
$competency = $uc->get_competency();
$competencyname = format_string($competency->get('shortname'), true, array('context' => $competency->get_context()));
// We want to send a message for one plan, trying to find an active one first, or the last modified one.
$plan = null;
$plans = $uc->get_plans();
foreach ($plans as $candidate) {
if ($candidate->get('status') == plan::STATUS_ACTIVE) {
$plan = $candidate;
break;
} else if (!empty($plan) && $plan->get('timemodified') < $candidate->get('timemodified')) {
$plan = $candidate;
} else if (empty($plan)) {
$plan = $candidate;
}
}
// Urls.
// TODO MDL-52749 Replace the link to the plan with the user competency page.
if (empty($plan)) {
$urlname = get_string('userplans', 'core_competency');
$url = url::plans($uc->get('userid'));
} else {
$urlname = $competencyname;
$url = url::user_competency_in_plan($uc->get('userid'), $uc->get('competencyid'), $plan->get('id'));
}
// Construct the message content.
$fullmessagehtml = get_string('usercommentedonacompetencyhtml', 'core_competency', array(
'fullname' => $fullname,
'competency' => $competencyname,
'comment' => format_text($comment->content, $comment->format, array('context' => $params->context->id)),
'url' => $url->out(true),
'urlname' => $urlname,
));
if ($comment->format == FORMAT_PLAIN || $comment->format == FORMAT_MOODLE) {
$format = FORMAT_MOODLE;
$fullmessage = get_string('usercommentedonacompetency', 'core_competency', array(
'fullname' => $fullname,
'competency' => $competencyname,
'comment' => $comment->content,
'url' => $url->out(false),
));
} else {
$format = FORMAT_HTML;
$fullmessage = $fullmessagehtml;
}
$message = new \core\message\message();
$message->courseid = SITEID;
$message->component = 'moodle';
$message->name = 'competencyusercompcomment';
$message->notification = 1;
$message->userfrom = core_user::get_noreply_user();
$message->subject = get_string('usercommentedonacompetencysubject', 'core_competency', $fullname);
$message->fullmessage = $fullmessage;
$message->fullmessageformat = $format;
$message->fullmessagehtml = $fullmessagehtml;
$message->smallmessage = get_string('usercommentedonacompetencysmall', 'core_competency', array(
'fullname' => $fullname,
'competency' => $competencyname,
));
$message->contexturl = $url->out(false);
$message->contexturlname = $urlname;
$userpicture = new \user_picture($user);
$userpicture->size = 1; // Use f1 size.
// Message each recipient.
foreach ($recipients as $recipient) {
$msgcopy = clone($message);
$msgcopy->userto = $recipient;
// Generate an out-of-session token for the user receiving the message.
$userpicture->includetoken = $recipient;
$msgcopy->customdata = [
'notificationiconurl' => $userpicture->get_url($PAGE)->out(false),
];
message_send($msgcopy);
}
} else if ($params->commentarea == 'plan') {
$plan = new plan($params->itemid);
// Message both the user and the reviewer, except when they are the author of the message.
$recipients = array($plan->get('userid'));
if ($plan->get('reviewerid')) {
$recipients[] = $plan->get('reviewerid');
}
$recipients = array_diff($recipients, array($comment->userid));
if (empty($recipients)) {
return;
}
// Get the sender.
$user = $USER;
if ($USER->id != $comment->userid) {
$user = core_user::get_user($comment->userid);
}
$fullname = fullname($user);
$planname = format_string($plan->get('name'), true, array('context' => $plan->get_context()));
$urlname = $planname;
$url = url::plan($plan->get('id'));
// Construct the message content.
$fullmessagehtml = get_string('usercommentedonaplanhtml', 'core_competency', array(
'fullname' => $fullname,
'plan' => $planname,
'comment' => format_text($comment->content, $comment->format, array('context' => $params->context->id)),
'url' => $url->out(true),
'urlname' => $urlname,
));
if ($comment->format == FORMAT_PLAIN || $comment->format == FORMAT_MOODLE) {
$format = FORMAT_MOODLE;
$fullmessage = get_string('usercommentedonaplan', 'core_competency', array(
'fullname' => $fullname,
'plan' => $planname,
'comment' => $comment->content,
'url' => $url->out(false),
));
} else {
$format = FORMAT_HTML;
$fullmessage = $fullmessagehtml;
}
$message = new \core\message\message();
$message->courseid = SITEID;
$message->component = 'moodle';
$message->name = 'competencyplancomment';
$message->notification = 1;
$message->userfrom = core_user::get_noreply_user();
$message->subject = get_string('usercommentedonaplansubject', 'core_competency', $fullname);
$message->fullmessage = $fullmessage;
$message->fullmessageformat = $format;
$message->fullmessagehtml = $fullmessagehtml;
$message->smallmessage = get_string('usercommentedonaplansmall', 'core_competency', array(
'fullname' => $fullname,
'plan' => $planname,
));
$message->contexturl = $url->out(false);
$message->contexturlname = $urlname;
$userpicture = new \user_picture($user);
$userpicture->size = 1; // Use f1 size.
// Message each recipient.
foreach ($recipients as $recipient) {
$msgcopy = clone($message);
$msgcopy->userto = $recipient;
// Generate an out-of-session token for the user receiving the message.
$userpicture->includetoken = $recipient;
$msgcopy->customdata = [
'notificationiconurl' => $userpicture->get_url($PAGE)->out(false),
];
message_send($msgcopy);
}
}
}
/**
* Return the permissions of for the comments.
*
* @param stdClass $params The parameters.
* @return array
*/
function core_competency_comment_permissions($params) {
if (!get_config('core_competency', 'enabled')) {
return array('post' => false, 'view' => false);
}
if ($params->commentarea == 'user_competency') {
$uc = new user_competency($params->itemid);
if ($uc->can_read()) {
return array('post' => $uc->can_comment(), 'view' => $uc->can_read_comments());
}
} else if ($params->commentarea == 'plan') {
$plan = new plan($params->itemid);
if ($plan->can_read()) {
return array('post' => $plan->can_comment(), 'view' => $plan->can_read_comments());
}
}
return array('post' => false, 'view' => false);
}
/**
* Validates comments.
*
* @param stdClass $params The parameters.
* @return bool
*/
function core_competency_comment_validate($params) {
if (!get_config('core_competency', 'enabled')) {
return false;
}
if ($params->commentarea == 'user_competency') {
if (!user_competency::record_exists($params->itemid)) {
return false;
}
return true;
} else if ($params->commentarea == 'plan') {
if (!plan::record_exists($params->itemid)) {
return false;
}
return true;
}
return false;
}
/**
* File serving.
*
* @param stdClass $course The course object.
* @param stdClass $cm The cm object.
* @param context $context The context object.
* @param string $filearea The file area.
* @param array $args List of arguments.
* @param bool $forcedownload Whether or not to force the download of the file.
* @param array $options Array of options.
* @return void|false
*/
function core_competency_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) {
global $CFG;
if (!get_config('core_competency', 'enabled')) {
return false;
}
$fs = get_file_storage();
$file = null;
$itemid = array_shift($args);
$filename = array_shift($args);
$filepath = $args ? '/' .implode('/', $args) . '/' : '/';
if ($filearea == 'userevidence' && $context->contextlevel == CONTEXT_USER) {
if (user_evidence::can_read_user($context->instanceid)) {
$file = $fs->get_file($context->id, 'core_competency', $filearea, $itemid, $filepath, $filename);
$forcedownload = true;
}
}
if (!$file) {
return false;
}
send_stored_file($file, null, 0, $forcedownload);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,319 @@
<?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/>.
namespace core_competency;
/**
* Competency ruleoutcome override grade tests
*
* @package core_competency
* @copyright 2022 Matthew Hilton <matthewhilton@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class competency_override_test extends \advanced_testcase {
/** @var \stdClass course record. */
protected $course;
/** @var \stdClass user record. */
protected $user;
/** @var \stdClass block instance record. */
protected $scale;
/** @var competency_framework loading competency frameworks from the DB. */
protected $framework;
/** @var plan loading competency plans from the DB. */
protected $plan;
/** @var competency loading competency from the DB. */
protected $comp1;
/** @var competency loading competency from the DB. */
protected $comp2;
/** @var \stdClass course module. */
protected $cm;
/** @var \completion_info completion information. */
protected $completion;
/** @var \context_course context course. */
protected $context;
public function setUp(): void {
$this->resetAfterTest(true);
$this->setAdminUser();
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
// Create user in course.
$c1 = $dg->create_course((object) ['enablecompletion' => true]);
$u1 = $dg->create_user();
$dg->enrol_user($u1->id, $c1->id);
// Create framework with three values.
$scale = $dg->create_scale(["scale" => "not,partially,fully"]);
$scaleconfiguration = json_encode([
['scaleid' => $scale->id],
['id' => 1, 'scaledefault' => 1, 'proficient' => 1]
]);
$framework = $lpg->create_framework([
'scaleid' => $scale->id,
'scaleconfiguration' => $scaleconfiguration
]);
$plan = $lpg->create_plan(['userid' => $u1->id]);
$comp1 = $lpg->create_competency([
'competencyframeworkid' => $framework->get('id'),
'scaleid' => $scale->id,
'scaleconfiguration' => $scaleconfiguration
]);
$comp2 = $lpg->create_competency([
'competencyframeworkid' => $framework->get('id'),
'scaleid' => $scale->id,
'scaleconfiguration' => $scaleconfiguration
]);
api::add_competency_to_plan($plan->get('id'), $comp1->get('id'));
api::add_competency_to_plan($plan->get('id'), $comp2->get('id'));
$lpg->create_course_competency([
'courseid' => $c1->id,
'competencyid' => $comp1->get('id'),
'ruleoutcome' => \core_competency\course_competency::OUTCOME_COMPLETE,
]);
$lpg->create_course_competency([
'courseid' => $c1->id,
'competencyid' => $comp2->get('id'),
'ruleoutcome' => \core_competency\course_competency::OUTCOME_COMPLETE,
]);
$label = $dg->create_module('label', ['course' => $c1, 'completion' => COMPLETION_VIEWED, 'completionview' => 1]);
$cm = get_coursemodule_from_instance('label', $label->id);
$completion = new \completion_info($c1);
$this->assertEquals(COMPLETION_ENABLED, $completion->is_enabled($cm));
// Link course module with the competency and setup a rule to complete the competency when the module is completed.
api::add_competency_to_course_module($cm, $comp1->get('id'));
api::add_competency_to_course_module($cm, $comp2->get('id'));
$coursemodulecomps = api::list_course_module_competencies_in_course_module($cm);
$this->assertCount(2, $coursemodulecomps);
api::set_course_module_competency_ruleoutcome($coursemodulecomps[0], \core_competency\course_competency::OUTCOME_COMPLETE);
api::set_course_module_competency_ruleoutcome($coursemodulecomps[1], \core_competency\course_competency::OUTCOME_COMPLETE);
$this->course = $c1;
$this->user = $u1;
$this->scale = $scale;
$this->framework = $framework;
$this->plan = $plan;
$this->comp1 = $comp1;
$this->comp2 = $comp2;
$this->cm = $cm;
$this->completion = new \completion_info($c1);
$this->context = \context_course::instance($this->course->id);
}
/**
* Test ruleoutcome overridegrade is correctly applied when coursemodule completion is processed.
*
* @covers \core_competency\api::set_course_module_competency_ruleoutcome
*/
public function test_ruleoutcome_overridegrade(): void {
// Initially the competency (and hence all the child competencies) should not be complete for the user.
[$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id'));
$this->assertEquals(0, $plancomp->usercompetency->get('grade'));
$this->assertEquals(0, $usercomp->get('grade'));
$this->assertEquals(0, $coursecomp->get('grade'));
[$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id'));
$this->assertEquals(0, $plancomp2->usercompetency->get('grade'));
$this->assertEquals(0, $usercomp2->get('grade'));
$this->assertEquals(0, $coursecomp2->get('grade'));
// Update the course module completion state to complete and trigger a competency update.
$data = $this->completion->get_data($this->cm, false, $this->user->id);
$data->completionstate = COMPLETION_COMPLETE;
$data->timemodified = time();
$this->completion->internal_set_data($this->cm, $data);
// Comptency should now be complete for user, plan, and course now that the course module is completed.
[$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id'));
$this->assertEquals(1, $plancomp->usercompetency->get('grade'));
$this->assertEquals(1, $usercomp->get('grade'));
$this->assertEquals(1, $coursecomp->get('grade'));
[$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id'));
$this->assertEquals(1, $plancomp2->usercompetency->get('grade'));
$this->assertEquals(1, $usercomp2->get('grade'));
$this->assertEquals(1, $coursecomp2->get('grade'));
// Change the competency completion for the user by adding evidence.
api::add_evidence($this->user->id, $this->comp1, $this->context,
evidence::ACTION_OVERRIDE, 'commentincontext', 'core', null, false, null, 2);
api::add_evidence($this->user->id, $this->comp2, $this->context,
evidence::ACTION_OVERRIDE, 'commentincontext', 'core', null, false, null, 2);
// After adding evidence, the competencies should now reflect the new grade value.
[$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id'));
$this->assertEquals(2, $plancomp->usercompetency->get('grade'));
$this->assertEquals(2, $usercomp->get('grade'));
$this->assertEquals(2, $coursecomp->get('grade'));
[$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id'));
$this->assertEquals(2, $plancomp2->usercompetency->get('grade'));
$this->assertEquals(2, $usercomp2->get('grade'));
$this->assertEquals(2, $coursecomp2->get('grade'));
// Update the course module competency to incomplete. This will not change the competency status.
$data = $this->completion->get_data($this->cm, false, $this->user->id);
$data->completionstate = COMPLETION_INCOMPLETE;
$data->timemodified = time();
$this->completion->internal_set_data($this->cm, $data);
[$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id'));
$this->assertEquals(2, $plancomp->usercompetency->get('grade'));
$this->assertEquals(2, $usercomp->get('grade'));
$this->assertEquals(2, $coursecomp->get('grade'));
[$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id'));
$this->assertEquals(2, $plancomp2->usercompetency->get('grade'));
$this->assertEquals(2, $usercomp2->get('grade'));
$this->assertEquals(2, $coursecomp2->get('grade'));
// Re-complete the course module, so that it attempts to re-complete the competencies.
$data = $this->completion->get_data($this->cm, false, $this->user->id);
$data->completionstate = COMPLETION_COMPLETE;
$data->timemodified = time();
$this->completion->internal_set_data($this->cm, $data);
// By default, this will not override the existing grade, so it should remain the same as before.
[$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id'));
$this->assertEquals(2, $plancomp->usercompetency->get('grade'));
$this->assertEquals(2, $usercomp->get('grade'));
$this->assertEquals(2, $coursecomp->get('grade'));
[$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id'));
$this->assertEquals(2, $plancomp2->usercompetency->get('grade'));
$this->assertEquals(2, $usercomp2->get('grade'));
$this->assertEquals(2, $coursecomp2->get('grade'));
// Update the completion rule for only competency 1 to $overridegrade = true.
$coursemodulecomps = api::list_course_module_competencies_in_course_module($this->cm);
api::set_course_module_competency_ruleoutcome($coursemodulecomps[0], \core_competency\course_competency::OUTCOME_COMPLETE,
true);
// Mark as incomplete then re-complete the course module.
$data = $this->completion->get_data($this->cm, false, $this->user->id);
$data->completionstate = COMPLETION_INCOMPLETE;
$data->timemodified = time();
$this->completion->internal_set_data($this->cm, $data);
$data = $this->completion->get_data($this->cm, false, $this->user->id);
$data->completionstate = COMPLETION_COMPLETE;
$data->timemodified = time();
$this->completion->internal_set_data($this->cm, $data);
// Because the rule is now set to override existing grades, the grade should have now updated as per the ruleoutcome.
// However the second competency didn't have this rule set, so it will not be overriden.
[$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id'));
$this->assertEquals(1, $plancomp->usercompetency->get('grade'));
$this->assertEquals(1, $usercomp->get('grade'));
$this->assertEquals(1, $coursecomp->get('grade'));
[$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id'));
$this->assertEquals(2, $plancomp2->usercompetency->get('grade'));
$this->assertEquals(2, $usercomp2->get('grade'));
$this->assertEquals(2, $coursecomp2->get('grade'));
// If competency 2 is changed now to override and re-completed, it will update the same as competency 1.
api::set_course_module_competency_ruleoutcome($coursemodulecomps[1], \core_competency\course_competency::OUTCOME_COMPLETE,
true);
$data = $this->completion->get_data($this->cm, false, $this->user->id);
$data->completionstate = COMPLETION_INCOMPLETE;
$data->timemodified = time();
$this->completion->internal_set_data($this->cm, $data);
$data = $this->completion->get_data($this->cm, false, $this->user->id);
$data->completionstate = COMPLETION_COMPLETE;
$data->timemodified = time();
$this->completion->internal_set_data($this->cm, $data);
// Now both the competencies have $overridegrade = true,
// they should both reflect the ruleoutcome after the completion above was processed.
[$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id'));
$this->assertEquals(1, $plancomp->usercompetency->get('grade'));
$this->assertEquals(1, $usercomp->get('grade'));
$this->assertEquals(1, $coursecomp->get('grade'));
[$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id'));
$this->assertEquals(1, $plancomp2->usercompetency->get('grade'));
$this->assertEquals(1, $usercomp2->get('grade'));
$this->assertEquals(1, $coursecomp2->get('grade'));
}
/**
* Test competency backup and restore correctly restores the ruleoutcome overridegrade value.
*
* @covers \core_competency\api::set_course_module_competency_ruleoutcome
*/
public function test_override_backup_restore(): void {
global $CFG;
require_once($CFG->dirroot . '/course/externallib.php');
// Set one to override grade and another to not override grade.
$coursemodulecomps = api::list_course_module_competencies_in_course_module($this->cm);
api::set_course_module_competency_ruleoutcome($coursemodulecomps[0], \core_competency\course_competency::OUTCOME_COMPLETE,
false);
api::set_course_module_competency_ruleoutcome($coursemodulecomps[1], \core_competency\course_competency::OUTCOME_COMPLETE,
true);
// Duplicate the course (backup and restore).
$duplicated = \core_course_external::duplicate_course($this->course->id, 'test', 'test', $this->course->category);
// Get the new course modules.
$newcoursemodules = get_coursemodules_in_course('label', $duplicated['id']);
$this->assertCount(1, $newcoursemodules);
$cm = array_pop($newcoursemodules);
// Get the comeptencies for this cm.
$newcoursemodulecomps = api::list_course_module_competencies_in_course_module($cm);
$this->assertCount(2, $newcoursemodulecomps);
// Ensure the override grade settings are restored properly.
$this->assertEquals($coursemodulecomps[0]->get('overridegrade'), $newcoursemodulecomps[0]->get('overridegrade'));
$this->assertEquals($coursemodulecomps[1]->get('overridegrade'), $newcoursemodulecomps[1]->get('overridegrade'));
}
/**
* Gets the course, user and plan competency for the given competency ID
*
* @param int $compid ID of the competency.
* @return array array containing the three related competencies
*/
private function get_related_competencies(int $compid): array {
$coursecomp = api::get_user_competency_in_course($this->course->id, $this->user->id, $compid);
$usercomp = api::get_user_competency($this->user->id, $compid);
$plancomp = api::get_plan_competency($this->plan, $compid);
return [$coursecomp, $plancomp, $usercomp];
}
}
+366
View File
@@ -0,0 +1,366 @@
<?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/>.
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
/**
* Competency rule testcase.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class competency_rule_test extends \externallib_advanced_testcase {
public function test_rule_all_matching(): void {
$this->resetAfterTest(true);
$this->setAdminUser();
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$u1 = $this->getDataGenerator()->create_user();
// Set up the framework and competencies.
$framework = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'),
'ruletype' => 'core_competency\competency_rule_all'));
$c11 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c1->get('id'),
'ruletype' => 'core_competency\competency_rule_all'));
$c111 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c11->get('id'),
'ruletype' => 'core_competency\competency_rule_all'));
$c112 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c11->get('id'),
'ruletype' => 'core_competency\competency_rule_all'));
$c12 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c1->get('id'),
'ruletype' => 'core_competency\competency_rule_all'));
$c13 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c1->get('id'),
'ruletype' => 'core_competency\competency_rule_all'));
$c131 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c13->get('id'),
'ruletype' => 'core_competency\competency_rule_all'));
// Create some user competency records.
$uc1 = $lpg->create_user_competency(array('competencyid' => $c1->get('id'), 'userid' => $u1->id));
$uc11 = $lpg->create_user_competency(array('competencyid' => $c11->get('id'), 'userid' => $u1->id,
'grade' => 1, 'proficiency' => 1));
$uc111 = $lpg->create_user_competency(array('competencyid' => $c111->get('id'), 'userid' => $u1->id,
'grade' => 1, 'proficiency' => 1));
$uc112 = $lpg->create_user_competency(array('competencyid' => $c112->get('id'), 'userid' => $u1->id,
'grade' => 1, 'proficiency' => 1));
$uc12 = $lpg->create_user_competency(array('competencyid' => $c12->get('id'), 'userid' => $u1->id));
$uc13 = new user_competency(0, (object) array('userid' => $u1->id, 'competencyid' => $c13->get('id')));
// Not all children are met.
$cr = new competency_rule_all($c1);
$this->assertFalse($cr->matches($uc1));
// All children are met.
$cr = new competency_rule_all($c11);
$this->assertTrue($cr->matches($uc11));
// The competency doesn't have any children.
$cr = new competency_rule_all($c12);
$this->assertFalse($cr->matches($uc12));
// The competency doesn't have saved user competency records.
$cr = new competency_rule_all($c13);
$this->assertFalse($cr->matches($uc13));
}
public function test_rule_points_validation(): void {
$this->resetAfterTest(true);
$this->setAdminUser();
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$framework = $lpg->create_framework();
$framework2 = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c1->get('id')));
$c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c1->get('id')));
$c4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$cx = $lpg->create_competency(array('competencyframeworkid' => $framework2->get('id')));
$c1->set('ruletype', 'core_competency\competency_rule_points');
$rule = new competency_rule_points($c1);
// Invalid config.
$config = json_encode(array());
$this->assertFalse($rule->validate_config($config));
// Missing required points.
$config = json_encode(array(
'base' => array(),
'competencies' => array(
array('id' => $c2->get('id'), 'points' => 1, 'required' => 0),
array('id' => $c3->get('id'), 'points' => 1, 'required' => 0),
)
));
$this->assertFalse($rule->validate_config($config));
// Invalid required points.
$config = json_encode(array(
'base' => array('points' => 'abc'),
'competencies' => array(
array('id' => $c2->get('id'), 'points' => 1, 'required' => 0),
array('id' => $c3->get('id'), 'points' => 1, 'required' => 0),
)
));
$this->assertFalse($rule->validate_config($config));
// Less than 1 required points.
$config = json_encode(array(
'base' => array('points' => 0),
'competencies' => array(
array('id' => $c2->get('id'), 'points' => 1, 'required' => 0),
array('id' => $c3->get('id'), 'points' => 1, 'required' => 0),
)
));
$this->assertFalse($rule->validate_config($config));
// Not enough required points.
$config = json_encode(array(
'base' => array('points' => 3),
'competencies' => array(
array('id' => $c2->get('id'), 'points' => 1, 'required' => 0),
array('id' => $c3->get('id'), 'points' => 1, 'required' => 0),
)
));
$this->assertFalse($rule->validate_config($config));
// Duplicate competency.
$config = json_encode(array(
'base' => array('points' => 1),
'competencies' => array(
array('id' => $c2->get('id'), 'points' => 1, 'required' => 0),
array('id' => $c2->get('id'), 'points' => 1, 'required' => 0),
)
));
$this->assertFalse($rule->validate_config($config));
// Competency includes itself.
$config = json_encode(array(
'base' => array('points' => 1),
'competencies' => array(
array('id' => $c1->get('id'), 'points' => 1, 'required' => 0),
array('id' => $c2->get('id'), 'points' => 1, 'required' => 0),
)
));
$this->assertFalse($rule->validate_config($config));
// Cannot use negative points.
$config = json_encode(array(
'base' => array('points' => 1),
'competencies' => array(
array('id' => $c2->get('id'), 'points' => -1, 'required' => 0),
array('id' => $c3->get('id'), 'points' => 1, 'required' => 0),
)
));
$this->assertFalse($rule->validate_config($config));
// Not competencies set.
$config = json_encode(array(
'base' => array('points' => 1),
'competencies' => array(
)
));
$this->assertFalse($rule->validate_config($config));
// There is a competency that is not a child.
$config = json_encode(array(
'base' => array('points' => 1),
'competencies' => array(
array('id' => $c1->get('id'), 'points' => 1, 'required' => 0),
array('id' => $c2->get('id'), 'points' => 1, 'required' => 0),
array('id' => $c3->get('id'), 'points' => 1, 'required' => 0),
)
));
$this->assertFalse($rule->validate_config($config));
// There is a competency from another framework in there.
$config = json_encode(array(
'base' => array('points' => 1),
'competencies' => array(
array('id' => $cx->get('id'), 'points' => 1, 'required' => 0),
array('id' => $c3->get('id'), 'points' => 1, 'required' => 0),
)
));
$this->assertFalse($rule->validate_config($config));
// A normal config.
$config = json_encode(array(
'base' => array('points' => 4),
'competencies' => array(
array('id' => $c2->get('id'), 'points' => 3, 'required' => 0),
array('id' => $c3->get('id'), 'points' => 2, 'required' => 1),
)
));
$this->assertTrue($rule->validate_config($config));
}
public function test_rule_points_matching(): void {
$this->resetAfterTest(true);
$this->setAdminUser();
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$u1 = $this->getDataGenerator()->create_user();
// Set up the framework and competencies.
$framework = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c1->set('ruletype', 'core_competency\competency_rule_points');
$comprule = new competency_rule_points($c1);
$c11 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c1->get('id')));
$c12 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c1->get('id')));
$c13 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c1->get('id')));
$c14 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 'parentid' => $c1->get('id')));
// Create some user competency records.
$uc1 = $lpg->create_user_competency(array('competencyid' => $c1->get('id'), 'userid' => $u1->id));
$uc11 = $lpg->create_user_competency(array('competencyid' => $c11->get('id'), 'userid' => $u1->id,
'grade' => 1, 'proficiency' => 1));
$uc12 = $lpg->create_user_competency(array('competencyid' => $c12->get('id'), 'userid' => $u1->id,
'grade' => 1, 'proficiency' => 1));
$uc13 = $lpg->create_user_competency(array('competencyid' => $c13->get('id'), 'userid' => $u1->id));
// Enough points.
$rule = array(
'base' => array('points' => 8),
'competencies' => array(
array(
'id' => $c11->get('id'),
'points' => 4,
'required' => 0
),
array(
'id' => $c12->get('id'),
'points' => 4,
'required' => 0
),
)
);
$c1->set('ruleconfig', json_encode($rule));
$c1->update();
$this->assertTrue($comprule->matches($uc1));
// Not enough points.
$rule = array(
'base' => array('points' => 8),
'competencies' => array(
array(
'id' => $c11->get('id'),
'points' => 4,
'required' => 0
),
array(
'id' => $c13->get('id'),
'points' => 4,
'required' => 0
),
)
);
$c1->set('ruleconfig', json_encode($rule));
$c1->update();
$this->assertFalse($comprule->matches($uc1));
// One required that is not met but points were OK.
$rule = array(
'base' => array('points' => 8),
'competencies' => array(
array(
'id' => $c11->get('id'),
'points' => 4,
'required' => 0
),
array(
'id' => $c12->get('id'),
'points' => 4,
'required' => 0
),
array(
'id' => $c13->get('id'),
'points' => 4,
'required' => 1
),
)
);
$c1->set('ruleconfig', json_encode($rule));
$c1->update();
$this->assertFalse($comprule->matches($uc1));
// One required, one not, should match.
$rule = array(
'base' => array('points' => 8),
'competencies' => array(
array(
'id' => $c11->get('id'),
'points' => 4,
'required' => 0
),
array(
'id' => $c12->get('id'),
'points' => 4,
'required' => 1
),
)
);
$c1->set('ruleconfig', json_encode($rule));
$c1->update();
$this->assertTrue($comprule->matches($uc1));
// All required and should match.
$rule = array(
'base' => array('points' => 8),
'competencies' => array(
array(
'id' => $c11->get('id'),
'points' => 4,
'required' => 1
),
array(
'id' => $c12->get('id'),
'points' => 4,
'required' => 1
),
)
);
$c1->set('ruleconfig', json_encode($rule));
$c1->update();
$this->assertTrue($comprule->matches($uc1));
// All required, but one doesn't have a user record.
$rule = array(
'base' => array('points' => 4),
'competencies' => array(
array(
'id' => $c12->get('id'),
'points' => 4,
'required' => 1
),
array(
'id' => $c14->get('id'),
'points' => 4,
'required' => 1
),
)
);
$c1->set('ruleconfig', json_encode($rule));
$c1->update();
$this->assertFalse($comprule->matches($uc1));
}
}
+60
View File
@@ -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/>.
namespace core_competency;
/**
* Competency testcase.
*
* @package core_competency
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class competency_test extends \advanced_testcase {
public function test_get_framework_depth(): void {
$this->resetAfterTest();
$ccg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$f1 = $ccg->create_framework();
$f2 = $ccg->create_framework();
$f3 = $ccg->create_framework();
$f4 = $ccg->create_framework();
$f1c1 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
$f1c11 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id'), 'parentid' => $f1c1->get('id')]);
$f1c111 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id'), 'parentid' => $f1c11->get('id')]);
$f1c1111 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id'), 'parentid' => $f1c111->get('id')]);
$f2c1 = $ccg->create_competency(['competencyframeworkid' => $f2->get('id')]);
$f2c2 = $ccg->create_competency(['competencyframeworkid' => $f2->get('id')]);
$f2c21 = $ccg->create_competency(['competencyframeworkid' => $f2->get('id'), 'parentid' => $f2c2->get('id')]);
$f2c22 = $ccg->create_competency(['competencyframeworkid' => $f2->get('id'), 'parentid' => $f2c2->get('id')]);
$f2c211 = $ccg->create_competency(['competencyframeworkid' => $f2->get('id'), 'parentid' => $f2c21->get('id')]);
$f2c221 = $ccg->create_competency(['competencyframeworkid' => $f2->get('id'), 'parentid' => $f2c22->get('id')]);
$f2c222 = $ccg->create_competency(['competencyframeworkid' => $f2->get('id'), 'parentid' => $f2c22->get('id')]);
$f2c223 = $ccg->create_competency(['competencyframeworkid' => $f2->get('id'), 'parentid' => $f2c22->get('id')]);
$f2c3 = $ccg->create_competency(['competencyframeworkid' => $f2->get('id')]);
$f3c1 = $ccg->create_competency(['competencyframeworkid' => $f3->get('id')]);
$this->assertEquals(4, competency::get_framework_depth($f1->get('id')));
$this->assertEquals(3, competency::get_framework_depth($f2->get('id')));
$this->assertEquals(1, competency::get_framework_depth($f3->get('id')));
$this->assertEquals(0, competency::get_framework_depth($f4->get('id')));
}
}
@@ -0,0 +1,114 @@
<?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/>.
namespace core_competency;
/**
* This test ensures that the course competency settings are applied and work correctly.
*
* @package core_competency
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_competency_settings_test extends \advanced_testcase {
public function test_who_can_change_settings(): void {
global $CFG, $DB;
$this->resetAfterTest(true);
$syscontext = \context_system::instance();
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$role = create_role('Settings changer role', 'settingschanger', 'Someone who can change course competency settings');
assign_capability('moodle/competency:coursecompetencyconfigure', CAP_ALLOW, $role, $syscontext->id);
assign_capability('moodle/competency:competencygrade', CAP_ALLOW, $role, $syscontext->id);
assign_capability('moodle/competency:coursecompetencyview', CAP_ALLOW, $role, $syscontext->id);
assign_capability('moodle/competency:planview', CAP_ALLOW, $role, $syscontext->id);
$gradedrole = create_role('Graded role', 'graded', 'Someone who can be graded');
assign_capability('moodle/competency:coursecompetencygradable', CAP_ALLOW, $gradedrole, $syscontext->id);
$c1 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$u3 = $dg->create_user();
$framework = $lpg->create_framework();
$comp1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$comp2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$lpg->create_course_competency(array('competencyid' => $comp1->get('id'), 'courseid' => $c1->id));
$lpg->create_course_competency(array('competencyid' => $comp2->get('id'), 'courseid' => $c1->id));
// Enrol the user.
$dg->enrol_user($u1->id, $c1->id);
role_assign($gradedrole, $u1->id, $syscontext->id);
// Assign roles.
role_assign($role, $u2->id, $syscontext->id);
$this->setUser($u2);
set_config('pushcourseratingstouserplans', true, 'core_competency');
$coursesettings = course_competency_settings::get_by_courseid($c1->id);
$this->assertTrue((boolean)$coursesettings->get('pushratingstouserplans'));
set_config('pushcourseratingstouserplans', false, 'core_competency');
$coursesettings = course_competency_settings::get_by_courseid($c1->id);
$this->assertFalse((boolean)$coursesettings->get('pushratingstouserplans'));
api::update_course_competency_settings($c1->id, (object) array('pushratingstouserplans' => true));
$coursesettings = course_competency_settings::get_by_courseid($c1->id);
$this->assertTrue((boolean)$coursesettings->get('pushratingstouserplans'));
set_config('pushcourseratingstouserplans', true, 'core_competency');
api::update_course_competency_settings($c1->id, (object) array('pushratingstouserplans' => false));
$coursesettings = course_competency_settings::get_by_courseid($c1->id);
$this->assertFalse((boolean)$coursesettings->get('pushratingstouserplans'));
// Right now the setting is false.
api::grade_competency_in_course($c1->id, $u1->id, $comp1->get('id'), 1, 'Note');
$filterparams = array(
'userid' => $u1->id,
'competencyid' => $comp1->get('id'),
);
$usercompcourse = \core_competency\user_competency_course::get_record($filterparams);
$usercomp = \core_competency\user_competency::get_record($filterparams);
// No grade in plan - only a grade in the course.
$this->assertEmpty($usercomp->get('grade'));
$this->assertEquals(1, $usercompcourse->get('grade'));
api::update_course_competency_settings($c1->id, (object) array('pushratingstouserplans' => true));
api::grade_competency_in_course($c1->id, $u1->id, $comp1->get('id'), 2, 'Note 2');
$filterparams = array(
'userid' => $u1->id,
'competencyid' => $comp1->get('id'),
);
$usercompcourse = \core_competency\user_competency_course::get_record($filterparams);
$usercomp = \core_competency\user_competency::get_record($filterparams);
// Updated grade in plan - updated grade in the course.
$this->assertEquals(2, $usercomp->get('grade'));
$this->assertEquals(2, $usercompcourse->get('grade'));
$this->setUser($u3);
$this->expectException('required_capability_exception');
api::update_course_competency_settings($c1->id, (object) array('pushratingstouserplans' => false));
}
}
+126
View File
@@ -0,0 +1,126 @@
<?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/>.
namespace core_competency;
/**
* Course competency persistent testcase.
*
* @package core_competency
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_competency_test extends \advanced_testcase {
public function test_get_courses_with_competency_and_user(): void {
global $CFG, $DB;
$this->resetAfterTest(true);
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$c1 = $dg->create_course();
$c2 = $dg->create_course();
$c3 = $dg->create_course();
$c4 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$u3 = $dg->create_user();
$u4 = $dg->create_user();
$flatfileplugin = enrol_get_plugin('flatfile');
$flatfileinstanceid = $flatfileplugin->add_instance($c2);
$framework = $lpg->create_framework();
$comp1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); // In C1, and C2.
$comp2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); // In C2.
$comp3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); // In None.
$comp4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); // In C4.
$lpg->create_course_competency(array('competencyid' => $comp1->get('id'), 'courseid' => $c1->id));
$lpg->create_course_competency(array('competencyid' => $comp1->get('id'), 'courseid' => $c2->id));
$lpg->create_course_competency(array('competencyid' => $comp2->get('id'), 'courseid' => $c2->id));
$lpg->create_course_competency(array('competencyid' => $comp4->get('id'), 'courseid' => $c4->id));
// Enrol the user 1 in C1, C2, and C3.
$dg->enrol_user($u1->id, $c1->id);
$dg->enrol_user($u1->id, $c2->id);
$dg->enrol_user($u1->id, $c3->id);
// Enrol the user 2 in C4.
$dg->enrol_user($u2->id, $c4->id);
// Enrol the user 3 in C1 and C2, but non active in C2.
$dg->enrol_user($u3->id, $c1->id);
$dg->enrol_user($u3->id, $c2->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
// Enrol the user 4 with a plugin which will be enabled/disabled.
$dg->enrol_user($u4->id, $c2->id, null, 'flatfile');
// Using the competency that is not used anywhere -> no courses.
$this->assertCount(0, course_competency::get_courses_with_competency_and_user($comp3->get('id'), $u1->id));
// Using the competency that is used in a course where the user is not enrolled -> no courses.
$this->assertCount(0, course_competency::get_courses_with_competency_and_user($comp4->get('id'), $u1->id));
// Using the competency that is used in a course where the user is enrolled -> one course.
$courses = course_competency::get_courses_with_competency_and_user($comp2->get('id'), $u1->id);
$this->assertCount(1, $courses);
$this->assertArrayHasKey($c2->id, $courses);
// Using the competency used multiple times.
$courses = course_competency::get_courses_with_competency_and_user($comp1->get('id'), $u1->id);
$this->assertCount(2, $courses);
$this->assertArrayHasKey($c1->id, $courses);
$this->assertArrayHasKey($c2->id, $courses);
// Checking for another user where the competency is used twice, but not for them.
$courses = course_competency::get_courses_with_competency_and_user($comp1->get('id'), $u2->id);
$this->assertCount(0, $courses);
// Checking for another user where the competency is used in their course.
$courses = course_competency::get_courses_with_competency_and_user($comp4->get('id'), $u2->id);
$this->assertCount(1, $courses);
$this->assertArrayHasKey($c4->id, $courses);
// Checking for a user who is suspended in a course.
$courses = course_competency::get_courses_with_competency_and_user($comp1->get('id'), $u3->id);
$this->assertCount(1, $courses);
$this->assertArrayHasKey($c1->id, $courses);
// Check for the user with plugin enabled.
$enrolplugins = explode(',', $CFG->enrol_plugins_enabled);
$enrolplugins[] = 'flatfile';
$CFG->enrol_plugins_enabled = implode(',', array_unique($enrolplugins));
$courses = course_competency::get_courses_with_competency_and_user($comp1->get('id'), $u4->id);
$this->assertCount(1, $courses);
$this->assertArrayHasKey($c2->id, $courses);
// Check for the user with plugin enabled, but enrolment instance disabled.
$flatfileinstance = $DB->get_record('enrol', array('id' => $flatfileinstanceid));
$flatfileplugin->update_status($flatfileinstance, ENROL_INSTANCE_DISABLED);
$courses = course_competency::get_courses_with_competency_and_user($comp1->get('id'), $u4->id);
$this->assertCount(0, $courses);
$flatfileplugin->update_status($flatfileinstance, ENROL_INSTANCE_ENABLED);
// Check for the user with plugin disabled.
$enrolplugins = array_flip(explode(',', $CFG->enrol_plugins_enabled));
unset($enrolplugins['flatfile']);
$CFG->enrol_plugins_enabled = implode(',', array_keys($enrolplugins));
$courses = course_competency::get_courses_with_competency_and_user($comp1->get('id'), $u4->id);
$this->assertCount(0, $courses);
}
}
@@ -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/>.
namespace core_competency;
/**
* Course module competency persistent testcase.
*
* @package core_competency
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_competency_test extends \advanced_testcase {
public function test_count_competencies(): void {
global $CFG, $DB;
$this->resetAfterTest(true);
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$c1 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$framework = $lpg->create_framework();
$comp1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); // In C1, and C2.
$comp2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); // In C2.
$lpg->create_course_competency(array('competencyid' => $comp1->get('id'), 'courseid' => $c1->id));
$lpg->create_course_competency(array('competencyid' => $comp2->get('id'), 'courseid' => $c1->id));
$assign1a = $dg->create_module('assign', ['course' => $c1]);
$assign1b = $dg->create_module('assign', ['course' => $c1]);
$cmc1a = $lpg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1a->cmid]);
$cmc1b = $lpg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1b->cmid]);
$cmc2b = $lpg->create_course_module_competency(['competencyid' => $comp2->get('id'), 'cmid' => $assign1b->cmid]);
// Enrol the user 1 in C1.
$dg->enrol_user($u1->id, $c1->id);
$all = course_module_competency::list_course_module_competencies($assign1a->cmid);
$this->assertEquals(course_module_competency::count_competencies($assign1a->cmid), count($all));
$all = course_module_competency::list_course_module_competencies($assign1b->cmid);
$this->assertEquals(course_module_competency::count_competencies($assign1b->cmid), count($all));
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,261 @@
<?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/>.
use core_competency\competency;
use core_competency\competency_framework;
use core_competency\plan;
/**
* Behat data generator for core_competency.
*
* @package core_competency
* @category test
* @copyright 2022 Noel De Martin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_core_competency_generator extends behat_generator_base {
/**
* Get a list of the entities that Behat can create using the generator step.
*
* @return array
*/
protected function get_creatable_entities(): array {
return [
'competencies' => [
'singular' => 'competency',
'datagenerator' => 'competency',
'required' => ['shortname', 'competencyframework'],
'switchids' => ['competencyframework' => 'competencyframeworkid'],
],
'course_competencies' => [
'singular' => 'course_competency',
'datagenerator' => 'course_competency',
'required' => ['course', 'competency'],
'switchids' => ['course' => 'courseid', 'competency' => 'competencyid'],
],
'frameworks' => [
'singular' => 'framework',
'datagenerator' => 'framework',
'required' => ['shortname'],
'switchids' => ['scale' => 'scaleid'],
],
'plans' => [
'singular' => 'plan',
'datagenerator' => 'plan',
'required' => ['name'],
'switchids' => ['user' => 'userid'],
],
'related_competencies' => [
'singular' => 'related_competency',
'datagenerator' => 'related_competency',
'required' => ['competency', 'relatedcompetency'],
'switchids' => ['competency' => 'competencyid', 'relatedcompetency' => 'relatedcompetencyid'],
],
'user_competency' => [
'singular' => 'user_competency',
'datagenerator' => 'user_competency',
'required' => ['competency', 'user'],
'switchids' => ['competency' => 'competencyid', 'user' => 'userid'],
],
'user_competency_courses' => [
'singular' => 'user_competency_course',
'datagenerator' => 'user_competency_course',
'required' => ['course', 'competency', 'user'],
'switchids' => ['course' => 'courseid', 'competency' => 'competencyid', 'user' => 'userid'],
],
'user_competency_plans' => [
'singular' => 'user_competency_plan',
'datagenerator' => 'user_competency_plan',
'required' => ['plan', 'competency', 'user'],
'switchids' => ['plan' => 'planid', 'competency' => 'competencyid', 'user' => 'userid'],
],
];
}
/**
* Get the competecy framework id using an idnumber.
*
* @param string $idnumber
* @return int The competecy framework id
*/
protected function get_competencyframework_id(string $idnumber): int {
global $DB;
if (!$id = $DB->get_field('competency_framework', 'id', ['idnumber' => $idnumber])) {
throw new Exception('The specified competency framework with idnumber "' . $idnumber . '" could not be found.');
}
return $id;
}
/**
* Get the competecy id using an idnumber.
*
* @param string $idnumber
* @return int The competecy id
*/
protected function get_competency_id(string $idnumber): int {
global $DB;
if (!$id = $DB->get_field('competency', 'id', ['idnumber' => $idnumber])) {
throw new Exception('The specified competency with idnumber "' . $idnumber . '" could not be found.');
}
return $id;
}
/**
* Get the learning plan id using a name.
*
* @param string $name
* @return int The learning plan id
*/
protected function get_plan_id(string $name): int {
global $DB;
if (!$id = $DB->get_field('competency_plan', 'id', ['name' => $name])) {
throw new Exception('The specified learning plan with name "' . $name . '" could not be found.');
}
return $id;
}
/**
* Get the related competecy id using an idnumber.
*
* @param string $idnumber
* @return int The related competecy id
*/
protected function get_relatedcompetency_id(string $idnumber): int {
return $this->get_competency_id($idnumber);
}
/**
* Add a plan.
*
* @param array $data Plan data.
*/
public function process_plan(array $data): void {
$generator = $this->get_data_generator();
$competencyids = $data['competencyids'] ?? [];
unset($data['competencyids']);
$plan = $generator->create_plan($data);
foreach ($competencyids as $competencyid) {
$generator->create_plan_competency([
'planid' => $plan->get('id'),
'competencyid' => $competencyid,
]);
}
}
/**
* Preprocess user competency data.
*
* @param array $data Raw data.
* @return array Processed data.
*/
protected function preprocess_user_competency(array $data): array {
$this->prepare_grading($data);
return $data;
}
/**
* Preprocess user course competency data.
*
* @param array $data Raw data.
* @return array Processed data.
*/
protected function preprocess_user_competency_course(array $data): array {
$this->prepare_grading($data);
return $data;
}
/**
* Preprocess user learning plan competency data.
*
* @param array $data Raw data.
* @return array Processed data.
*/
protected function preprocess_user_competency_plan(array $data): array {
$this->prepare_grading($data);
return $data;
}
/**
* Preprocess plan data.
*
* @param array $data Raw data.
* @return array Processed data.
*/
protected function preprocess_plan(array $data): array {
if (isset($data['competencies'])) {
$competencies = array_map('trim', str_getcsv($data['competencies']));
$data['competencyids'] = array_map([$this, 'get_competency_id'], $competencies);
unset($data['competencies']);
}
global $USER;
return $data + [
'userid' => $USER->id,
'status' => plan::STATUS_ACTIVE,
];
}
/**
* Prepare grading attributes for record data.
*
* @param array $data Record data.
*/
protected function prepare_grading(array &$data): void {
if (!isset($data['grade'])) {
return;
}
global $DB;
$competency = competency::get_record(['id' => $data['competencyid']]);
$competencyframework = competency_framework::get_record(['id' => $competency->get('competencyframeworkid')]);
$scale = $DB->get_field('scale', 'scale', ['id' => $competencyframework->get('scaleid')]);
$grades = array_map('trim', explode(',', $scale));
$grade = array_search($data['grade'], $grades);
if ($grade === false) {
throw new Exception('The grade "'.$data['grade'].'" was not found in the "'.
$competencyframework->get('shortname').'" competency framework.');
}
$data['proficiency'] = true;
$data['grade'] = $grade + 1;
}
/**
* Get the module data generator.
*
* @return core_competency_generator Competency data generator.
*/
protected function get_data_generator(): core_competency_generator {
return $this->componentdatagenerator;
}
}
+590
View File
@@ -0,0 +1,590 @@
<?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/>.
/**
* Competency data generator.
*
* @package core_competency
* @category test
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core_competency\competency;
use core_competency\competency_framework;
use core_competency\course_competency;
use core_competency\course_module_competency;
use core_competency\evidence;
use core_competency\external;
use core_competency\plan;
use core_competency\plan_competency;
use core_competency\related_competency;
use core_competency\template;
use core_competency\template_cohort;
use core_competency\template_competency;
use core_competency\user_competency;
use core_competency\user_competency_course;
use core_competency\user_competency_plan;
use core_competency\user_evidence;
use core_competency\user_evidence_competency;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/grade/grade_scale.php');
/**
* Competency data generator class.
*
* @package core_competency
* @category test
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_competency_generator extends component_generator_base {
/** @var int Number of created competencies. */
protected $competencycount = 0;
/** @var int Number of created frameworks. */
protected $frameworkcount = 0;
/** @var int Number of created plans. */
protected $plancount = 0;
/** @var int Number of created templates. */
protected $templatecount = 0;
/** @var int Number of created user_evidence. */
protected $userevidencecount = 0;
/** @var stdClass Scale that we might need. */
protected $scale;
/**
* Reset process.
*
* Do not call directly.
*
* @return void
*/
public function reset() {
$this->competencycount = 0;
$this->frameworkcount = 0;
$this->scale = null;
}
/**
* Create a new competency.
*
* @param array|stdClass $record
* @return competency
*/
public function create_competency($record = null) {
$this->competencycount++;
$i = $this->competencycount;
$record = (object) $record;
if (!isset($record->competencyframeworkid)) {
throw new coding_exception('The competencyframeworkid value is required.');
}
if (!isset($record->shortname)) {
$record->shortname = "Competency shortname $i";
}
if (!isset($record->idnumber)) {
$record->idnumber = "cmp{$i}";
}
if (!isset($record->description)) {
$record->description = "Competency $i description ";
}
if (!isset($record->descriptionformat)) {
$record->descriptionformat = FORMAT_HTML;
}
if (!isset($record->scaleconfiguration) && isset($record->scaleid)) {
$record->scaleconfiguration = json_encode($this->make_default_scale_configuration($record->scaleid));
}
if (isset($record->scaleconfiguration)
&& (is_array($record->scaleconfiguration) || is_object($record->scaleconfiguration))) {
// Conveniently encode the config.
$record->scaleconfiguration = json_encode($record->scaleconfiguration);
}
$competency = new competency(0, $record);
$competency->create();
return $competency;
}
/**
* Create a new framework.
*
* @param array|stdClass $record
* @return competency_framework
*/
public function create_framework($record = null) {
if (defined('BEHAT_TEST') && BEHAT_TEST) {
$generator = behat_util::get_data_generator();
} else {
$generator = phpunit_util::get_data_generator();
}
$this->frameworkcount++;
$i = $this->frameworkcount;
$record = (object) $record;
if (!isset($record->shortname)) {
$record->shortname = "Framework shortname $i";
}
if (!isset($record->idnumber)) {
$record->idnumber = "frm{$i}";
}
if (!isset($record->description)) {
$record->description = "Framework $i description ";
}
if (!isset($record->descriptionformat)) {
$record->descriptionformat = FORMAT_HTML;
}
if (!isset($record->visible)) {
$record->visible = 1;
}
if (!isset($record->scaleid)) {
if (isset($record->scaleconfiguration)) {
throw new coding_exception('Scale configuration must be provided with a scale.');
}
if (!$this->scale) {
$this->scale = $generator->create_scale(array('scale' => 'A,B,C,D'));
}
$record->scaleid = $this->scale->id;
}
if (!isset($record->scaleconfiguration)) {
$record->scaleconfiguration = json_encode($this->make_default_scale_configuration($record->scaleid));
}
if (is_array($record->scaleconfiguration) || is_object($record->scaleconfiguration)) {
// Conveniently encode the config.
$record->scaleconfiguration = json_encode($record->scaleconfiguration);
}
if (!isset($record->contextid)) {
$record->contextid = context_system::instance()->id;
}
$framework = new competency_framework(0, $record);
$framework->create();
return $framework;
}
/**
* Create a related competency.
*
* @param array|stdClass $record
* @return related_competency
*/
public function create_related_competency($record = null) {
$record = (object) $record;
if (!isset($record->competencyid)) {
throw new coding_exception('Property competencyid is required.');
}
if (!isset($record->relatedcompetencyid)) {
throw new coding_exception('Property relatedcompetencyid is required.');
}
$relation = related_competency::get_relation($record->competencyid, $record->relatedcompetencyid);
if ($relation->get('id')) {
throw new coding_exception('Relation already exists');
}
$relation->create();
return $relation;
}
/**
* Create a template.
*
* @param array|stdClass $record
* @return template
*/
public function create_template($record = null) {
$this->templatecount++;
$i = $this->templatecount;
$record = (object) $record;
if (!isset($record->shortname)) {
$record->shortname = "Template shortname $i";
}
if (!isset($record->description)) {
$record->description = "Template $i description ";
}
if (!isset($record->contextid)) {
$record->contextid = context_system::instance()->id;
}
$template = new template(0, $record);
$template->create();
return $template;
}
/**
* Create a template competency.
*
* @param array|stdClass $record
* @return template_competency
*/
public function create_template_competency($record = null) {
$record = (object) $record;
if (!isset($record->competencyid)) {
throw new coding_exception('Property competencyid is required.');
}
if (!isset($record->templateid)) {
throw new coding_exception('Property templateid is required.');
}
$relation = new template_competency(0, $record);
$relation->create();
return $relation;
}
/**
* Create a new user competency.
*
* @param array|stdClass $record
* @return user_competency
*/
public function create_user_competency($record = null) {
$record = (object) $record;
if (!isset($record->userid)) {
throw new coding_exception('The userid value is required.');
}
if (!isset($record->competencyid)) {
throw new coding_exception('The competencyid value is required.');
}
$usercompetency = new user_competency(0, $record);
$usercompetency->create();
return $usercompetency;
}
/**
* Create a new plan.
*
* @param array|stdClass $record
* @return plan
*/
public function create_plan($record = null) {
$this->plancount++;
$i = $this->plancount;
$record = (object) $record;
if (!isset($record->name)) {
$record->name = "Plan shortname $i";
}
if (!isset($record->description)) {
$record->description = "Plan $i description";
}
if (!isset($record->descriptionformat)) {
$record->descriptionformat = FORMAT_HTML;
}
if (!isset($record->userid)) {
throw new coding_exception('The userid value is required.');
}
$plan = new plan(0, $record);
$plan->create();
return $plan;
}
/**
* Create a new user competency course.
*
* @param array|stdClass $record
* @return user_competency_course
*/
public function create_user_competency_course($record = null) {
$record = (object) $record;
if (!isset($record->userid)) {
throw new coding_exception('The userid value is required.');
}
if (!isset($record->competencyid)) {
throw new coding_exception('The competencyid value is required.');
}
if (!isset($record->courseid)) {
throw new coding_exception('The courseid value is required.');
}
$usercompetencycourse = new user_competency_course(0, $record);
$usercompetencycourse->create();
return $usercompetencycourse;
}
/**
* Create a new user competency plan.
*
* @param array|stdClass $record
* @return user_competency_plan
*/
public function create_user_competency_plan($record = null) {
$record = (object) $record;
if (!isset($record->userid)) {
throw new coding_exception('The userid value is required.');
}
if (!isset($record->competencyid)) {
throw new coding_exception('The competencyid value is required.');
}
if (!isset($record->planid)) {
throw new coding_exception('The planid value is required.');
}
if (!isset($record->sortorder)) {
$record->sortorder = 0;
}
$usercompetencyplan = new user_competency_plan(0, $record);
$usercompetencyplan->create();
return $usercompetencyplan;
}
/**
* Create a new plan competency.
*
* @param array|stdClass $record
* @return plan_competency
*/
public function create_plan_competency($record = null) {
$record = (object) $record;
if (!isset($record->planid)) {
throw new coding_exception('The planid value is required.');
}
if (!isset($record->competencyid)) {
throw new coding_exception('The competencyid value is required.');
}
$plancompetency = new plan_competency(0, $record);
$plancompetency->create();
return $plancompetency;
}
/**
* Create a new template cohort.
*
* @param array|stdClass $record
* @return template_cohort
*/
public function create_template_cohort($record = null) {
$record = (object) $record;
if (!isset($record->templateid)) {
throw new coding_exception('The templateid value is required.');
}
if (!isset($record->cohortid)) {
throw new coding_exception('The cohortid value is required.');
}
$tplcohort = new template_cohort(0, $record);
$tplcohort->create();
return $tplcohort;
}
/**
* Create a new evidence.
*
* @param array|stdClass $record
* @return evidence
*/
public function create_evidence($record = null) {
$record = (object) $record;
if (!isset($record->usercompetencyid)) {
throw new coding_exception('The usercompetencyid value is required.');
}
if (!isset($record->action) && !isset($record->grade)) {
$record->action = evidence::ACTION_LOG;
}
if (!isset($record->action)) {
throw new coding_exception('The action value is required with a grade.');
}
if (!isset($record->contextid)) {
$record->contextid = context_system::instance()->id;
}
if (!isset($record->descidentifier)) {
$record->descidentifier = 'invalidevidencedesc';
}
if (!isset($record->desccomponent)) {
$record->desccomponent = 'core_competency';
}
$evidence = new evidence(0, $record);
$evidence->create();
return $evidence;
}
/**
* Create a new course competency.
*
* @param array|stdClass $record
* @return user_competency
*/
public function create_course_competency($record = null) {
$record = (object) $record;
if (!isset($record->courseid)) {
throw new coding_exception('The courseid value is required.');
}
if (!isset($record->competencyid)) {
throw new coding_exception('The competencyid value is required.');
}
$cc = new course_competency(0, $record);
$cc->create();
return $cc;
}
/**
* Create a new course module competency.
*
* @param array|stdClass $record
* @return course_module_competency
*/
public function create_course_module_competency($record = null) {
$record = (object) $record;
if (!isset($record->cmid)) {
throw new coding_exception('The cmid value is required.');
}
if (!isset($record->competencyid)) {
throw new coding_exception('The competencyid value is required.');
}
$cc = new course_module_competency(0, $record);
$cc->create();
return $cc;
}
/**
* Create a new user_evidence.
*
* @param array|stdClass $record
* @return evidence
*/
public function create_user_evidence($record = null) {
$this->userevidencecount++;
$i = $this->userevidencecount;
$record = (object) $record;
if (!isset($record->userid)) {
throw new coding_exception('The userid value is required.');
}
if (!isset($record->name)) {
$record->name = "Evidence $i name";
}
if (!isset($record->description)) {
$record->description = "Evidence $i description";
}
if (!isset($record->descriptionformat)) {
$record->descriptionformat = FORMAT_HTML;
}
$ue = new user_evidence(0, $record);
$ue->create();
return $ue;
}
/**
* Create a new user_evidence_comp.
*
* @param array|stdClass $record
* @return evidence
*/
public function create_user_evidence_competency($record = null) {
$record = (object) $record;
if (!isset($record->userevidenceid)) {
throw new coding_exception('The userevidenceid value is required.');
}
if (!isset($record->competencyid)) {
throw new coding_exception('The competencyid value is required.');
}
$uec = new user_evidence_competency(0, $record);
$uec->create();
return $uec;
}
/**
* Make a default scale configuration.
*
* The last and second-last item will be flagged proficient. The
* second-last item will be flagged as default.
*
* @param int $scaleid The scale ID.
* @return array Configuration as array.
*/
protected function make_default_scale_configuration($scaleid) {
$scale = grade_scale::fetch(array('id' => $scaleid));
$values = $scale->load_items();
foreach ($values as $key => $value) {
// Add a key (make the first value 1).
$values[$key] = array('id' => $key + 1, 'name' => $value);
}
if (count($values) < 2) {
throw new coding_exception('Please provide the scale configuration for one-item scales.');
}
$scaleconfig = array();
// Last item is proficient.
$item = array_pop($values);
array_unshift($scaleconfig, array(
'id' => $item['id'],
'proficient' => 1
));
// Second-last item is default and proficient.
$item = array_pop($values);
array_unshift($scaleconfig, array(
'id' => $item['id'],
'scaledefault' => 1,
'proficient' => 1
));
// Add the scale ID.
array_unshift($scaleconfig, array('scaleid' => $scaleid));
return $scaleconfig;
}
}
+237
View File
@@ -0,0 +1,237 @@
<?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/>.
namespace core_competency;
/**
* Tool LP data generator testcase.
*
* @package core_competency
* @category test
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class generator_test extends \advanced_testcase {
public function test_create_framework(): void {
$this->resetAfterTest(true);
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$this->assertEquals(0, competency_framework::count_records());
$framework = $lpg->create_framework();
$framework = $lpg->create_framework();
$this->assertEquals(2, competency_framework::count_records());
$this->assertInstanceOf('\core_competency\competency_framework', $framework);
}
public function test_create_competency(): void {
$this->resetAfterTest(true);
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$framework = $lpg->create_framework();
$this->assertEquals(0, competency::count_records());
$competency = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$competency = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$this->assertEquals(2, competency::count_records());
$this->assertInstanceOf('\core_competency\competency', $competency);
}
public function test_create_related_competency(): void {
$this->resetAfterTest(true);
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$framework = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$this->assertEquals(0, related_competency::count_records());
$rc = $lpg->create_related_competency(array('competencyid' => $c1->get('id'), 'relatedcompetencyid' => $c2->get('id')));
$rc = $lpg->create_related_competency(array('competencyid' => $c2->get('id'), 'relatedcompetencyid' => $c3->get('id')));
$this->assertEquals(2, related_competency::count_records());
$this->assertInstanceOf('\core_competency\related_competency', $rc);
}
public function test_create_plan(): void {
$this->resetAfterTest(true);
$user = $this->getDataGenerator()->create_user();
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$this->assertEquals(0, plan::count_records());
$plan = $lpg->create_plan(array('userid' => $user->id));
$this->assertEquals(1, plan::count_records());
$this->assertInstanceOf('\core_competency\plan', $plan);
}
public function test_create_user_competency(): void {
$this->resetAfterTest(true);
$user = $this->getDataGenerator()->create_user();
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$framework = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$this->assertEquals(0, user_competency::count_records());
$rc = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c1->get('id')));
$rc = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c2->get('id')));
$this->assertEquals(2, user_competency::count_records());
$this->assertInstanceOf('\core_competency\user_competency', $rc);
}
public function test_create_user_competency_plan(): void {
$this->resetAfterTest(true);
$user = $this->getDataGenerator()->create_user();
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$framework = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$plan = $lpg->create_plan(array('userid' => $user->id));
$this->assertEquals(0, user_competency_plan::count_records());
$ucp = $lpg->create_user_competency_plan(array(
'userid' => $user->id,
'competencyid' => $c1->get('id'),
'planid' => $plan->get('id')
));
$ucp = $lpg->create_user_competency_plan(array(
'userid' => $user->id,
'competencyid' => $c2->get('id'),
'planid' => $plan->get('id')
));
$this->assertEquals(2, user_competency_plan::count_records());
$this->assertInstanceOf('\core_competency\user_competency_plan', $ucp);
}
public function test_create_template(): void {
$this->resetAfterTest(true);
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$this->assertEquals(0, template::count_records());
$template = $lpg->create_template();
$template = $lpg->create_template();
$this->assertEquals(2, template::count_records());
$this->assertInstanceOf('\core_competency\template', $template);
}
public function test_create_template_competency(): void {
$this->resetAfterTest(true);
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$this->assertEquals(0, template_competency::count_records());
$framework = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$template = $lpg->create_template();
$relation = $lpg->create_template_competency(array('competencyid' => $c1->get('id'), 'templateid' => $template->get('id')));
$relation = $lpg->create_template_competency(array('competencyid' => $c2->get('id'), 'templateid' => $template->get('id')));
$this->assertEquals(2, template_competency::count_records());
$this->assertInstanceOf('\core_competency\template_competency', $relation);
}
public function test_create_plan_competency(): void {
$this->resetAfterTest(true);
$user = $this->getDataGenerator()->create_user();
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$framework = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$plan = $lpg->create_plan(array('userid' => $user->id));
$pc1 = $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c1->get('id')));
$pc2 = $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c2->get('id')));
$this->assertEquals(2, plan_competency::count_records());
$this->assertInstanceOf('\core_competency\plan_competency', $pc1);
$this->assertInstanceOf('\core_competency\plan_competency', $pc2);
$this->assertEquals($plan->get('id'), $pc1->get('planid'));
}
public function test_create_template_cohort(): void {
$this->resetAfterTest(true);
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$c1 = $this->getDataGenerator()->create_cohort();
$c2 = $this->getDataGenerator()->create_cohort();
$t1 = $lpg->create_template();
$this->assertEquals(0, template_cohort::count_records());
$tc = $lpg->create_template_cohort(array('templateid' => $t1->get('id'), 'cohortid' => $c1->id));
$this->assertEquals(1, template_cohort::count_records());
$tc = $lpg->create_template_cohort(array('templateid' => $t1->get('id'), 'cohortid' => $c2->id));
$this->assertEquals(2, template_cohort::count_records());
$this->assertInstanceOf('\core_competency\template_cohort', $tc);
}
public function test_create_evidence(): void {
$this->resetAfterTest(true);
$user = $this->getDataGenerator()->create_user();
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$framework = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$rc1 = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c1->get('id')));
$rc2 = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c2->get('id')));
$e = $lpg->create_evidence(array('usercompetencyid' => $rc1->get('id')));
$e = $lpg->create_evidence(array('usercompetencyid' => $rc2->get('id')));
$this->assertEquals(2, evidence::count_records());
$this->assertInstanceOf('\core_competency\evidence', $e);
}
public function test_create_course_competency(): void {
$this->resetAfterTest(true);
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$course1 = $this->getDataGenerator()->create_course();
$course2 = $this->getDataGenerator()->create_course();
$framework = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$this->assertEquals(0, course_competency::count_records());
$rc = $lpg->create_course_competency(array('competencyid' => $c1->get('id'), 'courseid' => $course1->id));
$rc = $lpg->create_course_competency(array('competencyid' => $c2->get('id'), 'courseid' => $course1->id));
$this->assertEquals(2, course_competency::count_records(array('courseid' => $course1->id)));
$this->assertEquals(0, course_competency::count_records(array('courseid' => $course2->id)));
$rc = $lpg->create_course_competency(array('competencyid' => $c3->get('id'), 'courseid' => $course2->id));
$this->assertEquals(1, course_competency::count_records(array('courseid' => $course2->id)));
$this->assertInstanceOf('\core_competency\course_competency', $rc);
}
public function test_create_course_module_competency(): void {
$this->resetAfterTest(true);
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$course1 = $this->getDataGenerator()->create_course();
$cm1 = $this->getDataGenerator()->create_module('forum', array('course' => $course1->id));
$cm2 = $this->getDataGenerator()->create_module('forum', array('course' => $course1->id));
$framework = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
$this->assertEquals(0, course_module_competency::count_records());
$rc = $lpg->create_course_module_competency(array('competencyid' => $c1->get('id'), 'cmid' => $cm1->cmid));
$rc = $lpg->create_course_module_competency(array('competencyid' => $c2->get('id'), 'cmid' => $cm1->cmid));
$this->assertEquals(2, course_module_competency::count_records(array('cmid' => $cm1->cmid)));
$this->assertEquals(0, course_module_competency::count_records(array('cmid' => $cm2->cmid)));
$rc = $lpg->create_course_module_competency(array('competencyid' => $c3->get('id'), 'cmid' => $cm2->cmid));
$this->assertEquals(1, course_module_competency::count_records(array('cmid' => $cm2->cmid)));
$this->assertInstanceOf('\core_competency\course_module_competency', $rc);
}
}
+241
View File
@@ -0,0 +1,241 @@
<?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/>.
namespace core_competency;
/**
* Hook tests.
*
* @package core_competency
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class hooks_test extends \advanced_testcase {
public function test_hook_course_deleted(): void {
$this->resetAfterTest();
$dg = $this->getDataGenerator();
$ccg = $dg->get_plugin_generator('core_competency');
$u1 = $dg->create_user();
$framework = $ccg->create_framework();
$comp1 = $ccg->create_competency(['competencyframeworkid' => $framework->get('id')]);
$comp2 = $ccg->create_competency(['competencyframeworkid' => $framework->get('id')]);
$c1 = $dg->create_course();
$cc1a = $ccg->create_course_competency(['competencyid' => $comp1->get('id'), 'courseid' => $c1->id]);
$cc1b = $ccg->create_course_competency(['competencyid' => $comp2->get('id'), 'courseid' => $c1->id]);
$assign1a = $dg->create_module('assign', ['course' => $c1]);
$assign1b = $dg->create_module('assign', ['course' => $c1]);
$cmc1a = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1a->cmid]);
$cmc1b = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1b->cmid]);
$ucc1a = $ccg->create_user_competency_course(['competencyid' => $comp1->get('id'), 'courseid' => $c1->id,
'userid' => $u1->id]);
$ucc1b = $ccg->create_user_competency_course(['competencyid' => $comp2->get('id'), 'courseid' => $c1->id,
'userid' => $u1->id]);
$c2 = $dg->create_course();
$cc2a = $ccg->create_course_competency(['competencyid' => $comp1->get('id'), 'courseid' => $c2->id]);
$cc2b = $ccg->create_course_competency(['competencyid' => $comp2->get('id'), 'courseid' => $c2->id]);
$assign2a = $dg->create_module('assign', ['course' => $c2]);
$assign2b = $dg->create_module('assign', ['course' => $c2]);
$cmc2a = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign2a->cmid]);
$cmc2b = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign2b->cmid]);
$ucc2a = $ccg->create_user_competency_course(['competencyid' => $comp1->get('id'), 'courseid' => $c2->id,
'userid' => $u1->id]);
$ucc2b = $ccg->create_user_competency_course(['competencyid' => $comp2->get('id'), 'courseid' => $c2->id,
'userid' => $u1->id]);
delete_course($c1, false);
$this->assertEquals(0, course_competency::count_records(['courseid' => $c1->id]));
$this->assertEquals(2, course_competency::count_records(['courseid' => $c2->id]));
$this->assertEquals(0, course_module_competency::count_records(['cmid' => $assign1a->cmid]));
$this->assertEquals(0, course_module_competency::count_records(['cmid' => $assign1b->cmid]));
$this->assertEquals(1, course_module_competency::count_records(['cmid' => $assign2a->cmid]));
$this->assertEquals(1, course_module_competency::count_records(['cmid' => $assign2b->cmid]));
$this->assertEquals(0, user_competency_course::count_records(['courseid' => $c1->id, 'userid' => $u1->id]));
$this->assertEquals(2, user_competency_course::count_records(['courseid' => $c2->id, 'userid' => $u1->id]));
}
public function test_hook_course_module_deleted(): void {
$this->resetAfterTest();
$dg = $this->getDataGenerator();
$ccg = $dg->get_plugin_generator('core_competency');
$u1 = $dg->create_user();
$framework = $ccg->create_framework();
$comp1 = $ccg->create_competency(['competencyframeworkid' => $framework->get('id')]);
$comp2 = $ccg->create_competency(['competencyframeworkid' => $framework->get('id')]);
$c1 = $dg->create_course();
$cc1a = $ccg->create_course_competency(['competencyid' => $comp1->get('id'), 'courseid' => $c1->id]);
$cc1b = $ccg->create_course_competency(['competencyid' => $comp2->get('id'), 'courseid' => $c1->id]);
$assign1a = $dg->create_module('assign', ['course' => $c1]);
$assign1b = $dg->create_module('assign', ['course' => $c1]);
$cmc1a = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1a->cmid]);
$cmc1b = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1b->cmid]);
$ucc1a = $ccg->create_user_competency_course(['competencyid' => $comp1->get('id'), 'courseid' => $c1->id,
'userid' => $u1->id]);
$ucc1b = $ccg->create_user_competency_course(['competencyid' => $comp2->get('id'), 'courseid' => $c1->id,
'userid' => $u1->id]);
$c2 = $dg->create_course();
$cc2a = $ccg->create_course_competency(['competencyid' => $comp1->get('id'), 'courseid' => $c2->id]);
$cc2b = $ccg->create_course_competency(['competencyid' => $comp2->get('id'), 'courseid' => $c2->id]);
$assign2a = $dg->create_module('assign', ['course' => $c2]);
$assign2b = $dg->create_module('assign', ['course' => $c2]);
$cmc2a = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign2a->cmid]);
$cmc2b = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign2b->cmid]);
$ucc2a = $ccg->create_user_competency_course(['competencyid' => $comp1->get('id'), 'courseid' => $c2->id,
'userid' => $u1->id]);
$ucc2b = $ccg->create_user_competency_course(['competencyid' => $comp2->get('id'), 'courseid' => $c2->id,
'userid' => $u1->id]);
course_delete_module($assign1b->cmid);
$this->assertEquals(2, course_competency::count_records(['courseid' => $c1->id]));
$this->assertEquals(1, course_module_competency::count_records(['cmid' => $assign1a->cmid]));
$this->assertEquals(0, course_module_competency::count_records(['cmid' => $assign1b->cmid]));
$this->assertEquals(2, user_competency_course::count_records(['courseid' => $c1->id]));
$this->assertEquals(2, course_competency::count_records(['courseid' => $c2->id]));
$this->assertEquals(1, course_module_competency::count_records(['cmid' => $assign2a->cmid]));
$this->assertEquals(1, course_module_competency::count_records(['cmid' => $assign2b->cmid]));
$this->assertEquals(2, user_competency_course::count_records(['courseid' => $c2->id, 'userid' => $u1->id]));
}
public function test_hook_course_reset_competency_ratings(): void {
$this->resetAfterTest();
$dg = $this->getDataGenerator();
$ccg = $dg->get_plugin_generator('core_competency');
$u1 = $dg->create_user();
$framework = $ccg->create_framework();
$comp1 = $ccg->create_competency(['competencyframeworkid' => $framework->get('id')]);
$comp2 = $ccg->create_competency(['competencyframeworkid' => $framework->get('id')]);
$c1 = $dg->create_course();
$cc1a = $ccg->create_course_competency(['competencyid' => $comp1->get('id'), 'courseid' => $c1->id]);
$cc1b = $ccg->create_course_competency(['competencyid' => $comp2->get('id'), 'courseid' => $c1->id]);
$assign1a = $dg->create_module('assign', ['course' => $c1]);
$assign1b = $dg->create_module('assign', ['course' => $c1]);
$cmc1a = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1a->cmid]);
$cmc1b = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1b->cmid]);
$ucc1a = $ccg->create_user_competency_course(['competencyid' => $comp1->get('id'), 'courseid' => $c1->id,
'userid' => $u1->id]);
$ucc1b = $ccg->create_user_competency_course(['competencyid' => $comp2->get('id'), 'courseid' => $c1->id,
'userid' => $u1->id]);
$c2 = $dg->create_course();
$cc2a = $ccg->create_course_competency(['competencyid' => $comp1->get('id'), 'courseid' => $c2->id]);
$cc2b = $ccg->create_course_competency(['competencyid' => $comp2->get('id'), 'courseid' => $c2->id]);
$assign2a = $dg->create_module('assign', ['course' => $c2]);
$assign2b = $dg->create_module('assign', ['course' => $c2]);
$cmc2a = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign2a->cmid]);
$cmc2b = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign2b->cmid]);
$ucc2a = $ccg->create_user_competency_course(['competencyid' => $comp1->get('id'), 'courseid' => $c2->id,
'userid' => $u1->id]);
$ucc2b = $ccg->create_user_competency_course(['competencyid' => $comp2->get('id'), 'courseid' => $c2->id,
'userid' => $u1->id]);
reset_course_userdata((object) ['id' => $c1->id, 'reset_competency_ratings' => true]);
$this->assertEquals(2, course_competency::count_records(['courseid' => $c1->id]));
$this->assertEquals(2, course_competency::count_records(['courseid' => $c2->id]));
$this->assertEquals(1, course_module_competency::count_records(['cmid' => $assign1a->cmid]));
$this->assertEquals(1, course_module_competency::count_records(['cmid' => $assign1b->cmid]));
$this->assertEquals(1, course_module_competency::count_records(['cmid' => $assign2a->cmid]));
$this->assertEquals(1, course_module_competency::count_records(['cmid' => $assign2b->cmid]));
$this->assertEquals(0, user_competency_course::count_records(['courseid' => $c1->id, 'userid' => $u1->id]));
$this->assertEquals(2, user_competency_course::count_records(['courseid' => $c2->id, 'userid' => $u1->id]));
}
public function test_hook_cohort_deleted(): void {
$this->resetAfterTest();
$this->setAdminUser();
$datagen = $this->getDataGenerator();
$corecompgen = $datagen->get_plugin_generator('core_competency');
$c1 = $datagen->create_cohort();
$c2 = $datagen->create_cohort();
$t1 = $corecompgen->create_template();
$t2 = $corecompgen->create_template();
// Create the template cohorts.
api::create_template_cohort($t1->get('id'), $c1->id);
api::create_template_cohort($t1->get('id'), $c2->id);
api::create_template_cohort($t2->get('id'), $c1->id);
// Check that the association was made.
$this->assertEquals(2, \core_competency\template_cohort::count_records(array('templateid' => $t1->get('id'))));
$this->assertEquals(1, \core_competency\template_cohort::count_records(array('templateid' => $t2->get('id'))));
// Delete the first cohort.
cohort_delete_cohort($c1);
// Check that the association was removed.
$this->assertEquals(1, \core_competency\template_cohort::count_records(array('templateid' => $t1->get('id'))));
$this->assertEquals(0, \core_competency\template_cohort::count_records(array('templateid' => $t2->get('id'))));
}
public function test_hook_user_deleted(): void {
$this->resetAfterTest();
$dg = $this->getDataGenerator();
$ccg = $dg->get_plugin_generator('core_competency');
$u1 = $dg->create_user();
$framework = $ccg->create_framework();
$comp1 = $ccg->create_competency(['competencyframeworkid' => $framework->get('id')]);
$comp2 = $ccg->create_competency(['competencyframeworkid' => $framework->get('id')]);
$c1 = $dg->create_course();
$cc1a = $ccg->create_course_competency(['competencyid' => $comp1->get('id'), 'courseid' => $c1->id]);
$cc1b = $ccg->create_course_competency(['competencyid' => $comp2->get('id'), 'courseid' => $c1->id]);
$assign1a = $dg->create_module('assign', ['course' => $c1]);
$assign1b = $dg->create_module('assign', ['course' => $c1]);
$cmc1a = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1a->cmid]);
$cmc1b = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1b->cmid]);
$ucc1a = $ccg->create_user_competency_course(['competencyid' => $comp1->get('id'), 'courseid' => $c1->id,
'userid' => $u1->id]);
$ucc1b = $ccg->create_user_competency_course(['competencyid' => $comp2->get('id'), 'courseid' => $c1->id,
'userid' => $u1->id]);
$c2 = $dg->create_course();
$cc2a = $ccg->create_course_competency(['competencyid' => $comp1->get('id'), 'courseid' => $c2->id]);
$cc2b = $ccg->create_course_competency(['competencyid' => $comp2->get('id'), 'courseid' => $c2->id]);
$assign2a = $dg->create_module('assign', ['course' => $c2]);
$assign2b = $dg->create_module('assign', ['course' => $c2]);
$cmc2a = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign2a->cmid]);
$cmc2b = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign2b->cmid]);
$ucc2a = $ccg->create_user_competency_course(['competencyid' => $comp1->get('id'), 'courseid' => $c2->id,
'userid' => $u1->id]);
$ucc2b = $ccg->create_user_competency_course(['competencyid' => $comp2->get('id'), 'courseid' => $c2->id,
'userid' => $u1->id]);
reset_course_userdata((object) ['id' => $c1->id, 'reset_competency_ratings' => true]);
delete_user($u1);
// Assert the records don't exist anymore.
$this->assertEquals(0, user_competency_course::count_records(['courseid' => $c1->id, 'userid' => $u1->id]));
}
}
+319
View File
@@ -0,0 +1,319 @@
<?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/>.
/**
* Lib tests.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_competency;
defined('MOODLE_INTERNAL') || die();
use core_competency\plan;
use core_competency\url;
use core_competency\user_competency;
global $CFG;
/**
* Lib testcase.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class lib_test extends \advanced_testcase {
public function test_comment_add_user_competency(): void {
global $DB, $PAGE;
$this->resetAfterTest();
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$u1 = $dg->create_user(['picture' => 1]);
$u2 = $dg->create_user();
$u3 = $dg->create_user();
$reviewerroleid = $dg->create_role();
assign_capability('moodle/competency:planview', CAP_ALLOW, $reviewerroleid, \context_system::instance()->id, true);
assign_capability('moodle/competency:usercompetencycomment', CAP_ALLOW, $reviewerroleid,
\context_system::instance()->id, true);
$dg->role_assign($reviewerroleid, $u2->id, \context_user::instance($u1->id));
$dg->role_assign($reviewerroleid, $u3->id, \context_user::instance($u1->id));
accesslib_clear_all_caches_for_unit_testing();
$f1 = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); // In 1 plan.
$c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); // In 2 plans.
$c3 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); // Orphan.
$p1 = $lpg->create_plan(array('userid' => $u1->id));
$lpg->create_plan_competency(array('planid' => $p1->get('id'), 'competencyid' => $c1->get('id')));
$lpg->create_plan_competency(array('planid' => $p1->get('id'), 'competencyid' => $c2->get('id')));
$p2 = $lpg->create_plan(array('userid' => $u1->id));
$lpg->create_plan_competency(array('planid' => $p2->get('id'), 'competencyid' => $c2->get('id')));
$DB->set_field(plan::TABLE, 'timemodified', 1, array('id' => $p1->get('id'))); // Make plan 1 appear as old.
$p1->read();
$uc1 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c1->get('id'),
'status' => user_competency::STATUS_IN_REVIEW, 'reviewerid' => $u2->id));
$uc2 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c2->get('id'),
'status' => user_competency::STATUS_IN_REVIEW, 'reviewerid' => $u2->id));
$uc3 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c3->get('id'),
'status' => user_competency::STATUS_IN_REVIEW, 'reviewerid' => $u2->id));
// Post a comment for the user competency being in one plan. The reviewer is messaged.
$this->setUser($u1);
$comment = $uc1->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('Hello world!');
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(1, $messages);
$message = array_pop($messages);
$expectedurlname = $c1->get('shortname');
$expectedurl = url::user_competency_in_plan($u1->id, $c1->get('id'), $p1->get('id'));
$this->assertEquals(\core_user::get_noreply_user()->id, $message->useridfrom);
$this->assertEquals($u2->id, $message->useridto);
$this->assertTrue(strpos($message->fullmessage, 'Hello world!') !== false);
$this->assertTrue(strpos($message->fullmessagehtml, 'Hello world!') !== false);
$this->assertEquals(FORMAT_MOODLE, $message->fullmessageformat);
$this->assertEquals($expectedurl->out(false), $message->contexturl);
$this->assertEquals($expectedurlname, $message->contexturlname);
// Test customdata.
$customdata = json_decode($message->customdata);
$this->assertObjectHasProperty('notificationiconurl', $customdata);
$this->assertStringContainsString('tokenpluginfile.php', $customdata->notificationiconurl);
$userpicture = new \user_picture($u1);
$userpicture->size = 1; // Use f1 size.
$userpicture->includetoken = $u2->id;
$this->assertEquals($userpicture->get_url($PAGE)->out(false), $customdata->notificationiconurl);
// Reviewer posts a comment for the user competency being in two plans. Owner is messaged.
$this->setUser($u2);
$comment = $uc2->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('Hello world!');
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(1, $messages);
$message = array_pop($messages);
$expectedurlname = $c2->get('shortname');
$expectedurl = url::user_competency_in_plan($u1->id, $c2->get('id'), $p2->get('id'));
$this->assertEquals(\core_user::get_noreply_user()->id, $message->useridfrom);
$this->assertEquals($u1->id, $message->useridto);
$this->assertTrue(strpos($message->fullmessage, 'Hello world!') !== false);
$this->assertTrue(strpos($message->fullmessagehtml, 'Hello world!') !== false);
$this->assertEquals(FORMAT_MOODLE, $message->fullmessageformat);
$this->assertEquals($expectedurl->out(false), $message->contexturl);
$this->assertEquals($expectedurlname, $message->contexturlname);
// Reviewer posts a comment for the user competency being in no plans. User is messaged.
$this->setUser($u2);
$comment = $uc3->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('Hello world!');
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(1, $messages);
$message = array_pop($messages);
$expectedurlname = get_string('userplans', 'core_competency');
$expectedurl = url::plans($u1->id);
$this->assertEquals(\core_user::get_noreply_user()->id, $message->useridfrom);
$this->assertEquals($u1->id, $message->useridto);
$this->assertTrue(strpos($message->fullmessage, 'Hello world!') !== false);
$this->assertTrue(strpos($message->fullmessagehtml, 'Hello world!') !== false);
$this->assertEquals(FORMAT_MOODLE, $message->fullmessageformat);
$this->assertEquals($expectedurl->out(false), $message->contexturl);
$this->assertEquals($expectedurlname, $message->contexturlname);
// A comment is posted by another user, reviewer and owner are messaged.
$this->setUser($u3);
$comment = $uc3->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('Hello world!');
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(2, $messages);
$message1 = array_shift($messages);
$message2 = array_shift($messages);
$this->assertEquals(\core_user::get_noreply_user()->id, $message->useridfrom);
$this->assertEquals($u1->id, $message1->useridto);
$this->assertEquals(\core_user::get_noreply_user()->id, $message->useridfrom);
$this->assertEquals($u2->id, $message2->useridto);
// A comment is posted in HTML.
$this->setUser($u2);
$comment = $uc3->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('<em>Hello world!</em>', FORMAT_HTML);
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(1, $messages);
$message = array_pop($messages);
$expectedurlname = get_string('userplans', 'core_competency');
$expectedurl = url::plans($u1->id);
$this->assertEquals(\core_user::get_noreply_user()->id, $message->useridfrom);
$this->assertEquals($u1->id, $message->useridto);
$this->assertTrue(strpos($message->fullmessage, '<em>Hello world!</em>') !== false);
$this->assertTrue(strpos($message->fullmessagehtml, '<em>Hello world!</em>') !== false);
$this->assertEquals(FORMAT_HTML, $message->fullmessageformat);
$this->assertEquals($expectedurl->out(false), $message->contexturl);
$this->assertEquals($expectedurlname, $message->contexturlname);
}
/**
* Commenting on a plan.
*/
public function test_comment_add_plan(): void {
$this->resetAfterTest();
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$u3 = $dg->create_user();
$userroleid = $dg->create_role();
$reviewerroleid = $dg->create_role();
assign_capability('moodle/competency:planviewowndraft', CAP_ALLOW, $userroleid, \context_system::instance()->id, true);
assign_capability('moodle/competency:planviewown', CAP_ALLOW, $userroleid, \context_system::instance()->id, true);
assign_capability('moodle/competency:planviewdraft', CAP_ALLOW, $reviewerroleid, \context_system::instance()->id, true);
assign_capability('moodle/competency:planmanage', CAP_ALLOW, $reviewerroleid, \context_system::instance()->id, true);
assign_capability('moodle/competency:plancomment', CAP_ALLOW, $reviewerroleid, \context_system::instance()->id, true);
$dg->role_assign($userroleid, $u1->id, \context_user::instance($u1->id));
$dg->role_assign($reviewerroleid, $u2->id, \context_user::instance($u1->id));
$dg->role_assign($reviewerroleid, $u3->id, \context_system::instance());
accesslib_clear_all_caches_for_unit_testing();
$p1 = $lpg->create_plan(array('userid' => $u1->id));
// Post a comment in own plan, no reviewer. Nobody is messaged.
$this->setUser($u1);
$comment = $p1->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('Hello world!');
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(0, $messages);
// Post a comment in plan as someone else, no reviewer. The owner is messages.
$this->setUser($u3);
$comment = $p1->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('Hello world!');
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(1, $messages);
$message = array_pop($messages);
$this->assertEquals(\core_user::get_noreply_user()->id, $message->useridfrom);
$this->assertEquals($u1->id, $message->useridto);
// Test customdata.
$customdata = json_decode($message->customdata);
$this->assertObjectHasProperty('notificationiconurl', $customdata);
// Post a comment in a plan with reviewer. The reviewer is messaged.
$p1->set('reviewerid', $u2->id);
$p1->update();
$this->setUser($u1);
$comment = $p1->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('Hello world!');
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(1, $messages);
$message = array_pop($messages);
$this->assertEquals(\core_user::get_noreply_user()->id, $message->useridfrom);
$this->assertEquals($u2->id, $message->useridto);
// Post a comment as reviewer in a plan being reviewed. The owner is messaged.
$p1->set('reviewerid', $u2->id);
$p1->update();
$this->setUser($u2);
$comment = $p1->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('Hello world!');
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(1, $messages);
$message = array_pop($messages);
$this->assertEquals(\core_user::get_noreply_user()->id, $message->useridfrom);
$this->assertEquals($u1->id, $message->useridto);
// Post a comment as someone else in a plan being reviewed. The owner and reviewer are messaged.
$p1->set('reviewerid', $u2->id);
$p1->update();
$this->setUser($u3);
$comment = $p1->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('Hello world!');
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(2, $messages);
$message1 = array_shift($messages);
$message2 = array_shift($messages);
$this->assertEquals(\core_user::get_noreply_user()->id, $message1->useridfrom);
$this->assertEquals($u1->id, $message1->useridto);
$this->assertEquals(\core_user::get_noreply_user()->id, $message2->useridfrom);
$this->assertEquals($u2->id, $message2->useridto);
$p1->set('reviewerid', null);
$p1->update();
// Test message content.
$this->setUser($u3);
$comment = $p1->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('Hello world!');
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(1, $messages);
$message = array_pop($messages);
$expectedurlname = $p1->get('name');
$expectedurl = url::plan($p1->get('id'));
$this->assertTrue(strpos($message->fullmessage, 'Hello world!') !== false);
$this->assertTrue(strpos($message->fullmessagehtml, 'Hello world!') !== false);
$this->assertEquals(FORMAT_MOODLE, $message->fullmessageformat);
$this->assertEquals($expectedurl->out(false), $message->contexturl);
$this->assertEquals($expectedurlname, $message->contexturlname);
// Test message content as HTML.
$this->setUser($u3);
$comment = $p1->get_comment_object();
$sink = $this->redirectMessages();
$comment->add('<em>Hello world!</em>', FORMAT_HTML);
$messages = $sink->get_messages();
$sink->close();
$this->assertCount(1, $messages);
$message = array_pop($messages);
$expectedurlname = $p1->get('name');
$expectedurl = url::plan($p1->get('id'));
$this->assertTrue(strpos($message->fullmessage, '<em>Hello world!</em>') !== false);
$this->assertTrue(strpos($message->fullmessagehtml, '<em>Hello world!</em>') !== false);
$this->assertEquals(FORMAT_HTML, $message->fullmessageformat);
$this->assertEquals($expectedurl->out(false), $message->contexturl);
$this->assertEquals($expectedurlname, $message->contexturlname);
}
}
@@ -0,0 +1,150 @@
<?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/>.
namespace core_competency;
use core_competency\external\performance_helper;
/**
* Performance helper testcase.
*
* @package core_competency
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class performance_helper_test extends \advanced_testcase {
public function test_get_context_from_competency(): void {
global $DB;
$this->resetAfterTest(true);
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$cat1 = $dg->create_category();
$framework = $lpg->create_framework();
$competency = $lpg->create_competency(['competencyframeworkid' => $framework->get('id')]);
$competency2 = $lpg->create_competency(['competencyframeworkid' => $framework->get('id')]);
$context = $competency->get_context();
$helper = new performance_helper();
$initdbqueries = $DB->perf_get_queries();
// Confirm that subsequent calls return a cached object.
// Note that here we check that the framework is not loaded more than once.
// The context objects are already cached in the context layer.
$firstruncontext = $helper->get_context_from_competency($competency);
$dbqueries = $DB->perf_get_queries();
$this->assertSame($context, $firstruncontext);
$this->assertNotEquals($initdbqueries, $dbqueries);
$secondruncontext = $helper->get_context_from_competency($competency);
$this->assertSame($context, $secondruncontext);
$this->assertSame($firstruncontext, $secondruncontext);
$this->assertEquals($DB->perf_get_queries(), $dbqueries);
$thirdruncontext = $helper->get_context_from_competency($competency2);
$this->assertSame($context, $thirdruncontext);
$this->assertSame($secondruncontext, $thirdruncontext);
$this->assertEquals($DB->perf_get_queries(), $dbqueries);
}
public function test_get_framework_from_competency(): void {
global $DB;
$this->resetAfterTest(true);
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$cat1 = $dg->create_category();
$framework1 = $lpg->create_framework();
$comp1a = $lpg->create_competency(['competencyframeworkid' => $framework1->get('id')]);
$comp1b = $lpg->create_competency(['competencyframeworkid' => $framework1->get('id')]);
$framework2 = $lpg->create_framework();
$comp2a = $lpg->create_competency(['competencyframeworkid' => $framework2->get('id')]);
$helper = new performance_helper();
$initdbqueries = $DB->perf_get_queries();
// Confirm that we get the right framework, and that subsequent calls
// do not trigger DB queries, even for other competencies.
$firstrunframework = $helper->get_framework_from_competency($comp1a);
$firstrundbqueries = $DB->perf_get_queries();
$this->assertNotEquals($initdbqueries, $firstrundbqueries);
$this->assertEquals($framework1, $firstrunframework);
$this->assertNotSame($framework1, $firstrunframework);
$secondrunframework = $helper->get_framework_from_competency($comp1b);
$this->assertEquals($firstrundbqueries, $DB->perf_get_queries());
$this->assertEquals($framework1, $secondrunframework);
$this->assertSame($firstrunframework, $secondrunframework);
$thirdrunframework = $helper->get_framework_from_competency($comp1a);
$this->assertEquals($firstrundbqueries, $DB->perf_get_queries());
$this->assertEquals($framework1, $thirdrunframework);
$this->assertSame($firstrunframework, $thirdrunframework);
// Fetch another framework.
$fourthrunframework = $helper->get_framework_from_competency($comp2a);
$fourthrundbqueries = $DB->perf_get_queries();
$this->assertNotEquals($firstrundbqueries, $fourthrundbqueries);
$this->assertEquals($framework2, $fourthrunframework);
$this->assertNotSame($framework2, $fourthrunframework);
$fifthrunframework = $helper->get_framework_from_competency($comp2a);
$this->assertEquals($fourthrundbqueries, $DB->perf_get_queries());
$this->assertEquals($framework2, $fifthrunframework);
$this->assertSame($fourthrunframework, $fifthrunframework);
}
public function test_get_scale_from_competency(): void {
global $DB;
$this->resetAfterTest(true);
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$scale1 = $dg->create_scale();
$scale2 = $dg->create_scale();
$cat1 = $dg->create_category();
$framework1 = $lpg->create_framework(['scaleid' => $scale1->id]);
$comp1 = $lpg->create_competency(['competencyframeworkid' => $framework1->get('id')]);
$comp2 = $lpg->create_competency(['competencyframeworkid' => $framework1->get('id'), 'scaleid' => $scale2->id]);
$comp3 = $lpg->create_competency(['competencyframeworkid' => $framework1->get('id')]);
$helper = new performance_helper();
$initdbqueries = $DB->perf_get_queries();
// Get the first scale.
$firstrunscale = $helper->get_scale_from_competency($comp1);
$firstrundbqueries = $DB->perf_get_queries();
$this->assertNotEquals($initdbqueries, $firstrundbqueries);
$this->assertEquals($scale1, $firstrunscale->get_record_data());
$secondrunscale = $helper->get_scale_from_competency($comp3);
$this->assertEquals($firstrundbqueries, $DB->perf_get_queries());
$this->assertSame($firstrunscale, $secondrunscale);
// Another scale, and its subsequent calls.
$thirdrunscale = $helper->get_scale_from_competency($comp2);
$thirddbqueries = $DB->perf_get_queries();
$this->assertNotEquals($firstrundbqueries, $thirddbqueries);
$this->assertEquals($scale2, $thirdrunscale->get_record_data());
$this->assertSame($thirdrunscale, $helper->get_scale_from_competency($comp2));
$this->assertEquals($thirddbqueries, $DB->perf_get_queries());
}
}
+562
View File
@@ -0,0 +1,562 @@
<?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/>.
namespace core_competency;
/**
* Plan persistent testcase.
*
* @package core_competency
* @copyright 2015 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class plan_test extends \advanced_testcase {
public function test_can_manage_user(): void {
$this->resetAfterTest(true);
$manage = create_role('Manage', 'manage', 'Plan manager');
$manageown = create_role('Manageown', 'manageown', 'Own plan manager');
$u1 = $this->getDataGenerator()->create_user();
$u2 = $this->getDataGenerator()->create_user();
$u3 = $this->getDataGenerator()->create_user();
$syscontext = \context_system::instance();
$u1context = \context_user::instance($u1->id);
$u2context = \context_user::instance($u2->id);
$u3context = \context_user::instance($u3->id);
assign_capability('moodle/competency:planmanage', CAP_ALLOW, $manage, $syscontext->id);
assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $manageown, $u2context->id);
role_assign($manage, $u1->id, $syscontext->id);
role_assign($manageown, $u2->id, $syscontext->id);
role_assign($manage, $u3->id, $u2context->id);
accesslib_clear_all_caches_for_unit_testing();
$this->setUser($u1);
$this->assertTrue(plan::can_manage_user($u1->id));
$this->assertTrue(plan::can_manage_user($u2->id));
$this->assertTrue(plan::can_manage_user($u3->id));
$this->setUser($u2);
$this->assertFalse(plan::can_manage_user($u1->id));
$this->assertTrue(plan::can_manage_user($u2->id));
$this->assertFalse(plan::can_manage_user($u3->id));
$this->setUser($u3);
$this->assertFalse(plan::can_manage_user($u1->id));
$this->assertTrue(plan::can_manage_user($u2->id));
$this->assertFalse(plan::can_manage_user($u3->id));
}
public function test_can_manage_user_draft(): void {
$this->resetAfterTest(true);
$manage = create_role('Manage', 'manage', 'Plan manager');
$manageown = create_role('Manageown', 'manageown', 'Own plan manager');
$managedraft = create_role('Managedraft', 'managedraft', 'Draft plan manager');
$manageowndraft = create_role('Manageowndraft', 'manageowndraft', 'Own draft plan manager');
$u1 = $this->getDataGenerator()->create_user();
$u2 = $this->getDataGenerator()->create_user();
$u3 = $this->getDataGenerator()->create_user();
$u4 = $this->getDataGenerator()->create_user();
$u5 = $this->getDataGenerator()->create_user();
$syscontext = \context_system::instance();
$u1context = \context_user::instance($u1->id);
$u2context = \context_user::instance($u2->id);
$u3context = \context_user::instance($u3->id);
$u4context = \context_user::instance($u4->id);
$u5context = \context_user::instance($u5->id);
assign_capability('moodle/competency:planmanage', CAP_ALLOW, $manage, $syscontext->id);
assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $manageown, $syscontext->id);
assign_capability('moodle/competency:planmanagedraft', CAP_ALLOW, $managedraft, $syscontext->id);
assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $manageowndraft, $syscontext->id);
role_assign($manage, $u1->id, $syscontext->id);
role_assign($manageown, $u2->id, $syscontext->id);
role_assign($managedraft, $u3->id, $syscontext->id);
role_assign($managedraft, $u4->id, $u2context->id);
role_assign($manageowndraft, $u5->id, $syscontext->id);
accesslib_clear_all_caches_for_unit_testing();
$this->setUser($u1);
$this->assertFalse(plan::can_manage_user_draft($u1->id));
$this->assertFalse(plan::can_manage_user_draft($u2->id));
$this->assertFalse(plan::can_manage_user_draft($u3->id));
$this->assertFalse(plan::can_manage_user_draft($u4->id));
$this->assertFalse(plan::can_manage_user_draft($u5->id));
$this->setUser($u2);
$this->assertFalse(plan::can_manage_user_draft($u1->id));
$this->assertFalse(plan::can_manage_user_draft($u2->id));
$this->assertFalse(plan::can_manage_user_draft($u3->id));
$this->assertFalse(plan::can_manage_user_draft($u4->id));
$this->assertFalse(plan::can_manage_user_draft($u5->id));
$this->setUser($u3);
$this->assertTrue(plan::can_manage_user_draft($u1->id));
$this->assertTrue(plan::can_manage_user_draft($u2->id));
$this->assertTrue(plan::can_manage_user_draft($u3->id));
$this->assertTrue(plan::can_manage_user_draft($u4->id));
$this->assertTrue(plan::can_manage_user_draft($u5->id));
$this->setUser($u4);
$this->assertFalse(plan::can_manage_user_draft($u1->id));
$this->assertTrue(plan::can_manage_user_draft($u2->id));
$this->assertFalse(plan::can_manage_user_draft($u3->id));
$this->assertFalse(plan::can_manage_user_draft($u4->id));
$this->assertFalse(plan::can_manage_user_draft($u5->id));
$this->setUser($u5);
$this->assertFalse(plan::can_manage_user_draft($u1->id));
$this->assertFalse(plan::can_manage_user_draft($u2->id));
$this->assertFalse(plan::can_manage_user_draft($u3->id));
$this->assertFalse(plan::can_manage_user_draft($u4->id));
$this->assertTrue(plan::can_manage_user_draft($u5->id));
}
public function test_can_read_user(): void {
$this->resetAfterTest(true);
$read = create_role('Read', 'read', 'Plan reader');
$readown = create_role('Readown', 'readown', 'Own plan reader');
$u1 = $this->getDataGenerator()->create_user();
$u2 = $this->getDataGenerator()->create_user();
$u3 = $this->getDataGenerator()->create_user();
$syscontext = \context_system::instance();
$u1context = \context_user::instance($u1->id);
$u2context = \context_user::instance($u2->id);
$u3context = \context_user::instance($u3->id);
assign_capability('moodle/competency:planview', CAP_ALLOW, $read, $syscontext->id);
assign_capability('moodle/competency:planviewown', CAP_ALLOW, $readown, $u2context->id);
role_assign($read, $u1->id, $syscontext->id);
role_assign($readown, $u2->id, $syscontext->id);
role_assign($read, $u3->id, $u2context->id);
accesslib_clear_all_caches_for_unit_testing();
$this->setUser($u1);
$this->assertTrue(plan::can_read_user($u1->id));
$this->assertTrue(plan::can_read_user($u2->id));
$this->assertTrue(plan::can_read_user($u3->id));
$this->setUser($u2);
$this->assertFalse(plan::can_read_user($u1->id));
$this->assertTrue(plan::can_read_user($u2->id));
$this->assertFalse(plan::can_read_user($u3->id));
$this->setUser($u3);
$this->assertFalse(plan::can_read_user($u1->id));
$this->assertTrue(plan::can_read_user($u2->id));
$this->assertTrue(plan::can_read_user($u3->id)); // Due to the default capability.
}
public function test_can_read_user_draft(): void {
$this->resetAfterTest(true);
$read = create_role('Read', 'read', 'Plan readr');
$readown = create_role('Readown', 'readown', 'Own plan readr');
$readdraft = create_role('Readdraft', 'readdraft', 'Draft plan readr');
$readowndraft = create_role('Readowndraft', 'readowndraft', 'Own draft plan readr');
$u1 = $this->getDataGenerator()->create_user();
$u2 = $this->getDataGenerator()->create_user();
$u3 = $this->getDataGenerator()->create_user();
$u4 = $this->getDataGenerator()->create_user();
$u5 = $this->getDataGenerator()->create_user();
$syscontext = \context_system::instance();
$u1context = \context_user::instance($u1->id);
$u2context = \context_user::instance($u2->id);
$u3context = \context_user::instance($u3->id);
$u4context = \context_user::instance($u4->id);
$u5context = \context_user::instance($u5->id);
assign_capability('moodle/competency:planview', CAP_ALLOW, $read, $syscontext->id);
assign_capability('moodle/competency:planviewown', CAP_ALLOW, $readown, $syscontext->id);
assign_capability('moodle/competency:planviewdraft', CAP_ALLOW, $readdraft, $syscontext->id);
assign_capability('moodle/competency:planviewowndraft', CAP_ALLOW, $readowndraft, $syscontext->id);
assign_capability('moodle/competency:planviewown', CAP_PROHIBIT, $readowndraft, $syscontext->id);
role_assign($read, $u1->id, $syscontext->id);
role_assign($readown, $u2->id, $syscontext->id);
role_assign($readdraft, $u3->id, $syscontext->id);
role_assign($readdraft, $u4->id, $u2context->id);
role_assign($readowndraft, $u5->id, $syscontext->id);
accesslib_clear_all_caches_for_unit_testing();
$this->setUser($u1);
$this->assertFalse(plan::can_read_user_draft($u1->id));
$this->assertFalse(plan::can_read_user_draft($u2->id));
$this->assertFalse(plan::can_read_user_draft($u3->id));
$this->assertFalse(plan::can_read_user_draft($u4->id));
$this->assertFalse(plan::can_read_user_draft($u5->id));
$this->setUser($u2);
$this->assertFalse(plan::can_read_user_draft($u1->id));
$this->assertFalse(plan::can_read_user_draft($u2->id));
$this->assertFalse(plan::can_read_user_draft($u3->id));
$this->assertFalse(plan::can_read_user_draft($u4->id));
$this->assertFalse(plan::can_read_user_draft($u5->id));
$this->setUser($u3);
$this->assertTrue(plan::can_read_user_draft($u1->id));
$this->assertTrue(plan::can_read_user_draft($u2->id));
$this->assertTrue(plan::can_read_user_draft($u3->id));
$this->assertTrue(plan::can_read_user_draft($u4->id));
$this->assertTrue(plan::can_read_user_draft($u5->id));
$this->setUser($u4);
$this->assertFalse(plan::can_read_user_draft($u1->id));
$this->assertTrue(plan::can_read_user_draft($u2->id));
$this->assertFalse(plan::can_read_user_draft($u3->id));
$this->assertFalse(plan::can_read_user_draft($u4->id));
$this->assertFalse(plan::can_read_user_draft($u5->id));
$this->setUser($u5);
$this->assertFalse(plan::can_read_user_draft($u1->id));
$this->assertFalse(plan::can_read_user_draft($u2->id));
$this->assertFalse(plan::can_read_user_draft($u3->id));
$this->assertFalse(plan::can_read_user_draft($u4->id));
$this->assertTrue(plan::can_read_user_draft($u5->id));
}
public function test_validate_duedate(): void {
global $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$dg = $this->getDataGenerator();
$lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
$user = $dg->create_user();
$record = array('userid' => $user->id,
'status' => plan::STATUS_DRAFT,
'duedate' => time() - 8000);
// Ignore duedate validation on create/update draft plan.
$plan = $lpg->create_plan($record);
$this->assertInstanceOf(plan::class, $plan);
// Passing from draft to active.
$plan->set('status', plan::STATUS_ACTIVE);
// Draft to active with duedate in the past.
$expected = array(
'duedate' => new \lang_string('errorcannotsetduedateinthepast', 'core_competency'),
);
$this->assertEquals($expected, $plan->validate());
// Draft to active: past date => past date(fail).
$plan->set('duedate', time() - 100);
$expected = array(
'duedate' => new \lang_string('errorcannotsetduedateinthepast', 'core_competency'),
);
$this->assertEquals($expected, $plan->validate());
// Draft to active: past date => too soon (fail).
$plan->set('duedate', time() + 100);
$expected = array(
'duedate' => new \lang_string('errorcannotsetduedatetoosoon', 'core_competency'),
);
$this->assertEquals($expected, $plan->validate());
// Draft to active: past date => future date (pass).
$plan->set('duedate', time() + plan::DUEDATE_THRESHOLD + 10);
$this->assertEquals(true, $plan->validate());
// Draft to active: past date => unset date (pass).
$plan->set('duedate', 0);
$this->assertEquals(true, $plan->validate());
// Updating active plan.
$plan->update();
// Active to active: past => same past (pass).
$record = $plan->to_record();
$record->duedate = 1;
$DB->update_record(plan::TABLE, $record);
$plan->read();
$plan->set('description', uniqid()); // Force revalidation.
$this->assertTrue($plan->is_valid());
// Active to active: past => unset (pass).
$plan->set('duedate', 0);
$this->assertTrue($plan->is_valid());
$plan->update();
// Active to active: unset => unset (pass).
$plan->set('description', uniqid()); // Force revalidation.
$this->assertTrue($plan->is_valid());
// Active to active: unset date => past date(fail).
$plan->set('duedate', time() - 100);
$expected = array(
'duedate' => new \lang_string('errorcannotsetduedateinthepast', 'core_competency'),
);
$this->assertEquals($expected, $plan->validate());
// Active to active: unset date => too soon (fail).
$plan->set('duedate', time() + 100);
$expected = array(
'duedate' => new \lang_string('errorcannotsetduedatetoosoon', 'core_competency'),
);
$this->assertEquals($expected, $plan->validate());
// Active to active: unset date => future date (pass).
$plan->set('duedate', time() + plan::DUEDATE_THRESHOLD + 10);
$this->assertEquals(true, $plan->validate());
// Updating active plan with future date.
$plan->update();
// Active to active: future => same future (pass).
$plan->set('description', uniqid()); // Force revalidation.
$this->assertTrue($plan->is_valid());
// Active to active: future date => unset date (pass).
$plan->set('duedate', 0);
$this->assertEquals(true, $plan->validate());
// Active to active: future date => past date(fail).
$plan->set('duedate', time() - 100);
$expected = array(
'duedate' => new \lang_string('errorcannotsetduedateinthepast', 'core_competency'),
);
$this->assertEquals($expected, $plan->validate());
// Active to active: future date => too soon (fail).
$plan->set('duedate', time() + 100);
$expected = array(
'duedate' => new \lang_string('errorcannotsetduedatetoosoon', 'core_competency'),
);
$this->assertEquals($expected, $plan->validate());
// Active to active: future date => future date (pass).
$plan->set('duedate', time() + plan::DUEDATE_THRESHOLD + 10);
$this->assertEquals(true, $plan->validate());
// Completing plan: with due date in the past.
$record = $plan->to_record();
$record->status = plan::STATUS_ACTIVE;
$record->duedate = time() - 200;
$DB->update_record(plan::TABLE, $record);
$success = api::complete_plan($plan->get('id'));
$this->assertTrue($success);
// Completing plan: with due date too soon (pass).
$record = $plan->to_record();
$record->status = plan::STATUS_ACTIVE;
$record->duedate = time() + 200;
$DB->update_record(plan::TABLE, $record);
$success = api::complete_plan($plan->get('id'));
$this->assertTrue($success);
// Completing plan: with due date in the future (pass).
$record = $plan->to_record();
$record->status = plan::STATUS_ACTIVE;
$record->duedate = time() + plan::DUEDATE_THRESHOLD + 10;
$DB->update_record(plan::TABLE, $record);
$success = api::complete_plan($plan->get('id'));
$this->assertTrue($success);
// Completing plan: with due date unset (pass).
$record = $plan->to_record();
$record->status = plan::STATUS_ACTIVE;
$record->duedate = 0;
$DB->update_record(plan::TABLE, $record);
$success = api::complete_plan($plan->get('id'));
$this->assertTrue($success);
// Reopening plan: with due date in the past => duedate unset.
$record = $plan->to_record();
$record->status = plan::STATUS_COMPLETE;
$record->duedate = time() - 200;
$DB->update_record(plan::TABLE, $record);
$success = api::reopen_plan($plan->get('id'));
$this->assertTrue($success);
$plan->read();
$this->assertEquals(0, $plan->get('duedate'));
// Reopening plan: with due date too soon => duedate unset.
$record = $plan->to_record();
$record->status = plan::STATUS_COMPLETE;
$record->duedate = time() + 100;
$DB->update_record(plan::TABLE, $record);
$success = api::reopen_plan($plan->get('id'));
$this->assertTrue($success);
$plan->read();
$this->assertEquals(0, $plan->get('duedate'));
// Reopening plan: with due date in the future => duedate unchanged.
$record = $plan->to_record();
$record->status = plan::STATUS_COMPLETE;
$duedate = time() + plan::DUEDATE_THRESHOLD + 10;
$record->duedate = $duedate;
$DB->update_record(plan::TABLE, $record);
$success = api::reopen_plan($plan->get('id'));
$this->assertTrue($success);
$plan->read();
// Check that the due date has not changed.
$this->assertNotEquals(0, $plan->get('duedate'));
$this->assertEquals($duedate, $plan->get('duedate'));
}
public function test_get_by_user_and_competency(): void {
$this->resetAfterTest();
$this->setAdminUser();
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$u3 = $dg->create_user();
$u4 = $dg->create_user();
$f1 = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
$tpl1 = $lpg->create_template();
$lpg->create_template_competency(array('competencyid' => $c1->get('id'), 'templateid' => $tpl1->get('id')));
$p1 = $lpg->create_plan(array('userid' => $u1->id));
$lpg->create_plan_competency(array('planid' => $p1->get('id'), 'competencyid' => $c1->get('id')));
$p2 = $lpg->create_plan(array('userid' => $u2->id));
$lpg->create_plan_competency(array('planid' => $p2->get('id'), 'competencyid' => $c1->get('id')));
$p3 = $lpg->create_plan(array('userid' => $u3->id, 'templateid' => $tpl1->get('id')));
$p4 = $lpg->create_plan(array('userid' => $u4->id, 'templateid' => $tpl1->get('id')));
api::complete_plan($p2);
api::complete_plan($p4);
// Finding a plan, not completed.
$plans = plan::get_by_user_and_competency($u1->id, $c1->get('id'));
$this->assertCount(1, $plans);
$plan = array_shift($plans);
$this->assertEquals($p1->get('id'), $plan->get('id'));
$this->assertNotEquals(plan::STATUS_COMPLETE, $plan->get('status'));
// Finding a completed plan.
$plans = plan::get_by_user_and_competency($u2->id, $c1->get('id'));
$this->assertCount(1, $plans);
$plan = array_shift($plans);
$this->assertEquals($p2->get('id'), $plan->get('id'));
$this->assertEquals(plan::STATUS_COMPLETE, $plan->get('status'));
// Finding a plan based on a template, not completed.
$plans = plan::get_by_user_and_competency($u3->id, $c1->get('id'));
$this->assertCount(1, $plans);
$plan = array_shift($plans);
$this->assertEquals($p3->get('id'), $plan->get('id'));
$this->assertTrue($plan->is_based_on_template());
$this->assertNotEquals(plan::STATUS_COMPLETE, $plan->get('status'));
// Finding a plan based on a template.
$plans = plan::get_by_user_and_competency($u4->id, $c1->get('id'));
$this->assertCount(1, $plans);
$plan = array_shift($plans);
$this->assertEquals($p4->get('id'), $plan->get('id'));
$this->assertTrue($plan->is_based_on_template());
$this->assertEquals(plan::STATUS_COMPLETE, $plan->get('status'));
// Finding more than one plan, no template.
$p5 = $lpg->create_plan(array('userid' => $u1->id));
$lpg->create_plan_competency(array('planid' => $p5->get('id'), 'competencyid' => $c1->get('id')));
$plans = plan::get_by_user_and_competency($u1->id, $c1->get('id'));
$this->assertCount(2, $plans);
$plan = array_shift($plans);
$this->assertEquals($p1->get('id'), $plan->get('id'));
$plan = array_shift($plans);
$this->assertEquals($p5->get('id'), $plan->get('id'));
// Finding more than one plan, with template.
$p6 = $lpg->create_plan(array('userid' => $u1->id, 'templateid' => $tpl1->get('id')));
$plans = plan::get_by_user_and_competency($u1->id, $c1->get('id'));
$this->assertCount(3, $plans);
$plan = array_shift($plans);
$this->assertEquals($p1->get('id'), $plan->get('id'));
$plan = array_shift($plans);
$this->assertEquals($p5->get('id'), $plan->get('id'));
$plan = array_shift($plans);
$this->assertEquals($p6->get('id'), $plan->get('id'));
// Finding no plans.
$plans = plan::get_by_user_and_competency($u1->id, $c2->get('id'));
$this->assertCount(0, $plans);
}
public function test_get_competency(): void {
$this->resetAfterTest();
$this->setAdminUser();
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$u3 = $dg->create_user();
$u4 = $dg->create_user();
$f1 = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
$c3 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
$c4 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
$tpl1 = $lpg->create_template();
$p1 = $lpg->create_plan(array('userid' => $u1->id));
$p2 = $lpg->create_plan(array('userid' => $u2->id));
$p3 = $lpg->create_plan(array('userid' => $u3->id, 'templateid' => $tpl1->get('id')));
$p4 = $lpg->create_plan(array('userid' => $u4->id, 'templateid' => $tpl1->get('id')));
$lpg->create_plan_competency(array('planid' => $p1->get('id'), 'competencyid' => $c1->get('id')));
$lpg->create_plan_competency(array('planid' => $p2->get('id'), 'competencyid' => $c2->get('id')));
$lpg->create_template_competency(array('templateid' => $tpl1->get('id'), 'competencyid' => $c3->get('id')));
$lpg->create_template_competency(array('templateid' => $tpl1->get('id'), 'competencyid' => $c4->get('id')));
// Completing the plans and removing a competency from the template.
api::complete_plan($p2);
api::complete_plan($p4);
api::remove_competency_from_template($tpl1->get('id'), $c4->get('id'));
// We can find all competencies.
$this->assertEquals($c1->to_record(), $p1->get_competency($c1->get('id'))->to_record());
$this->assertEquals($c2->to_record(), $p2->get_competency($c2->get('id'))->to_record());
$this->assertEquals($c3->to_record(), $p3->get_competency($c3->get('id'))->to_record());
$this->assertEquals($c4->to_record(), $p4->get_competency($c4->get('id'))->to_record());
// Getting the competency 4 from the non-completed plan based on a template p4, will throw an exception.
$this->expectException('coding_exception');
$this->expectExceptionMessage('The competency does not belong to this template:');
$p3->get_competency($c4->get('id'));
}
}
File diff suppressed because it is too large Load Diff
+307
View File
@@ -0,0 +1,307 @@
<?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/>.
namespace core_competency\task;
use core_competency\api;
use core_competency\plan;
use core_competency\template;
/**
* Task tests.
*
* @package core_competency
* @copyright 2015 Issam Taboubi <issam.taboubi@umontreal.ca>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class task_test extends \advanced_testcase {
public function test_sync_plans_from_cohorts_task(): void {
global $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
// Sql to simulate the execution in time.
$cmsql = "UPDATE {cohort_members} SET timeadded = :currenttime WHERE cohortid = :cohortid AND userid = :userid";
$tplsql = "UPDATE {" . template::TABLE . "} SET timemodified = :currenttime WHERE id = :templateid";
$plansql = "UPDATE {" . plan::TABLE . "} SET timemodified = :currenttime WHERE id = :planid";
$currenttime = time();
$user1 = $dg->create_user();
$user2 = $dg->create_user();
$user3 = $dg->create_user();
$user4 = $dg->create_user();
$user5 = $dg->create_user();
$cohort = $dg->create_cohort();
$tpl = $lpg->create_template();
// Add 2 users to the cohort.
cohort_add_member($cohort->id, $user1->id);
cohort_add_member($cohort->id, $user2->id);
// Creating plans from template cohort.
$templatecohort = api::create_template_cohort($tpl->get('id'), $cohort->id);
$created = api::create_plans_from_template_cohort($tpl->get('id'), $cohort->id);
$this->assertEquals(2, $created);
$task = \core\task\manager::get_scheduled_task('\\core\\task\\sync_plans_from_template_cohorts_task');
$this->assertInstanceOf('\core\task\sync_plans_from_template_cohorts_task', $task);
// Add two more users to the cohort.
cohort_add_member($cohort->id, $user3->id);
cohort_add_member($cohort->id, $user4->id);
$currenttime = $currenttime + 1;
$task->execute();
$task->set_last_run_time($currenttime);
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
// Test if remove user from cohort will affect plans.
cohort_remove_member($cohort->id, $user3->id);
cohort_remove_member($cohort->id, $user4->id);
$currenttime = $currenttime + 1;
$task->execute();
$task->set_last_run_time($currenttime);
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
// The template is now hidden, and I've added a user with a missing plan. Nothing should happen.
$currenttime = $currenttime + 1;
$tpl->set('visible', false);
$tpl->update();
$DB->execute($tplsql, array('currenttime' => $currenttime, 'templateid' => $tpl->get('id')));
$currenttime = $currenttime + 1;
cohort_add_member($cohort->id, $user5->id);
$DB->execute($cmsql, array('currenttime' => $currenttime, 'cohortid' => $cohort->id, 'userid' => $user5->id));
$this->assertFalse(plan::record_exists_select('userid = ? AND templateid = ?', array($user5->id, $tpl->get('id'))));
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
$currenttime = $currenttime + 1;
$task->execute();
$task->set_last_run_time($currenttime);
$this->assertFalse(plan::record_exists_select('userid = ? AND templateid = ?', array($user5->id, $tpl->get('id'))));
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
// Now I set the template as visible again, the plan is created.
$currenttime = $currenttime + 1;
$tpl->set('visible', true);
$tpl->update();
$DB->execute($tplsql, array('currenttime' => $currenttime, 'templateid' => $tpl->get('id')));
$currenttime = $currenttime + 1;
$task->execute();
$task->set_last_run_time($currenttime);
$this->assertTrue(plan::record_exists_select('userid = ? AND templateid = ?', array($user5->id, $tpl->get('id'))));
$this->assertEquals(5, plan::count_records(array('templateid' => $tpl->get('id'))));
// Let's unlink the plan and run the task again, it should not be recreated.
$currenttime = $currenttime + 1;
$plan = plan::get_record(array('userid' => $user5->id, 'templateid' => $tpl->get('id')));
api::unlink_plan_from_template($plan);
$DB->execute($plansql, array('currenttime' => $currenttime, 'planid' => $plan->get('id')));
$this->assertTrue(plan::record_exists_select('userid = ?', array($user5->id)));
$this->assertFalse(plan::record_exists_select('userid = ? AND templateid = ?', array($user5->id, $tpl->get('id'))));
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
$currenttime = $currenttime + 1;
$task->execute();
$task->set_last_run_time($currenttime);
$this->assertTrue(plan::record_exists_select('userid = ?', array($user5->id)));
$this->assertFalse(plan::record_exists_select('userid = ? AND templateid = ?', array($user5->id, $tpl->get('id'))));
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
// Adding users to cohort that already exist in plans.
$currenttime = $currenttime + 1;
cohort_add_member($cohort->id, $user3->id);
cohort_add_member($cohort->id, $user4->id);
$DB->execute($cmsql, array('currenttime' => $currenttime, 'cohortid' => $cohort->id, 'userid' => $user3->id));
$DB->execute($cmsql, array('currenttime' => $currenttime, 'cohortid' => $cohort->id, 'userid' => $user3->id));
$currenttime = $currenttime + 1;
$task->execute();
$task->set_last_run_time($currenttime);
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
// Test a user plan deleted will not be recreated.
$currenttime = $currenttime + 1;
$plan = plan::get_record(array('userid' => $user4->id, 'templateid' => $tpl->get('id')));
api::delete_plan($plan->get('id'));
$currenttime = $currenttime + 1;
$task->execute();
$task->set_last_run_time($currenttime);
$this->assertEquals(3, plan::count_records(array('templateid' => $tpl->get('id'))));
}
public function test_sync_plans_from_cohorts_with_templateduedate_task(): void {
$this->resetAfterTest(true);
$this->setAdminUser();
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$user1 = $dg->create_user();
$user2 = $dg->create_user();
$user3 = $dg->create_user();
$user4 = $dg->create_user();
$user5 = $dg->create_user();
$cohort = $dg->create_cohort();
$tpl = $lpg->create_template(array('duedate' => time() + 400));
// Add 2 users to the cohort.
cohort_add_member($cohort->id, $user1->id);
cohort_add_member($cohort->id, $user2->id);
// Creating plans from template cohort.
$templatecohort = api::create_template_cohort($tpl->get('id'), $cohort->id);
$created = api::create_plans_from_template_cohort($tpl->get('id'), $cohort->id);
$this->assertEquals(2, $created);
$task = \core\task\manager::get_scheduled_task('\\core\\task\\sync_plans_from_template_cohorts_task');
$this->assertInstanceOf('\core\task\sync_plans_from_template_cohorts_task', $task);
// Add two more users to the cohort.
cohort_add_member($cohort->id, $user3->id);
cohort_add_member($cohort->id, $user4->id);
$task->execute();
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
// Test if remove user from cohort will affect plans.
cohort_remove_member($cohort->id, $user3->id);
cohort_remove_member($cohort->id, $user4->id);
$task->execute();
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
// The template is now hidden, and I've added a user with a missing plan. Nothing should happen.
$tpl->set('visible', false);
$tpl->update();
cohort_add_member($cohort->id, $user5->id);
$this->assertFalse(plan::record_exists_select('userid = ? AND templateid = ?', array($user5->id, $tpl->get('id'))));
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
$task->execute();
$this->assertFalse(plan::record_exists_select('userid = ? AND templateid = ?', array($user5->id, $tpl->get('id'))));
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
// Now I set the template as visible again, the plan is created.
$tpl->set('visible', true);
$tpl->update();
$task->execute();
$this->assertTrue(plan::record_exists_select('userid = ? AND templateid = ?', array($user5->id, $tpl->get('id'))));
$this->assertEquals(5, plan::count_records(array('templateid' => $tpl->get('id'))));
// Let's unlink the plan and run the task again, it should not be recreated.
$plan = plan::get_record(array('userid' => $user5->id, 'templateid' => $tpl->get('id')));
api::unlink_plan_from_template($plan);
$this->assertTrue(plan::record_exists_select('userid = ?', array($user5->id)));
$this->assertFalse(plan::record_exists_select('userid = ? AND templateid = ?', array($user5->id, $tpl->get('id'))));
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
$task->execute();
$this->assertTrue(plan::record_exists_select('userid = ?', array($user5->id)));
$this->assertFalse(plan::record_exists_select('userid = ? AND templateid = ?', array($user5->id, $tpl->get('id'))));
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
// Adding users to cohort that already exist in plans.
cohort_add_member($cohort->id, $user3->id);
cohort_add_member($cohort->id, $user4->id);
$task->execute();
$this->assertEquals(4, plan::count_records(array('templateid' => $tpl->get('id'))));
}
public function test_sync_plans_from_cohorts_with_passed_duedate(): void {
global $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$user1 = $dg->create_user();
$user2 = $dg->create_user();
$cohort = $dg->create_cohort();
$tpl = $lpg->create_template(array('duedate' => time() + 1000));
$templatecohort = api::create_template_cohort($tpl->get('id'), $cohort->id);
$task = \core\task\manager::get_scheduled_task('\\core\\task\\sync_plans_from_template_cohorts_task');
// Add 1 user to the cohort.
cohort_add_member($cohort->id, $user1->id);
// Creating plans from template cohort.
$task->execute();
$this->assertEquals(1, plan::count_records());
// Now add another user, but this time the template will be expired.
cohort_add_member($cohort->id, $user2->id);
$record = $tpl->to_record();
$record->duedate = time() - 10000;
$DB->update_record(template::TABLE, $record);
$tpl->read();
$task->execute();
$this->assertEquals(1, plan::count_records()); // Still only one plan.
// Pretend it wasn't expired.
$tpl->set('duedate', time() + 100);
$tpl->update();
$task->execute();
$this->assertEquals(2, plan::count_records()); // Now there is two.
}
public function test_complete_plans_task(): void {
global $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$user = $dg->create_user();
$up1 = $lpg->create_plan(array('userid' => $user->id,
'status' => plan::STATUS_DRAFT));
$up2 = $lpg->create_plan(array('userid' => $user->id,
'status' => plan::STATUS_ACTIVE));
// Set duedate in the past.
$date = new \DateTime('yesterday');
$record1 = $up1->to_record();
$record2 = $up2->to_record();
$record1->duedate = $date->getTimestamp();
$record2->duedate = $date->getTimestamp();
$DB->update_record(plan::TABLE, $record1);
$DB->update_record(plan::TABLE, $record2);
$task = \core\task\manager::get_scheduled_task('\\core\\task\\complete_plans_task');
$this->assertInstanceOf('\\core\\task\\complete_plans_task', $task);
// Test that draft plan can not be completed on running task.
$task->execute();
$plandraft = api::read_plan($up1->get('id'));
$this->assertEquals(plan::STATUS_DRAFT, $plandraft->get('status'));
// Test that active plan can be completed on running task.
$task->execute();
$planactive = api::read_plan($up2->get('id'));
$this->assertEquals(plan::STATUS_COMPLETE, $planactive->get('status'));
}
}
+104
View File
@@ -0,0 +1,104 @@
<?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/>.
namespace core_competency;
/**
* Template persistent testcase.
*
* @package core_competency
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class template_test extends \advanced_testcase {
public function test_validate_duedate(): void {
global $DB;
$this->resetAfterTest();
$tpl = $this->getDataGenerator()->get_plugin_generator('core_competency')->create_template();
// No due date -> pass.
$tpl->set('duedate', 0);
$this->assertTrue($tpl->is_valid());
// Setting new due date in the past -> fail.
$tpl->set('duedate', 1);
$errors = $tpl->get_errors();
$this->assertCount(1, $errors);
$this->assertArrayHasKey('duedate', $errors);
// Setting new due date in very close past -> pass.
$tpl->set('duedate', time() - 10);
$this->assertTrue($tpl->is_valid());
// Setting new due date in future -> pass.
$tpl->set('duedate', time() + 600);
$this->assertTrue($tpl->is_valid());
// Save due date in the future.
$tpl->update();
// Going from future date to past -> fail.
$tpl->set('duedate', 1);
$errors = $tpl->get_errors();
$this->assertCount(1, $errors);
$this->assertArrayHasKey('duedate', $errors);
// Going from future date to none -> pass.
$tpl->set('duedate', 0);
$this->assertTrue($tpl->is_valid());
// Going from future date to other future -> pass.
$tpl->set('duedate', time() + 6000);
$this->assertTrue($tpl->is_valid());
// Going from future date to close past -> pass.
$tpl->set('duedate', time() - 10);
$this->assertTrue($tpl->is_valid());
// Mocking past due date.
$record = $tpl->to_record();
$record->duedate = 1;
$DB->update_record(template::TABLE, $record);
$tpl->read();
$this->assertEquals(1, $tpl->get('duedate'));
// Not changing the past due date -> pass.
// Note: changing visibility to force validation.
$tpl->set('visible', 0);
$tpl->set('visible', 1);
$this->assertTrue($tpl->is_valid());
// Changing past due date to other past -> fail.
$tpl->set('duedate', 10);
$errors = $tpl->get_errors();
$this->assertCount(1, $errors);
$this->assertArrayHasKey('duedate', $errors);
// Changing past due date close past -> pass.
$tpl->set('duedate', time() + 10);
$this->assertTrue($tpl->is_valid());
// Changing past due date to future -> pass.
$tpl->set('duedate', time() + 1000);
$this->assertTrue($tpl->is_valid());
// Changing past due date to none -> pass.
$tpl->set('duedate', 0);
$this->assertTrue($tpl->is_valid());
}
}
@@ -0,0 +1,81 @@
<?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/>.
namespace core_competency;
/**
* User evidence competency persistent testcase.
*
* @package core_competency
* @copyright 2016 Serge Gauthier - <serge.gauthier.2@umontreal.ca>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_evidence_competency_test extends \advanced_testcase {
public function test_get_user_competencies_by_userevidenceid(): void {
global $CFG, $DB;
$this->resetAfterTest(true);
$dg = $this->getDataGenerator();
$lpg = $dg->get_plugin_generator('core_competency');
$u1 = $dg->create_user();
// Create framework with competencies.
$fw = $lpg->create_framework();
$c1 = $lpg->create_competency(array('competencyframeworkid' => $fw->get('id')));
$c2 = $lpg->create_competency(array('competencyframeworkid' => $fw->get('id')));
$c3 = $lpg->create_competency(array('competencyframeworkid' => $fw->get('id')));
$c4 = $lpg->create_competency(array('competencyframeworkid' => $fw->get('id')));
// Create a plan with competencies.
$p1 = $lpg->create_plan(array('userid' => $u1->id));
$lpg->create_plan_competency(array('planid' => $p1->get('id'), 'competencyid' => $c1->get('id')));
$lpg->create_plan_competency(array('planid' => $p1->get('id'), 'competencyid' => $c2->get('id')));
$lpg->create_plan_competency(array('planid' => $p1->get('id'), 'competencyid' => $c3->get('id')));
$lpg->create_plan_competency(array('planid' => $p1->get('id'), 'competencyid' => $c4->get('id')));
// Create a prior learning evidence and link competencies.
$ue1 = $lpg->create_user_evidence(array('userid' => $u1->id));
$uec11 = $lpg->create_user_evidence_competency(array('userevidenceid' => $ue1->get('id'), 'competencyid' => $c1->get('id')));
$uec12 = $lpg->create_user_evidence_competency(array('userevidenceid' => $ue1->get('id'), 'competencyid' => $c2->get('id')));
$uec13 = $lpg->create_user_evidence_competency(array('userevidenceid' => $ue1->get('id'), 'competencyid' => $c3->get('id')));
$uc11 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c1->get('id')));
$uc12 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c2->get('id')));
$uc13 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c3->get('id')));
// Create an other prior learning evidence and link competencies.
$ue2 = $lpg->create_user_evidence(array('userid' => $u1->id));
$uec22 = $lpg->create_user_evidence_competency(array('userevidenceid' => $ue2->get('id'), 'competencyid' => $c4->get('id')));
$uc22 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c4->get('id')));
// Check the user competencies associated to the first prior learning evidence.
$ucs = user_evidence_competency::get_user_competencies_by_userevidenceid($ue1->get('id'));
$this->assertCount(3, $ucs);
$uc = array_shift($ucs);
$this->assertEquals($uc->get('id'), $uc11->get('id'));
$uc = array_shift($ucs);
$this->assertEquals($uc->get('id'), $uc12->get('id'));
$uc = array_shift($ucs);
$this->assertEquals($uc->get('id'), $uc13->get('id'));
// Check the user competencies associated to the second prior learning evidence.
$ucs = user_evidence_competency::get_user_competencies_by_userevidenceid($ue2->get('id'));
$this->assertCount(1, $ucs);
$uc = array_shift($ucs);
$this->assertEquals($uc->get('id'), $uc22->get('id'));
}
}
+18
View File
@@ -0,0 +1,18 @@
This files describes API changes in /competency/*. The information provided
here is intended especially for developers.
=== 3.7 ===
* tool_lp can render the ui for a competency summary or a competency picker.
Use get_plugins_with_function('competency_picker') or
get_plugins_with_function('render_competency_summary') to call it.
=== 3.3 ===
* Deprecated classes and their new equivalent:
- core_competency\persistent -> core\persistent
- core_competency\invalid_persistent_exception -> core\invalid_persistent_exception
- core_competency\external\exporter -> core\external\exporter
- core_competency\external\persistent_exporter -> core\external\persistent_exporter
- core_competency\external\comment_area_exporter -> core_cohort\external\comment_area_exporter
- core_competency\external\stored_file_exporter -> core_files\external\stored_file_exporter
- core_competency\external\user_summary_exporter -> core_user\external\user_summary_exporter