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);
}
}