From c27113d93efa3e4e9c48809d5b8ececcddc6cc97 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Fri, 25 Sep 2015 09:49:57 +0800 Subject: [PATCH] MDL-51506 tool_lp: Migrating competency to new model API --- admin/tool/lp/classes/api.php | 17 +- admin/tool/lp/classes/competency.php | 556 ++++++++---------- admin/tool/lp/classes/external.php | 18 +- .../output/course_competencies_page.php | 2 + .../output/manage_competencies_page.php | 2 + .../output/template_competencies_page.php | 2 + 6 files changed, 270 insertions(+), 327 deletions(-) diff --git a/admin/tool/lp/classes/api.php b/admin/tool/lp/classes/api.php index 69c8f3c095f..1f5f4aa996e 100644 --- a/admin/tool/lp/classes/api.php +++ b/admin/tool/lp/classes/api.php @@ -74,7 +74,6 @@ class api { require_capability('tool/lp:competencymanage', $competency->get_framework()->get_context()); // OK - all set. - $competency->set_id($id); return $competency->delete(); } @@ -93,7 +92,7 @@ class api { require_capability('tool/lp:competencymanage', $current->get_framework()->get_context()); $max = self::count_competencies(array('parentid' => $current->get_parentid(), - 'competencyframeworkid' => $current->get_competencyframeworkid())); + 'competencyframeworkid' => $current->get_competencyframeworkid())); if ($max > 0) { $max--; } @@ -179,11 +178,6 @@ class api { $parentpath = $parent->get_path(); } - if ($parentframeworkid != $current->get_competencyframeworkid()) { - // Only allow moving within the same framework. - throw new coding_exception('Moving competencies is only supported within the same framework.'); - } - // If we are moving a node to a child of itself, promote all the child nodes by one level. $newparents = explode('/', $parentpath); @@ -271,8 +265,7 @@ class api { } // OK - all set. - $competency = new competency(); - return $competency->search($textsearch, $competencyframeworkid); + return competency::search($textsearch, $competencyframeworkid); } /** @@ -301,8 +294,7 @@ class api { } // OK - all set. - $competency = new competency(); - return $competency->get_records($filters, $sort, $order, $skip, $limit); + return competency::get_records($filters, $sort, $order, $skip, $limit); } /** @@ -327,8 +319,7 @@ class api { } // OK - all set. - $competency = new competency(); - return $competency->count_records($filters); + return competency::count_records($filters); } /** diff --git a/admin/tool/lp/classes/competency.php b/admin/tool/lp/classes/competency.php index aa3c38ccc11..725fa082afb 100644 --- a/admin/tool/lp/classes/competency.php +++ b/admin/tool/lp/classes/competency.php @@ -23,8 +23,9 @@ */ namespace tool_lp; -use stdClass; use context_system; +use lang_string; +use stdClass; /** * Class for loading/storing competencies from the DB. @@ -34,349 +35,200 @@ use context_system; */ class competency extends persistent { - /** @var string $shortname Short name for this competency */ - private $shortname = ''; + const TABLE = 'tool_lp_competency'; - /** @var string $idnumber Unique idnumber for this competency - must be unique within the framework if it is non-empty */ - private $idnumber = ''; - - /** @var string $description Description for this competency */ - private $description = ''; - - /** @var int $descriptionformat Format for the description */ - private $descriptionformat = 0; - - /** @var int $sortorder A number used to influence sorting */ - private $sortorder = 0; - - /** @var bool $visible Used to show/hide this competency */ - private $visible = true; - - /** @var int $parentid id of the parent of this competency (0 means root competency) */ - private $parentid = 0; - - /** @var string $path ids of all the parents up the tree separated with slashes */ - private $path = '/0/'; - - /** @var int $id of the competency framework this competency belongs to */ - private $competencyframeworkid = 0; + /** @var competency Object before update. */ + protected $beforeupdate = null; /** - * Method that provides the table name matching this class. + * Return the definition of the properties of this model. * - * @return string + * @return array */ - public function get_table_name() { - return 'tool_lp_competency'; + protected static function define_properties() { + return array( + 'shortname' => array( + 'type' => PARAM_TEXT + ), + 'idnumber' => array( + 'type' => PARAM_TEXT + ), + 'description' => array( + 'default' => '', + 'type' => PARAM_TEXT + ), + 'descriptionformat' => array( + 'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN), + 'type' => PARAM_INT, + 'default' => FORMAT_HTML + ), + 'sortorder' => array( + 'default' => null, + 'type' => PARAM_INT + ), + 'visible' => array( + 'default' => 1, + 'type' => PARAM_BOOL + ), + 'parentid' => array( + 'default' => 0, + 'type' => PARAM_INT + ), + 'path' => array( + 'default' => '/0/', + 'type' => PARAM_RAW + ), + 'competencyframeworkid' => array( + 'default' => 0, + 'type' => PARAM_INT + ), + ); } /** - * Get the short name. + * Hook to execute before validate. * - * @return string The short name + * @return void */ - public function get_shortname() { - return $this->shortname; + 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($parent); + $this->set_new_sortorder(); + } + + // During create. + } else { + + $this->set_new_path(); + if ($this->get_sortorder() === null) { + // Get a sortorder if it wasn't set. + $this->set_new_sortorder(); + } + } } /** - * Set the short name. + * Hook to execute after an update. * - * @param string $shortname The short name + * @param bool $result Whether or not the update was successful. + * @return void */ - public function set_shortname($shortname) { - $this->shortname = $shortname; + 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 + )); + } + + $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; + } + + // We need to delete all the children. + $deletepath = $DB->sql_like_escape($this->get_path() . $this->get_id() . '/') . '%'; + $like = $DB->sql_like('path', ':deletepath'); + $DB->delete_records_select(self::TABLE, $like, array('deletepath' => $deletepath)); + + // And all the links to courses. + $DB->delete_records('tool_lp_course_competency', array('competencyid' => $this->get_id())); } /** - * Get the description format. + * Get the competency framework. * - * @return int The description format - */ - public function get_descriptionformat() { - return $this->descriptionformat; - } - - /** - * Set the description format - * - * @param int $descriptionformat The description format - */ - public function set_descriptionformat($descriptionformat) { - $this->descriptionformat = $descriptionformat; - } - - /** - * Get the id number. - * - * @return string The id number - */ - public function get_idnumber() { - return $this->idnumber; - } - - /** - * Set the id number. - * - * @param string $idnumber The id number - */ - public function set_idnumber($idnumber) { - $this->idnumber = $idnumber; - } - - /** - * Get the description. - * - * @return string The description - */ - public function get_description() { - return $this->description; - } - - /** - * Set the description. - * - * @param string $description The description - */ - public function set_description($description) { - $this->description = $description; - } - - /** - * Get the sort order index. - * - * @return int The sort order index - */ - public function get_sortorder() { - return $this->sortorder; - } - - /** - * Set the sort order index. - * - * @param string $sortorder The sort order index - */ - public function set_sortorder($sortorder) { - $this->sortorder = $sortorder; - } - - /** - * Get the visible flag. - * - * @return string The visible flag - */ - public function get_visible() { - return $this->visible; - } - - /** - * Set the visible flag. - * - * @param string $visible The visible flag - */ - public function set_visible($visible) { - $this->visible = $visible; - } - - /** - * Get the parentid - * - * @return int The id of the parent - */ - public function get_parentid() { - return $this->parentid; - } - - /** - * Set the parent id - * - * @param int $id The parent id number (can be null) - */ - public function set_parentid($id) { - $this->parentid = $id; - } - - /** - * Get the path - * - * @return string The ids of all the parents joined with a slash. - */ - public function get_path() { - return $this->path; - } - - /** - * Set the path. - * - * @param string $path The ids of all the parents joined with a slash. - */ - public function set_path($path) { - $this->path = $path; - } - - /** - * Get the competencyframeworkid - * - * @return int The competency framework id. + * @return competency_framework */ public function get_framework() { return new competency_framework($this->get_competencyframeworkid()); } /** - * Get the competencyframeworkid + * Return the parent competency. * - * @return int The competency framework id. + * @return null|competency */ - public function get_competencyframeworkid() { - return $this->competencyframeworkid; + public function get_parent() { + $parentid = $this->get_parentid(); + if (!$parentid) { + return null; + } + return new competency($parentid); } /** - * Set the competencyframeworkid. + * Helper method to set the path. * - * @param int $competencyframeworkid The competency framework id. + * @param competency $parent The parent competency object. + * @return void */ - public function set_competencyframeworkid($competencyframeworkid) { - $this->competencyframeworkid = $competencyframeworkid; + 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->set('path', $path); } /** - * Populate this class with data from a DB record. + * Helper method to set the sortorder. * - * @param stdClass $record A DB record. - * @return \tool_lp\competency + * @return void */ - public function from_record($record) { - if (isset($record->id)) { - $this->set_id($record->id); - } - if (isset($record->shortname)) { - $this->set_shortname($record->shortname); - } - if (isset($record->idnumber)) { - $this->set_idnumber($record->idnumber); - } - if (isset($record->description)) { - $this->set_description($record->description); - } - if (isset($record->descriptionformat)) { - $this->set_descriptionformat($record->descriptionformat); - } - if (isset($record->sortorder)) { - $this->set_sortorder($record->sortorder); - } - if (isset($record->visible)) { - $this->set_visible($record->visible); - } - if (isset($record->timecreated)) { - $this->set_timecreated($record->timecreated); - } - if (isset($record->timemodified)) { - $this->set_timemodified($record->timemodified); - } - if (isset($record->usermodified)) { - $this->set_usermodified($record->usermodified); - } - if (isset($record->competencyframeworkid)) { - $this->set_competencyframeworkid($record->competencyframeworkid); - } - if (isset($record->parentid)) { - $this->set_parentid($record->parentid); - } - if (isset($record->path)) { - $this->set_path($record->path); - } - return $this; - } - - /** - * Create a DB record from this class. - * - * @return stdClass - */ - public function to_record() { - $record = new stdClass(); - $record->id = $this->get_id(); - $record->shortname = $this->get_shortname(); - $record->idnumber = $this->get_idnumber(); - $record->description = $this->get_description(); - $record->descriptionformat = $this->get_descriptionformat(); - $options = array('context' => context_system::instance()); - $record->descriptionformatted = format_text($this->get_description(), $this->get_descriptionformat(), $options); - $record->sortorder = $this->get_sortorder(); - $record->visible = $this->get_visible(); - $record->timecreated = $this->get_timecreated(); - $record->timemodified = $this->get_timemodified(); - $record->usermodified = $this->get_usermodified(); - $record->competencyframeworkid = $this->get_competencyframeworkid(); - $record->parentid = $this->get_parentid(); - $record->path = $this->get_path(); - - return $record; - } - - /** - * Add a default for the sortorder field to the default create logic. - * - * @return persistent - */ - public function create() { - if ($this->parentid) { - // Load the parent so we can set the path. - $parent = new competency($this->parentid); - $this->path = $parent->path . $this->parentid . '/'; - } else { - $this->path = '/0/'; - } - $this->sortorder = $this->count_records(array('parentid' => $this->parentid, - 'competencyframeworkid' => $this->competencyframeworkid)); - return parent::create(); - } - - /** - * Fix all paths when moving to a new parent. - * - * @return persistent - */ - public function update() { - global $DB; - - // See if the parentid changed, if so we have work to do. - $before = new competency($this->get_id()); - if ($before->parentid != $this->parentid) { - if ($this->parentid) { - $parent = new competency($this->parentid); - $this->path = $parent->path . $this->parentid . '/'; - } else { - $this->path = '/0/'; - } - - $search = array('parentid' => $this->parentid, - 'competencyframeworkid' => $this->competencyframeworkid); - $this->sortorder = $this->count_records($search); - - // We need to fix all the paths of the children. - $like = $DB->sql_like('path', '?'); - $likesearch = $DB->sql_like_escape($before->path . $before->get_id() . '/') . '%'; - $sql = 'UPDATE {tool_lp_competency} SET path = REPLACE(path, ?, ?) WHERE ' . $like; - $DB->execute($sql, array($before->path . $this->get_id() . '/', $this->path . $this->get_id() . '/', $likesearch)); - } - // Do the default update. - return parent::update(); + protected function set_new_sortorder() { + $search = array('parentid' => $this->get_parentid(), 'competencyframeworkid' => $this->get_competencyframeworkid()); + $this->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 + * @return persistent[] */ - public function search($searchtext, $competencyframeworkid) { + public static function search($searchtext, $competencyframeworkid) { global $DB; $like1 = $DB->sql_like('shortname', ':like1', false); @@ -391,7 +243,7 @@ class competency extends persistent { ); $sql = 'competencyframeworkid = :frameworkid AND ((' . $like1 . ') OR (' . $like2 . ') OR (' . $like3 . '))'; - $records = $DB->get_records_select($this->get_table_name(), $sql, $params, 'path, sortorder ASC', '*'); + $records = $DB->get_records_select(self::TABLE, $sql, $params, 'path, sortorder ASC', '*'); // Now get all the parents. $parents = array(); @@ -413,7 +265,7 @@ class competency extends persistent { if (count($parents)) { list($parentsql, $parentparams) = $DB->get_in_or_equal($parents, SQL_PARAMS_NAMED); - $parentrecords = $DB->get_records_select($this->get_table_name(), 'id ' . $parentsql, + $parentrecords = $DB->get_records_select(self::TABLE, 'id ' . $parentsql, $parentparams, 'path, sortorder ASC', '*'); foreach ($parentrecords as $id => $record) { @@ -431,22 +283,102 @@ class competency extends persistent { } /** - * Delete a competency (drastic). Will delete all child nodes in the tree. + * Validate the competency framework ID. * - * @return persistent + * @param int $value The framework ID. + * @return true|lang_string */ - public function delete() { - global $DB; + protected function validate_competencyframeworkid($value) { - $deletepath = $DB->sql_like_escape($this->path . $this->get_id() . '/') . '%'; + // During update. + if ($this->get_id()) { - // We need to delete all the children. - $like = $DB->sql_like('path', ':deletepath'); - $DB->delete_records_select('tool_lp_competency', $like, array('deletepath' => $deletepath)); + // Ensure that we are not trying to move the competency across frameworks. + if ($this->beforeupdate->get_competencyframeworkid() != $value) { + return new lang_string('invaliddata', 'error'); + } - // And all the links to courses. - $DB->delete_records('tool_lp_course_competency', array('competencyid' => $this->get_id())); - // Do the default delete. - return parent::delete(); + // During create. + } else { + + // 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'); + + // The format of the path should be as follows. + } else if (!preg_match('@/([0-9]+/)+@', $value)) { + + 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 && !competency::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; + } + } diff --git a/admin/tool/lp/classes/external.php b/admin/tool/lp/classes/external.php index 65a1bd9896c..99c45cd920a 100644 --- a/admin/tool/lp/classes/external.php +++ b/admin/tool/lp/classes/external.php @@ -906,7 +906,10 @@ class external extends external_api { $params = (object) $params; $result = api::create_competency($params); - return $result->to_record(); + $record = $result->to_record(); + $record->descriptionformatted = format_text($record->description, $record->descriptionformat, + array('context' => context_system::instance())); + return $record; } /** @@ -957,7 +960,10 @@ class external extends external_api { )); $result = api::read_competency($params['id']); - return $result->to_record(); + $record = $result->to_record(); + $options = array('context' => context_system::instance()); + $record->descriptionformatted = format_text($record->description, $record->descriptionformat, $options); + return $record; } /** @@ -1175,8 +1181,10 @@ class external extends external_api { $params['skip'], $params['limit']); $records = array(); + $options = array('context' => context_system::instance()); foreach ($results as $result) { $record = $result->to_record(); + $record->descriptionformatted = format_text($record->description, $record->descriptionformat, $options); array_push($records, $record); } return $records; @@ -1244,9 +1252,11 @@ class external extends external_api { )); $results = api::search_competencies($searchtext, $competencyframeworkid); + $options = array('context' => context_system::instance()); $records = array(); foreach ($results as $result) { $record = $result->to_record(); + $record->descriptionformatted = format_text($record->description, $record->descriptionformat, $options); array_push($records, $record); } return $records; @@ -1765,9 +1775,11 @@ class external extends external_api { )); $competencies = api::list_competencies_in_course($params['id']); + $options = array('context' => context_system::instance()); $results = array(); foreach ($competencies as $competency) { $record = $competency->to_record(); + $record->descriptionformatted = format_text($record->description, $record->descriptionformat, $options); array_push($results, $record); } return $results; @@ -2886,9 +2898,11 @@ class external extends external_api { )); $competencies = api::list_competencies_in_template($params['id']); + $options = array('context' => context_system::instance()); $results = array(); foreach ($competencies as $competency) { $record = $competency->to_record(); + $record->descriptionformatted = format_text($record->description, $record->descriptionformat, $options); array_push($results, $record); } return $results; diff --git a/admin/tool/lp/classes/output/course_competencies_page.php b/admin/tool/lp/classes/output/course_competencies_page.php index fa3e0dc680b..5b536b1e3f0 100644 --- a/admin/tool/lp/classes/output/course_competencies_page.php +++ b/admin/tool/lp/classes/output/course_competencies_page.php @@ -94,8 +94,10 @@ class course_competencies_page implements renderable, templatable { $data->courseid = $this->courseid; $data->pagecontextid = $this->context->id; $data->competencies = array(); + $options = array('context' => context_system::instance()); foreach ($this->competencies as $competency) { $record = $competency->to_record(); + $record->descriptionformatted = format_text($record->description, $record->descriptionformat, $options); array_push($data->competencies, $record); } $data->canmanagecompetencyframeworks = $this->canmanagecompetencyframeworks; diff --git a/admin/tool/lp/classes/output/manage_competencies_page.php b/admin/tool/lp/classes/output/manage_competencies_page.php index bf098741722..8d7bd142bd2 100644 --- a/admin/tool/lp/classes/output/manage_competencies_page.php +++ b/admin/tool/lp/classes/output/manage_competencies_page.php @@ -112,10 +112,12 @@ class manage_competencies_page implements renderable, templatable { $data->competencies = array(); $data->search = $this->search; $data->pagecontextid = $this->pagecontext->id; + $options = array('context' => context_system::instance()); foreach ($this->competencies as $competency) { if ($competency->get_parentid() == 0) { $record = $competency->to_record(); + $record->descriptionformatted = format_text($record->description, $record->descriptionformat, $options); $record->children = array(); $record->haschildren = false; $data->competencies[] = $record; diff --git a/admin/tool/lp/classes/output/template_competencies_page.php b/admin/tool/lp/classes/output/template_competencies_page.php index 050ca85abdd..0066961c401 100644 --- a/admin/tool/lp/classes/output/template_competencies_page.php +++ b/admin/tool/lp/classes/output/template_competencies_page.php @@ -83,8 +83,10 @@ class template_competencies_page implements renderable, templatable { $data->pagecontextid = $this->pagecontext->id; $data->templateid = $this->templateid; $data->competencies = array(); + $options = array('context' => context_system::instance()); foreach ($this->competencies as $competency) { $record = $competency->to_record(); + $record = format_text($record->description, $record->descriptionformat, $options); array_push($data->competencies, $record); } $data->canmanagecompetencyframeworks = $this->canmanagecompetencyframeworks;