Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Class for loading/storing competencies from the DB. 19 * 20 * @package core_competency 21 * @copyright 2015 Damyon Wiese 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 namespace core_competency; 25 defined('MOODLE_INTERNAL') || die(); 26 27 use coding_exception; 28 use context_system; 29 use lang_string; 30 use stdClass; 31 32 require_once($CFG->libdir . '/grade/grade_scale.php'); 33 34 /** 35 * Class for loading/storing competencies from the DB. 36 * 37 * @copyright 2015 Damyon Wiese 38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 */ 40 class competency extends persistent { 41 42 const TABLE = 'competency'; 43 44 /** Outcome none. */ 45 const OUTCOME_NONE = 0; 46 /** Outcome evidence. */ 47 const OUTCOME_EVIDENCE = 1; 48 /** Outcome complete. */ 49 const OUTCOME_COMPLETE = 2; 50 /** Outcome recommend. */ 51 const OUTCOME_RECOMMEND = 3; 52 53 /** @var competency Object before update. */ 54 protected $beforeupdate = null; 55 56 /** @var competency|null To store new parent. */ 57 protected $newparent; 58 59 /** 60 * Return the definition of the properties of this model. 61 * 62 * @return array 63 */ 64 protected static function define_properties() { 65 return array( 66 'shortname' => array( 67 'type' => PARAM_TEXT 68 ), 69 'idnumber' => array( 70 'type' => PARAM_RAW 71 ), 72 'description' => array( 73 'default' => '', 74 'type' => PARAM_CLEANHTML 75 ), 76 'descriptionformat' => array( 77 'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN), 78 'type' => PARAM_INT, 79 'default' => FORMAT_HTML 80 ), 81 'sortorder' => array( 82 'default' => 0, 83 'type' => PARAM_INT 84 ), 85 'parentid' => array( 86 'default' => 0, 87 'type' => PARAM_INT 88 ), 89 'path' => array( 90 'default' => '/0/', 91 'type' => PARAM_RAW 92 ), 93 'ruleoutcome' => array( 94 'choices' => array(self::OUTCOME_NONE, self::OUTCOME_EVIDENCE, self::OUTCOME_COMPLETE, self::OUTCOME_RECOMMEND), 95 'default' => self::OUTCOME_NONE, 96 'type' => PARAM_INT 97 ), 98 'ruletype' => array( 99 'type' => PARAM_RAW, 100 'default' => null, 101 'null' => NULL_ALLOWED 102 ), 103 'ruleconfig' => array( 104 'default' => null, 105 'type' => PARAM_RAW, 106 'null' => NULL_ALLOWED 107 ), 108 'scaleid' => array( 109 'default' => null, 110 'type' => PARAM_INT, 111 'null' => NULL_ALLOWED 112 ), 113 'scaleconfiguration' => array( 114 'default' => null, 115 'type' => PARAM_RAW, 116 'null' => NULL_ALLOWED 117 ), 118 'competencyframeworkid' => array( 119 'default' => 0, 120 'type' => PARAM_INT 121 ), 122 ); 123 } 124 125 /** 126 * Hook to execute before validate. 127 * 128 * @return void 129 */ 130 protected function before_validate() { 131 $this->beforeupdate = null; 132 $this->newparent = null; 133 134 // During update. 135 if ($this->get('id')) { 136 $this->beforeupdate = new competency($this->get('id')); 137 138 // The parent ID has changed. 139 if ($this->beforeupdate->get('parentid') != $this->get('parentid')) { 140 $this->newparent = $this->get_parent(); 141 142 // Update path and sortorder. 143 $this->set_new_path($this->newparent); 144 $this->set_new_sortorder(); 145 } 146 147 } else { 148 // During create. 149 150 $this->set_new_path(); 151 // Always generate new sortorder when we create new competency. 152 $this->set_new_sortorder(); 153 154 } 155 } 156 157 /** 158 * Hook to execute after an update. 159 * 160 * @param bool $result Whether or not the update was successful. 161 * @return void 162 */ 163 protected function after_update($result) { 164 global $DB; 165 166 if (!$result) { 167 $this->beforeupdate = null; 168 return; 169 } 170 171 // The parent ID has changed, we need to fix all the paths of the children. 172 if ($this->beforeupdate->get('parentid') != $this->get('parentid')) { 173 $beforepath = $this->beforeupdate->get('path') . $this->get('id') . '/'; 174 175 $like = $DB->sql_like('path', '?'); 176 $likesearch = $DB->sql_like_escape($beforepath) . '%'; 177 178 $table = '{' . self::TABLE . '}'; 179 $sql = "UPDATE $table SET path = REPLACE(path, ?, ?) WHERE " . $like; 180 $DB->execute($sql, array( 181 $beforepath, 182 $this->get('path') . $this->get('id') . '/', 183 $likesearch 184 )); 185 186 // Resolving sortorder holes left after changing parent. 187 $table = '{' . self::TABLE . '}'; 188 $sql = "UPDATE $table SET sortorder = sortorder -1 " 189 . " WHERE competencyframeworkid = ? AND parentid = ? AND sortorder > ?"; 190 $DB->execute($sql, array($this->get('competencyframeworkid'), 191 $this->beforeupdate->get('parentid'), 192 $this->beforeupdate->get('sortorder') 193 )); 194 } 195 196 $this->beforeupdate = null; 197 } 198 199 200 /** 201 * Hook to execute after a delete. 202 * 203 * @param bool $result Whether or not the delete was successful. 204 * @return void 205 */ 206 protected function after_delete($result) { 207 global $DB; 208 if (!$result) { 209 return; 210 } 211 212 // Resolving sortorder holes left after delete. 213 $table = '{' . self::TABLE . '}'; 214 $sql = "UPDATE $table SET sortorder = sortorder -1 WHERE competencyframeworkid = ? AND parentid = ? AND sortorder > ?"; 215 $DB->execute($sql, array($this->get('competencyframeworkid'), $this->get('parentid'), $this->get('sortorder'))); 216 } 217 218 /** 219 * Extracts the default grade from the scale configuration. 220 * 221 * Returns an array where the first element is the grade, and the second 222 * is a boolean representing whether or not this grade is considered 'proficient'. 223 * 224 * @return array(int grade, bool proficient) 225 */ 226 public function get_default_grade() { 227 $scaleid = $this->get('scaleid'); 228 $scaleconfig = $this->get('scaleconfiguration'); 229 if ($scaleid === null) { 230 $scaleconfig = $this->get_framework()->get('scaleconfiguration'); 231 } 232 return competency_framework::get_default_grade_from_scale_configuration($scaleconfig); 233 } 234 235 /** 236 * Get the competency framework. 237 * 238 * @return competency_framework 239 */ 240 public function get_framework() { 241 return new competency_framework($this->get('competencyframeworkid')); 242 } 243 244 /** 245 * Get the competency level. 246 * 247 * @return int 248 */ 249 public function get_level() { 250 $path = $this->get('path'); 251 $path = trim($path, '/'); 252 return substr_count($path, '/') + 1; 253 } 254 255 /** 256 * Return the parent competency. 257 * 258 * @return null|competency 259 */ 260 public function get_parent() { 261 $parentid = $this->get('parentid'); 262 if (!$parentid) { 263 return null; 264 } 265 return new competency($parentid); 266 } 267 268 /** 269 * Extracts the proficiency of a grade from the scale configuration. 270 * 271 * @param int $grade The grade (scale item ID). 272 * @return array(int grade, bool proficient) 273 */ 274 public function get_proficiency_of_grade($grade) { 275 $scaleid = $this->get('scaleid'); 276 $scaleconfig = $this->get('scaleconfiguration'); 277 if ($scaleid === null) { 278 $scaleconfig = $this->get_framework()->get('scaleconfiguration'); 279 } 280 return competency_framework::get_proficiency_of_grade_from_scale_configuration($scaleconfig, $grade); 281 } 282 283 /** 284 * Return the related competencies. 285 * 286 * @return competency[] 287 */ 288 public function get_related_competencies() { 289 return related_competency::get_related_competencies($this->get('id')); 290 } 291 292 /** 293 * Get the rule object. 294 * 295 * @return null|competency_rule 296 */ 297 public function get_rule_object() { 298 $rule = $this->get('ruletype'); 299 300 if (!$rule || !is_subclass_of($rule, 'core_competency\\competency_rule')) { 301 // Double check that the rule is extending the right class to avoid bad surprises. 302 return null; 303 } 304 305 return new $rule($this); 306 } 307 308 /** 309 * Return the scale. 310 * 311 * @return \grade_scale 312 */ 313 public function get_scale() { 314 $scaleid = $this->get('scaleid'); 315 if ($scaleid === null) { 316 return $this->get_framework()->get_scale(); 317 } 318 $scale = \grade_scale::fetch(array('id' => $scaleid)); 319 $scale->load_items(); 320 return $scale; 321 } 322 323 /** 324 * Returns true when the competency has user competencies. 325 * 326 * This is useful to determine if the competency, or part of it, should be locked down. 327 * 328 * @return boolean 329 */ 330 public function has_user_competencies() { 331 return user_competency::has_records_for_competency($this->get('id')) || 332 user_competency_plan::has_records_for_competency($this->get('id')); 333 } 334 335 /** 336 * Check if the competency is the parent of passed competencies. 337 * 338 * @param array $ids IDs of supposedly direct children. 339 * @return boolean 340 */ 341 public function is_parent_of(array $ids) { 342 global $DB; 343 344 list($insql, $params) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED); 345 $params['parentid'] = $this->get('id'); 346 347 return $DB->count_records_select(self::TABLE, "id $insql AND parentid = :parentid", $params) == count($ids); 348 } 349 350 /** 351 * Reset the rule. 352 * 353 * @return void 354 */ 355 public function reset_rule() { 356 $this->raw_set('ruleoutcome', static::OUTCOME_NONE); 357 $this->raw_set('ruletype', null); 358 $this->raw_set('ruleconfig', null); 359 } 360 361 /** 362 * Helper method to set the path. 363 * 364 * @param competency $parent The parent competency object. 365 * @return void 366 */ 367 protected function set_new_path(competency $parent = null) { 368 $path = '/0/'; 369 if ($this->get('parentid')) { 370 $parent = $parent !== null ? $parent : $this->get_parent(); 371 $path = $parent->get('path') . $this->get('parentid') . '/'; 372 } 373 $this->raw_set('path', $path); 374 } 375 376 /** 377 * Helper method to set the sortorder. 378 * 379 * @return void 380 */ 381 protected function set_new_sortorder() { 382 $search = array('parentid' => $this->get('parentid'), 'competencyframeworkid' => $this->get('competencyframeworkid')); 383 $this->raw_set('sortorder', $this->count_records($search)); 384 } 385 386 /** 387 * This does a specialised search that finds all nodes in the tree with matching text on any text like field, 388 * and returns this node and all its parents in a displayable sort order. 389 * 390 * @param string $searchtext The text to search for. 391 * @param int $competencyframeworkid The competency framework to limit the search. 392 * @return persistent[] 393 */ 394 public static function search($searchtext, $competencyframeworkid) { 395 global $DB; 396 397 $like1 = $DB->sql_like('shortname', ':like1', false); 398 $like2 = $DB->sql_like('idnumber', ':like2', false); 399 $like3 = $DB->sql_like('description', ':like3', false); 400 401 $params = array( 402 'like1' => '%' . $DB->sql_like_escape($searchtext) . '%', 403 'like2' => '%' . $DB->sql_like_escape($searchtext) . '%', 404 'like3' => '%' . $DB->sql_like_escape($searchtext) . '%', 405 'frameworkid' => $competencyframeworkid 406 ); 407 408 $sql = 'competencyframeworkid = :frameworkid AND ((' . $like1 . ') OR (' . $like2 . ') OR (' . $like3 . '))'; 409 $records = $DB->get_records_select(self::TABLE, $sql, $params, 'path, sortorder ASC', '*'); 410 411 // Now get all the parents. 412 $parents = array(); 413 foreach ($records as $record) { 414 $split = explode('/', trim($record->path, '/')); 415 foreach ($split as $parent) { 416 $parents[intval($parent)] = true; 417 } 418 } 419 $parents = array_keys($parents); 420 421 // Skip ones we already fetched. 422 foreach ($parents as $idx => $parent) { 423 if ($parent == 0 || isset($records[$parent])) { 424 unset($parents[$idx]); 425 } 426 } 427 428 if (count($parents)) { 429 list($parentsql, $parentparams) = $DB->get_in_or_equal($parents, SQL_PARAMS_NAMED); 430 431 $parentrecords = $DB->get_records_select(self::TABLE, 'id ' . $parentsql, 432 $parentparams, 'path, sortorder ASC', '*'); 433 434 foreach ($parentrecords as $id => $record) { 435 $records[$id] = $record; 436 } 437 } 438 439 $instances = array(); 440 // Convert to instances of this class. 441 foreach ($records as $record) { 442 $newrecord = new static(0, $record); 443 $instances[$newrecord->get('id')] = $newrecord; 444 } 445 return $instances; 446 } 447 448 /** 449 * Validate the competency framework ID. 450 * 451 * @param int $value The framework ID. 452 * @return true|lang_string 453 */ 454 protected function validate_competencyframeworkid($value) { 455 456 // During update. 457 if ($this->get('id')) { 458 459 // Ensure that we are not trying to move the competency across frameworks. 460 if ($this->beforeupdate->get('competencyframeworkid') != $value) { 461 return new lang_string('invaliddata', 'error'); 462 } 463 464 } else { 465 // During create. 466 467 // Check that the framework exists. 468 if (!competency_framework::record_exists($value)) { 469 return new lang_string('invaliddata', 'error'); 470 } 471 } 472 473 return true; 474 } 475 476 /** 477 * Validate the ID number. 478 * 479 * @param string $value The ID number. 480 * @return true|lang_string 481 */ 482 protected function validate_idnumber($value) { 483 global $DB; 484 $sql = 'idnumber = :idnumber AND competencyframeworkid = :competencyframeworkid AND id <> :id'; 485 $params = array( 486 'id' => $this->get('id'), 487 'idnumber' => $value, 488 'competencyframeworkid' => $this->get('competencyframeworkid') 489 ); 490 if ($DB->record_exists_select(self::TABLE, $sql, $params)) { 491 return new lang_string('idnumbertaken', 'error'); 492 } 493 return true; 494 } 495 496 /** 497 * Validate the path. 498 * 499 * @param string $value The path. 500 * @return true|lang_string 501 */ 502 protected function validate_path($value) { 503 504 // The last item should be the parent ID. 505 $id = $this->get('parentid'); 506 if (substr($value, -(strlen($id) + 2)) != '/' . $id . '/') { 507 return new lang_string('invaliddata', 'error'); 508 509 } else if (!preg_match('@/([0-9]+/)+@', $value)) { 510 // The format of the path is not correct. 511 return new lang_string('invaliddata', 'error'); 512 } 513 514 return true; 515 } 516 517 /** 518 * Validate the parent ID. 519 * 520 * @param string $value The ID. 521 * @return true|lang_string 522 */ 523 protected function validate_parentid($value) { 524 525 // Check that the parent exists. But only if we don't have it already, and we actually have a parent. 526 if (!empty($value) && !$this->newparent && !self::record_exists($value)) { 527 return new lang_string('invaliddata', 'error'); 528 } 529 530 // During update. 531 if ($this->get('id')) { 532 533 // If there is a new parent. 534 if ($this->beforeupdate->get('parentid') != $value && $this->newparent) { 535 536 // Check that the new parent belongs to the same framework. 537 if ($this->newparent->get('competencyframeworkid') != $this->get('competencyframeworkid')) { 538 return new lang_string('invaliddata', 'error'); 539 } 540 } 541 } 542 543 return true; 544 } 545 546 /** 547 * Validate the rule. 548 * 549 * @param string $value The ID. 550 * @return true|lang_string 551 */ 552 protected function validate_ruletype($value) { 553 if ($value === null) { 554 return true; 555 } 556 557 if (!class_exists($value) || !is_subclass_of($value, 'core_competency\\competency_rule')) { 558 return new lang_string('invaliddata', 'error'); 559 } 560 561 return true; 562 } 563 564 /** 565 * Validate the rule config. 566 * 567 * @param string $value The ID. 568 * @return true|lang_string 569 */ 570 protected function validate_ruleconfig($value) { 571 $rule = $this->get_rule_object(); 572 573 // We don't have a rule. 574 if (empty($rule)) { 575 if ($value === null) { 576 // No config, perfect. 577 return true; 578 } 579 // Config but no rules, whoops! 580 return new lang_string('invaliddata', 'error'); 581 } 582 583 $valid = $rule->validate_config($value); 584 if ($valid !== true) { 585 // Whoops! 586 return new lang_string('invaliddata', 'error'); 587 } 588 589 return true; 590 } 591 592 /** 593 * Validate the scale ID. 594 * 595 * Note that the value for a scale can never be 0, null has to be used when 596 * the framework's scale has to be used. 597 * 598 * @param int $value 599 * @return true|lang_string 600 */ 601 protected function validate_scaleid($value) { 602 global $DB; 603 604 if ($value === null) { 605 return true; 606 } 607 608 // Always validate that the scale exists. 609 if (!$DB->record_exists_select('scale', 'id = :id', array('id' => $value))) { 610 return new lang_string('invalidscaleid', 'error'); 611 } 612 613 // During update. 614 if ($this->get('id')) { 615 616 // Validate that we can only change the scale when it is not used yet. 617 if ($this->beforeupdate->get('scaleid') != $value) { 618 if ($this->has_user_competencies()) { 619 return new lang_string('errorscalealreadyused', 'core_competency'); 620 } 621 } 622 623 } 624 625 return true; 626 } 627 628 /** 629 * Validate the scale configuration. 630 * 631 * This logic is adapted from {@link \core_competency\competency_framework::validate_scaleconfiguration()}. 632 * 633 * @param string $value The scale configuration. 634 * @return bool|lang_string 635 */ 636 protected function validate_scaleconfiguration($value) { 637 $scaleid = $this->get('scaleid'); 638 if ($scaleid === null && $value === null) { 639 return true; 640 } 641 642 $scaledefaultselected = false; 643 $proficientselected = false; 644 $scaleconfigurations = json_decode($value); 645 646 if (is_array($scaleconfigurations)) { 647 648 // The first element of the array contains the scale ID. 649 $scaleinfo = array_shift($scaleconfigurations); 650 if (empty($scaleinfo) || !isset($scaleinfo->scaleid) || $scaleinfo->scaleid != $scaleid) { 651 // This should never happen. 652 return new lang_string('errorscaleconfiguration', 'core_competency'); 653 } 654 655 // Walk through the array to find proficient and default values. 656 foreach ($scaleconfigurations as $scaleconfiguration) { 657 if (isset($scaleconfiguration->scaledefault) && $scaleconfiguration->scaledefault) { 658 $scaledefaultselected = true; 659 } 660 if (isset($scaleconfiguration->proficient) && $scaleconfiguration->proficient) { 661 $proficientselected = true; 662 } 663 } 664 } 665 666 if (!$scaledefaultselected || !$proficientselected) { 667 return new lang_string('errorscaleconfiguration', 'core_competency'); 668 } 669 670 return true; 671 } 672 673 /** 674 * Return whether or not the competency IDs share the same framework. 675 * 676 * @param array $ids Competency IDs 677 * @return bool 678 */ 679 public static function share_same_framework(array $ids) { 680 global $DB; 681 list($insql, $params) = $DB->get_in_or_equal($ids); 682 $sql = "SELECT COUNT('x') FROM (SELECT DISTINCT(competencyframeworkid) FROM {" . self::TABLE . "} WHERE id {$insql}) f"; 683 return $DB->count_records_sql($sql, $params) == 1; 684 } 685 686 /** 687 * Get the available rules. 688 * 689 * @return array Keys are the class names, values are the name of the rule. 690 */ 691 public static function get_available_rules() { 692 // Fully qualified class names without leading slashes because get_class() does not add them either. 693 $rules = array( 694 'core_competency\\competency_rule_all' => competency_rule_all::get_name(), 695 'core_competency\\competency_rule_points' => competency_rule_points::get_name(), 696 ); 697 return $rules; 698 } 699 700 /** 701 * Return the current depth of a competency framework. 702 * 703 * @param int $frameworkid The framework ID. 704 * @return int 705 */ 706 public static function get_framework_depth($frameworkid) { 707 global $DB; 708 $totallength = $DB->sql_length('path'); 709 $trimmedlength = $DB->sql_length("REPLACE(path, '/', '')"); 710 $sql = "SELECT ($totallength - $trimmedlength - 1) AS depth 711 FROM {" . self::TABLE . "} 712 WHERE competencyframeworkid = :id 713 ORDER BY depth DESC"; 714 $record = $DB->get_record_sql($sql, array('id' => $frameworkid), IGNORE_MULTIPLE); 715 if (!$record) { 716 $depth = 0; 717 } else { 718 $depth = $record->depth; 719 } 720 return $depth; 721 } 722 723 /** 724 * Build a framework tree with competency nodes. 725 * 726 * @param int $frameworkid the framework id 727 * @return stdClass[] tree of framework competency nodes 728 */ 729 public static function get_framework_tree($frameworkid) { 730 $competencies = self::search('', $frameworkid); 731 return self::build_tree($competencies, 0); 732 } 733 734 /** 735 * Get the context from the framework. 736 * 737 * @return \context 738 */ 739 public function get_context() { 740 return $this->get_framework()->get_context(); 741 } 742 743 /** 744 * Recursively build up the tree of nodes. 745 * 746 * @param array $all - List of all competency classes. 747 * @param int $parentid - The current parent ID. Pass 0 to build the tree from the top. 748 * @return stdClass[] $tree tree of nodes 749 */ 750 protected static function build_tree($all, $parentid) { 751 $tree = array(); 752 foreach ($all as $one) { 753 if ($one->get('parentid') == $parentid) { 754 $node = new stdClass(); 755 $node->competency = $one; 756 $node->children = self::build_tree($all, $one->get('id')); 757 $tree[] = $node; 758 } 759 } 760 return $tree; 761 } 762 763 /** 764 * Check if we can delete competencies safely. 765 * 766 * This moethod does not check any capablities. 767 * Check if competency is used in a plan and user competency. 768 * Check if competency is used in a template. 769 * Check if competency is linked to a course. 770 * 771 * @param array $ids Array of competencies ids. 772 * @return bool True if we can delete the competencies. 773 */ 774 public static function can_all_be_deleted($ids) { 775 global $CFG; 776 777 if (empty($ids)) { 778 return true; 779 } 780 // Check if competency is used in template. 781 if (template_competency::has_records_for_competencies($ids)) { 782 return false; 783 } 784 // Check if competency is used in plan. 785 if (plan_competency::has_records_for_competencies($ids)) { 786 return false; 787 } 788 // Check if competency is used in course. 789 if (course_competency::has_records_for_competencies($ids)) { 790 return false; 791 } 792 // Check if competency is used in user_competency. 793 if (user_competency::has_records_for_competencies($ids)) { 794 return false; 795 } 796 // Check if competency is used in user_competency_plan. 797 if (user_competency_plan::has_records_for_competencies($ids)) { 798 return false; 799 } 800 801 require_once($CFG->libdir . '/badgeslib.php'); 802 // Check if competency is used in a badge. 803 if (badge_award_criteria_competency_has_records_for_competencies($ids)) { 804 return false; 805 } 806 807 return true; 808 } 809 810 /** 811 * Delete the competencies. 812 * 813 * This method is reserved to core usage. 814 * This method does not trigger the after_delete event. 815 * This method does not delete related objects such as related competencies and evidences. 816 * 817 * @param array $ids The competencies ids. 818 * @return bool True if the competencies were deleted successfully. 819 */ 820 public static function delete_multiple($ids) { 821 global $DB; 822 list($insql, $params) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED); 823 return $DB->delete_records_select(self::TABLE, "id $insql", $params); 824 } 825 826 /** 827 * Get descendant ids. 828 * 829 * @param competency $competency The competency. 830 * @return array Array of competencies ids. 831 */ 832 public static function get_descendants_ids($competency) { 833 global $DB; 834 835 $path = $DB->sql_like_escape($competency->get('path') . $competency->get('id') . '/') . '%'; 836 $like = $DB->sql_like('path', ':likepath'); 837 return $DB->get_fieldset_select(self::TABLE, 'id', $like, array('likepath' => $path)); 838 } 839 840 /** 841 * Get competencyids by frameworkid. 842 * 843 * @param int $frameworkid The competency framework ID. 844 * @return array Array of competency ids. 845 */ 846 public static function get_ids_by_frameworkid($frameworkid) { 847 global $DB; 848 849 return $DB->get_fieldset_select(self::TABLE, 'id', 'competencyframeworkid = :frmid', array('frmid' => $frameworkid)); 850 } 851 852 /** 853 * Delete competencies by framework ID. 854 * 855 * This method is reserved to core usage. 856 * This method does not trigger the after_delete event. 857 * This method does not delete related objects such as related competencies and evidences. 858 * 859 * @param int $id the framework ID 860 * @return bool Return true if delete was successful. 861 */ 862 public static function delete_by_frameworkid($id) { 863 global $DB; 864 return $DB->delete_records(self::TABLE, array('competencyframeworkid' => $id)); 865 } 866 867 /** 868 * Get competency ancestors. 869 * 870 * @return competency[] Return array of ancestors. 871 */ 872 public function get_ancestors() { 873 global $DB; 874 $ancestors = array(); 875 $ancestorsids = explode('/', trim($this->get('path'), '/')); 876 // Drop the root item from the array /0/. 877 array_shift($ancestorsids); 878 if (!empty($ancestorsids)) { 879 list($insql, $params) = $DB->get_in_or_equal($ancestorsids, SQL_PARAMS_NAMED); 880 $ancestors = self::get_records_select("id $insql", $params); 881 } 882 return $ancestors; 883 } 884 885 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body