| Línea 231... | Línea 231... | 
          
            | 231 |  * 1/ All questions are deleted for this question category.
 | 231 |  * 1/ All questions are deleted for this question category.
 | 
          
            | 232 |  * 2/ Any questions that can't be deleted are moved to a new category
 | 232 |  * 2/ Any questions that can't be deleted are moved to a new category
 | 
          
            | 233 |  * NOTE: this function is called from lib/db/upgrade.php
 | 233 |  * NOTE: this function is called from lib/db/upgrade.php
 | 
          
            | 234 |  *
 | 234 |  *
 | 
          
            | 235 |  * @param object|core_course_category $category course category object
 | 235 |  * @param object|core_course_category $category course category object
 | 
          
            | - |   | 236 |  * @param bool $coursedeletion Is the course this category is under being deleted? If so, move saved questions to the site course.
 | 
          
            | 236 |  */
 | 237 |  */
 | 
          
            | 237 | function question_category_delete_safe($category): void {
 | 238 | function question_category_delete_safe($category, bool $coursedeletion = false): void {
 | 
          
            | 238 |     global $DB;
 | 239 |     global $DB;
 | 
          
            | 239 |     $criteria = ['questioncategoryid' => $category->id];
 | 240 |     $criteria = ['questioncategoryid' => $category->id];
 | 
          
            | 240 |     $context = context::instance_by_id($category->contextid, IGNORE_MISSING);
 | 241 |     $context = context::instance_by_id($category->contextid, IGNORE_MISSING);
 | 
          
            | 241 |     $rescue = null; // See the code around the call to question_save_from_deletion.
 | 242 |     $rescue = null; // See the code around the call to question_save_from_deletion.
 | 
          
            | Línea 264... | Línea 265... | 
          
            | 264 |             foreach ($versions as $key => $version) {
 | 265 |             foreach ($versions as $key => $version) {
 | 
          
            | 265 |                 $questionids[$key] = $version;
 | 266 |                 $questionids[$key] = $version;
 | 
          
            | 266 |             }
 | 267 |             }
 | 
          
            | 267 |         }
 | 268 |         }
 | 
          
            | 268 |         if (!empty($questionids)) {
 | 269 |         if (!empty($questionids)) {
 | 
          
            | 269 |             $parentcontextid = SYSCONTEXTID;
 | - |   | 
          
            | 270 |             $name = get_string('unknown', 'question');
 | 270 |             $name = get_string('unknown', 'question');
 | 
          
            | 271 |             if ($context !== false) {
 | 271 |             if ($context !== false) {
 | 
          
            | 272 |                 $name = $context->get_context_name();
 | 272 |                 $name = $context->get_context_name();
 | 
          
            | 273 |                 $parentcontext = $context->get_parent_context();
 | 273 |                 $parentcontext = $context->get_course_context(false);
 | 
          
            | 274 |                 if ($parentcontext) {
 | - |   | 
          
            | 275 |                     $parentcontextid = $parentcontext->id;
 | 274 |                 $course = ($parentcontext && !$coursedeletion) ? get_course($parentcontext->instanceid) : get_site();
 | 
          
            | 276 |                 }
 | - |   | 
          
            | 277 |             }
 | 275 |             }
 | 
          
            | - |   | 276 |             $qbank = core_question\local\bank\question_bank_helper::get_default_open_instance_system_type($course, true);
 | 
          
            | 278 |             question_save_from_deletion(array_keys($questionids), $parentcontextid, $name, $rescue);
 | 277 |             question_save_from_deletion(array_keys($questionids), $qbank->context->id, $name, $rescue);
 | 
          
            | 279 |         }
 | 278 |         }
 | 
          
            | 280 |     }
 | 279 |     }
 | 
          
            | Línea 281... | Línea 280... | 
          
            | 281 |  
 | 280 |  
 | 
          
            | 282 |     // Now delete the category.
 | 281 |     // Now delete the category.
 | 
          
            | Línea 422... | Línea 421... | 
          
            | 422 |  
 | 421 |  
 | 
          
            | 423 | /**
 | 422 | /**
 | 
          
            | 424 |  * All question categories and their questions are deleted for this context id.
 | 423 |  * All question categories and their questions are deleted for this context id.
 | 
          
            | 425 |  *
 | 424 |  *
 | 
          
            | - |   | 425 |  * @param int $contextid The contextid to delete question categories from
 | 
          
            | 426 |  * @param int $contextid The contextid to delete question categories from
 | 426 |  * @param bool $coursedeletion Are we calling this as part of deleting the course the context is under?
 | 
          
            | 427 |  * @return array only returns an empty array for backwards compatibility.
 | 427 |  * @return array only returns an empty array for backwards compatibility.
 | 
          
            | 428 |  */
 | 428 |  */
 | 
          
            | 429 | function question_delete_context($contextid): array {
 | 429 | function question_delete_context($contextid, bool $coursedeletion = false): array {
 | 
          
            | Línea 430... | Línea 430... | 
          
            | 430 |     global $DB;
 | 430 |     global $DB;
 | 
          
            | 431 |  
 | 431 |  
 | 
          
            | 432 |     $fields = 'id, parent, name, contextid';
 | 432 |     $fields = 'id, parent, name, contextid';
 | 
          
            | 433 |     if ($categories = $DB->get_records('question_categories', ['contextid' => $contextid], 'parent', $fields)) {
 | 433 |     if ($categories = $DB->get_records('question_categories', ['contextid' => $contextid], 'parent', $fields)) {
 | 
          
            | 434 |         // Sort categories following their tree (parent-child) relationships this will make the feedback more readable.
 | 434 |         // Sort categories following their tree (parent-child) relationships this will make the feedback more readable.
 | 
          
            | 435 |         $categories = sort_categories_by_tree($categories);
 | 435 |         $categories = sort_categories_by_tree($categories);
 | 
          
            | 436 |         foreach ($categories as $category) {
 | 436 |         foreach ($categories as $category) {
 | 
          
            | 437 |             question_category_delete_safe($category);
 | 437 |             question_category_delete_safe($category, $coursedeletion);
 | 
          
            | 438 |         }
 | 438 |         }
 | 
          
            | 439 |     }
 | 439 |     }
 | 
          
            | Línea 440... | Línea 440... | 
          
            | 440 |     return [];
 | 440 |     return [];
 | 
          
            | 441 | }
 | - |   | 
          
            | 442 |  
 | - |   | 
          
            | 443 | /**
 | - |   | 
          
            | 444 |  * All question categories and their questions are deleted for this course.
 | - |   | 
          
            | 445 |  *
 | - |   | 
          
            | 446 |  * @param stdClass $course an object representing the activity
 | - |   | 
          
            | 447 |  * @param bool $notused this argument is not used any more. Kept for backwards compatibility.
 | - |   | 
          
            | 448 |  * @return bool always true.
 | - |   | 
          
            | 449 |  */
 | - |   | 
          
            | 450 | function question_delete_course($course, $notused = false): bool {
 | - |   | 
          
            | 451 |     $coursecontext = context_course::instance($course->id);
 | - |   | 
          
            | 452 |     question_delete_context($coursecontext->id);
 | - |   | 
          
            | 453 |     return true;
 | - |   | 
          
            | 454 | }
 | - |   | 
          
            | 455 |  
 | - |   | 
          
            | 456 | /**
 | - |   | 
          
            | 457 |  * Category is about to be deleted,
 | - |   | 
          
            | 458 |  * 1/ All question categories and their questions are deleted for this course category.
 | - |   | 
          
            | 459 |  * 2/ All questions are moved to new category
 | - |   | 
          
            | 460 |  *
 | - |   | 
          
            | 461 |  * @param stdClass|core_course_category $category course category object
 | - |   | 
          
            | 462 |  * @param stdClass|core_course_category $newcategory empty means everything deleted, otherwise id of
 | - |   | 
          
            | 463 |  *      category where content moved
 | - |   | 
          
            | 464 |  * @param bool $notused this argument is no longer used. Kept for backwards compatibility.
 | - |   | 
          
            | 465 |  * @return boolean
 | - |   | 
          
            | 466 |  */
 | - |   | 
          
            | 467 | function question_delete_course_category($category, $newcategory, $notused=false): bool {
 | - |   | 
          
            | 468 |     global $DB;
 | - |   | 
          
            | 469 |  
 | - |   | 
          
            | 470 |     $context = context_coursecat::instance($category->id);
 | - |   | 
          
            | 471 |     if (empty($newcategory)) {
 | - |   | 
          
            | 472 |         question_delete_context($context->id);
 | - |   | 
          
            | 473 |  
 | - |   | 
          
            | 474 |     } else {
 | - |   | 
          
            | 475 |         // Move question categories to the new context.
 | - |   | 
          
            | 476 |         if (!$newcontext = context_coursecat::instance($newcategory->id)) {
 | - |   | 
          
            | 477 |             return false;
 | - |   | 
          
            | 478 |         }
 | - |   | 
          
            | 479 |  
 | - |   | 
          
            | 480 |         // Only move question categories if there is any question category at all!
 | - |   | 
          
            | 481 |         if ($topcategory = question_get_top_category($context->id)) {
 | - |   | 
          
            | 482 |             $newtopcategory = question_get_top_category($newcontext->id, true);
 | - |   | 
          
            | 483 |  
 | - |   | 
          
            | 484 |             question_move_category_to_context($topcategory->id, $context->id, $newcontext->id);
 | - |   | 
          
            | 485 |             $DB->set_field('question_categories', 'parent', $newtopcategory->id, ['parent' => $topcategory->id]);
 | - |   | 
          
            | 486 |             // Now delete the top category.
 | - |   | 
          
            | 487 |             $DB->delete_records('question_categories', ['id' => $topcategory->id]);
 | - |   | 
          
            | 488 |         }
 | - |   | 
          
            | 489 |     }
 | - |   | 
          
            | 490 |  
 | - |   | 
          
            | 491 |     return true;
 | - |   | 
          
            | 492 | }
 | 441 | }
 | 
          
            | 493 |  
 | 442 |  
 | 
          
            | 494 | /**
 | 443 | /**
 | 
          
            | 495 |  * Creates a new category to save the questions in use.
 | 444 |  * Creates a new category to save the questions in use.
 | 
          
            | 496 |  *
 | 445 |  *
 | 
          
            | Línea 502... | Línea 451... | 
          
            | 502 |  * @return mixed false on
 | 451 |  * @return mixed false on
 | 
          
            | 503 |  */
 | 452 |  */
 | 
          
            | 504 | function question_save_from_deletion($questionids, $newcontextid, $oldplace, $newcategory = null) {
 | 453 | function question_save_from_deletion($questionids, $newcontextid, $oldplace, $newcategory = null) {
 | 
          
            | 505 |     global $DB;
 | 454 |     global $DB;
 | 
          
            | Línea -... | Línea 455... | 
          
            | - |   | 455 |  
 | 
          
            | - |   | 456 |     $newcontext = context::instance_by_id($newcontextid);
 | 
          
            | - |   | 457 |     if ($newcontext->contextlevel !== CONTEXT_MODULE) {
 | 
          
            | - |   | 458 |         throw new moodle_exception("Invalid contextlevel: {$newcontext->contextlevel} for \$newcontextid {$newcontextid}");
 | 
          
            | - |   | 459 |     }
 | 
          
            | 506 |  
 | 460 |  
 | 
          
            | 507 |     // Make a category in the parent context to move the questions to.
 | 461 |     // Make a category in the parent context to move the questions to.
 | 
          
            | 508 |     if (is_null($newcategory)) {
 | 462 |     if (is_null($newcategory)) {
 | 
          
            | 509 |         $newcategory = new stdClass();
 | 463 |         $newcategory = new stdClass();
 | 
          
            | 510 |         $newcategory->parent = question_get_top_category($newcontextid, true)->id;
 | 464 |         $newcategory->parent = question_get_top_category($newcontextid, true)->id;
 | 
          
            | Línea 527... | Línea 481... | 
          
            | 527 | /**
 | 481 | /**
 | 
          
            | 528 |  * All question categories and their questions are deleted for this activity.
 | 482 |  * All question categories and their questions are deleted for this activity.
 | 
          
            | 529 |  *
 | 483 |  *
 | 
          
            | 530 |  * @param object $cm the course module object representing the activity
 | 484 |  * @param object $cm the course module object representing the activity
 | 
          
            | 531 |  * @param bool $notused the argument is not used any more. Kept for backwards compatibility.
 | 485 |  * @param bool $notused the argument is not used any more. Kept for backwards compatibility.
 | 
          
            | - |   | 486 |  * @param bool $coursedeletion Are we calling this as part of deleting the course the activity belongs to?
 | 
          
            | 532 |  * @return boolean
 | 487 |  * @return boolean
 | 
          
            | 533 |  */
 | 488 |  */
 | 
          
            | 534 | function question_delete_activity($cm, $notused = false): bool {
 | 489 | function question_delete_activity($cm, $notused = false, bool $coursedeletion = false): bool {
 | 
          
            | 535 |     $modcontext = context_module::instance($cm->id);
 | 490 |     $modcontext = context_module::instance($cm->id);
 | 
          
            | 536 |     question_delete_context($modcontext->id);
 | 491 |     question_delete_context($modcontext->id, $coursedeletion);
 | 
          
            | 537 |     return true;
 | 492 |     return true;
 | 
          
            | 538 | }
 | 493 | }
 | 
          
            | Línea 539... | Línea 494... | 
          
            | 539 |  
 | 494 |  
 | 
          
            | 540 | /**
 | 495 | /**
 | 
          
            | 541 |  * This function will handle moving all tag instances to a new context for a
 | 496 |  * This function will handle moving all tag instances to a new context for a
 | 
          
            | 542 |  * given list of questions.
 | 497 |  * given list of questions.
 | 
          
            | 543 |  *
 | - |   | 
          
            | 544 |  * Questions can be tagged in up to two contexts:
 | - |   | 
          
            | 545 |  * 1.) The context the question exists in.
 | - |   | 
          
            | 546 |  * 2.) The course context (if the question context is a higher context.
 | - |   | 
          
            | 547 |  *     E.g. course category context or system context.
 | - |   | 
          
            | 548 |  *
 | - |   | 
          
            | 549 |  * This means a question that exists in a higher context (e.g. course cat or
 | - |   | 
          
            | 550 |  * system context) may have multiple groups of tags in any number of child
 | - |   | 
          
            | 551 |  * course contexts.
 | - |   | 
          
            | 552 |  *
 | - |   | 
          
            | 553 |  * Questions in the course category context can be move "down" a context level
 | - |   | 
          
            | 554 |  * into one of their child course contexts or activity contexts which affects the
 | - |   | 
          
            | 555 |  * availability of that question in other courses / activities.
 | - |   | 
          
            | 556 |  *
 | - |   | 
          
            | 557 |  * In this case it makes the questions no longer available in the other course or
 | - |   | 
          
            | 558 |  * activity contexts so we need to make sure that the tag instances in those other
 | - |   | 
          
            | 559 |  * contexts are removed.
 | - |   | 
          
            | 560 |  *
 | 498 |  *
 | 
          
            | 561 |  * @param stdClass[] $questions The list of question being moved (must include
 | 499 |  * @param stdClass[] $questions The list of question being moved (must include
 | 
          
            | 562 |  *                              the id and contextid)
 | 500 |  *                              the id and contextid)
 | 
          
            | 563 |  * @param context $newcontext The Moodle context the questions are being moved to
 | 501 |  * @param context $newcontext The Moodle context the questions are being moved to, must be module context.
 | 
          
            | 564 |  */
 | 502 |  */
 | 
          
            | 565 | function question_move_question_tags_to_new_context(array $questions, context $newcontext): void {
 | - |   | 
          
            | - |   | 503 | function question_move_question_tags_to_new_context(array $questions, context $newcontext): void {
 | 
          
            | 566 |     // If the questions are moving to a new course/activity context then we need to
 | 504 |  
 | 
          
            | 567 |     // find any existing tag instances from any unavailable course contexts and
 | 505 |     if ($newcontext->contextlevel !== CONTEXT_MODULE) {
 | 
          
            | 568 |     // delete them because they will no longer be applicable (we don't support
 | - |   | 
          
            | 569 |     // tagging questions across courses).
 | 506 |         debugging("Invalid contextlevel: {$newcontext->contextlevel}", DEBUG_DEVELOPER);
 | 
          
            | - |   | 507 |     }
 | 
          
            | 570 |     $instancestodelete = [];
 | 508 |  
 | 
          
            | 571 |     $instancesfornewcontext = [];
 | - |   | 
          
            | 572 |     $newcontextparentids = $newcontext->get_parent_context_ids();
 | 509 |     $instancesfornewcontext = [];
 | 
          
            | 573 |     $questionids = array_map(function($question) {
 | 510 |     $questionids = array_map(function($question) {
 | 
          
            | 574 |         return $question->id;
 | 511 |         return $question->id;
 | 
          
            | 575 |     }, $questions);
 | 512 |     }, $questions);
 | 
          
            | Línea 580... | Línea 517... | 
          
            | 580 |  
 | 517 |  
 | 
          
            | 581 |         foreach ($tagobjects as $tagobject) {
 | 518 |         foreach ($tagobjects as $tagobject) {
 | 
          
            | 582 |             $tagid = $tagobject->taginstanceid;
 | 519 |             $tagid = $tagobject->taginstanceid;
 | 
          
            | 583 |             $tagcontextid = $tagobject->taginstancecontextid;
 | 520 |             $tagcontextid = $tagobject->taginstancecontextid;
 | 
          
            | 584 |             $istaginnewcontext = $tagcontextid == $newcontext->id;
 | - |   | 
          
            | Línea 585... | Línea 521... | 
          
            | 585 |             $istaginquestioncontext = $tagcontextid == $question->contextid;
 | 521 |             $istaginnewcontext = $tagcontextid == $newcontext->id;
 | 
          
            | 586 |  
 | 522 |  
 | 
          
            | 587 |             if ($istaginnewcontext) {
 | 523 |             if ($istaginnewcontext) {
 | 
          
            | 588 |                 // This tag instance is already in the correct context so we can
 | 524 |                 // This tag instance is already in the correct context so we can
 | 
          
            | 589 |                 // ignore it.
 | 525 |                 // ignore it.
 | 
          
            | Línea 590... | Línea -... | 
          
            | 590 |                 continue;
 | - |   | 
          
            | 591 |             }
 | - |   | 
          
            | 592 |  
 | - |   | 
          
            | 593 |             if ($istaginquestioncontext) {
 | - |   | 
          
            | 594 |                 // This tag instance is in the question context so it needs to be
 | - |   | 
          
            | 595 |                 // updated.
 | - |   | 
          
            | 596 |                 $instancesfornewcontext[] = $tagid;
 | - |   | 
          
            | 597 |                 continue;
 | - |   | 
          
            | 598 |             }
 | - |   | 
          
            | 599 |  
 | - |   | 
          
            | 600 |             // These tag instances are in neither the new context nor the
 | - |   | 
          
            | 601 |             // question context so we need to determine what to do based on
 | - |   | 
          
            | 602 |             // the context they are in and the new question context.
 | - |   | 
          
            | 603 |             $tagcontext = context::instance_by_id($tagcontextid);
 | - |   | 
          
            | 604 |             $tagcoursecontext = $tagcontext->get_course_context(false);
 | - |   | 
          
            | 605 |             // The tag is in a course context if get_course_context() returns
 | - |   | 
          
            | 606 |             // itself.
 | - |   | 
          
            | 607 |             $istaginstancecontextcourse = !empty($tagcoursecontext)
 | - |   | 
          
            | 608 |                 && $tagcontext->id == $tagcoursecontext->id;
 | - |   | 
          
            | 609 |  
 | - |   | 
          
            | 610 |             if ($istaginstancecontextcourse) {
 | - |   | 
          
            | 611 |                 // If the tag instance is in a course context we need to add some
 | - |   | 
          
            | 612 |                 // special handling.
 | - |   | 
          
            | 613 |                 $tagcontextparentids = $tagcontext->get_parent_context_ids();
 | - |   | 
          
            | 614 |                 $isnewcontextaparent = in_array($newcontext->id, $tagcontextparentids);
 | - |   | 
          
            | 615 |                 $isnewcontextachild = in_array($tagcontext->id, $newcontextparentids);
 | - |   | 
          
            | 616 |  
 | - |   | 
          
            | 617 |                 if ($isnewcontextaparent) {
 | - |   | 
          
            | 618 |                     // If the tag instance is a course context tag and the new
 | - |   | 
          
            | 619 |                     // context is still a parent context to the tag context then
 | - |   | 
          
            | 620 |                     // we can leave this tag where it is.
 | - |   | 
          
            | 621 |                     continue;
 | - |   | 
          
            | 622 |                 } else if ($isnewcontextachild) {
 | - |   | 
          
            | 623 |                     // If the new context is a child context (e.g. activity) of this
 | - |   | 
          
            | 624 |                     // tag instance then we should move all of this tag instance
 | - |   | 
          
            | 625 |                     // down into the activity context along with the question.
 | - |   | 
          
            | 626 |                     $instancesfornewcontext[] = $tagid;
 | - |   | 
          
            | 627 |                 } else {
 | - |   | 
          
            | 628 |                     // If the tag is in a course context that is no longer a parent
 | - |   | 
          
            | 629 |                     // or child of the new context then this tag instance should be
 | - |   | 
          
            | 630 |                     // removed.
 | - |   | 
          
            | 631 |                     $instancestodelete[] = $tagid;
 | - |   | 
          
            | 632 |                 }
 | - |   | 
          
            | 633 |             } else {
 | - |   | 
          
            | 634 |                 // This is a catch all for any tag instances not in the question
 | - |   | 
          
            | 635 |                 // context or a course context. These tag instances should be
 | 526 |                 continue;
 | 
          
            | 636 |                 // updated to the new context id. This will clean up old invalid
 | - |   | 
          
            | 637 |                 // data.
 | 527 |             }
 | 
          
            | 638 |                 $instancesfornewcontext[] = $tagid;
 | 528 |  
 | 
          
            | Línea 639... | Línea -... | 
          
            | 639 |             }
 | - |   | 
          
            | 640 |         }
 | - |   | 
          
            | 641 |     }
 | - |   | 
          
            | 642 |  
 | - |   | 
          
            | 643 |     if (!empty($instancestodelete)) {
 | - |   | 
          
            | 644 |         // Delete any course context tags that may no longer be valid.
 | 529 |             $instancesfornewcontext[] = $tagid;
 | 
          
            | 645 |         core_tag_tag::delete_instances_by_id($instancestodelete);
 | 530 |         }
 | 
          
            | 646 |     }
 | 531 |     }
 | 
          
            | 647 |  
 | 532 |  
 | 
          
            | 648 |     if (!empty($instancesfornewcontext)) {
 | 533 |     if (!empty($instancesfornewcontext)) {
 | 
          
            | Línea 780... | Línea 665... | 
          
            | 780 |     global $DB;
 | 665 |     global $DB;
 | 
          
            | Línea 781... | Línea 666... | 
          
            | 781 |  
 | 666 |  
 | 
          
            | 782 |     if ($delete || $oldcontextid !== $newcontextid) {
 | 667 |     if ($delete || $oldcontextid !== $newcontextid) {
 | 
          
            | 783 |         $setreferences = $DB->get_recordset('question_set_references', ['questionscontextid' => $oldcontextid]);
 | 668 |         $setreferences = $DB->get_recordset('question_set_references', ['questionscontextid' => $oldcontextid]);
 | 
          
            | 784 |         foreach ($setreferences as $setreference) {
 | 669 |         foreach ($setreferences as $setreference) {
 | 
          
            | 785 |             $filter = json_decode($setreference->filtercondition);
 | 670 |             $filter = json_decode($setreference->filtercondition, true);
 | 
          
            | 786 |             if (isset($filter->questioncategoryid)) {
 | 671 |             if (isset($filter['questioncategoryid'])) {
 | 
          
            | - |   | 672 |                 $filter = question_reference_manager::convert_legacy_set_reference_filter_condition($filter);
 | 
          
            | 787 |                 if ((int)$filter->questioncategoryid === $oldcategoryid) {
 | 673 |             }
 | 
          
            | 788 |                     $setreference->questionscontextid = $newcontextid;
 | 674 |             $setreference->questionscontextid = $newcontextid;
 | 
          
            | 789 |                     if ($oldcategoryid !== $newcatgoryid) {
 | 675 |             if (
 | 
          
            | 790 |                         $filter->questioncategoryid = $newcatgoryid;
 | 676 |                 (int)$filter['filter']['category']['values'][0] === $oldcategoryid
 | 
          
            | 791 |                         $setreference->filtercondition = json_encode($filter);
 | 677 |                 && $oldcategoryid !== $newcatgoryid
 | 
          
            | 792 |                     }
 | 678 |             ) {
 | 
          
            | 793 |                     $DB->update_record('question_set_references', $setreference);
 | 679 |                 $filter['filter']['category']['values'][0] = $newcatgoryid;
 | 
          
            | 794 |                 }
 | 680 |                 $setreference->filtercondition = json_encode($filter);
 | 
          
            | - |   | 681 |             }
 | 
          
            | 795 |             }
 | 682 |             $DB->update_record('question_set_references', $setreference);
 | 
          
            | 796 |         }
 | 683 |         }
 | 
          
            | 797 |         $setreferences->close();
 | 684 |         $setreferences->close();
 | 
          
            | 798 |     }
 | 685 |     }
 | 
          
            | Línea 807... | Línea 694... | 
          
            | 807 |  * @param integer $newcontextid the new context id.
 | 694 |  * @param integer $newcontextid the new context id.
 | 
          
            | 808 |  */
 | 695 |  */
 | 
          
            | 809 | function question_move_category_to_context($categoryid, $oldcontextid, $newcontextid): void {
 | 696 | function question_move_category_to_context($categoryid, $oldcontextid, $newcontextid): void {
 | 
          
            | 810 |     global $DB;
 | 697 |     global $DB;
 | 
          
            | Línea -... | Línea 698... | 
          
            | - |   | 698 |  
 | 
          
            | - |   | 699 |     $newcontext = context::instance_by_id($newcontextid);
 | 
          
            | - |   | 700 |     if ($newcontext->contextlevel !== CONTEXT_MODULE) {
 | 
          
            | - |   | 701 |         debugging("Invalid contextlevel: {$newcontext->contextlevel}, must use CONTEXT_MODULE", DEBUG_DEVELOPER);
 | 
          
            | - |   | 702 |     }
 | 
          
            | 811 |  
 | 703 |  
 | 
          
            | 812 |     $questions = [];
 | 704 |     $questions = [];
 | 
          
            | 813 |     $sql = "SELECT q.id, q.qtype
 | 705 |     $sql = "SELECT q.id, q.qtype
 | 
          
            | 814 |               FROM {question} q
 | 706 |               FROM {question} q
 | 
          
            | 815 |               JOIN {question_versions} qv ON qv.questionid = q.id
 | 707 |               JOIN {question_versions} qv ON qv.questionid = q.id
 | 
          
            | 816 |               JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
 | 708 |               JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
 | 
          
            | Línea 817... | Línea 709... | 
          
            | 817 |              WHERE qbe.questioncategoryid = ?";
 | 709 |              WHERE qbe.questioncategoryid = ?";
 | 
          
            | 818 |  
 | 710 |  
 | 
          
            | - |   | 711 |     $questionids = $DB->get_records_sql_menu($sql, [$categoryid]);
 | 
          
            | - |   | 712 |     foreach ($questionids as $questionid => $qtype) {
 | 
          
            | - |   | 713 |  
 | 
          
            | - |   | 714 |         // If the question type is invalid, use "missingtype" so we have a valid qtype to call move_files() on.
 | 
          
            | - |   | 715 |         if (!\question_bank::is_qtype_installed($qtype)) {
 | 
          
            | - |   | 716 |             $qtype = 'missingtype';
 | 
          
            | 819 |     $questionids = $DB->get_records_sql_menu($sql, [$categoryid]);
 | 717 |         }
 | 
          
            | 820 |     foreach ($questionids as $questionid => $qtype) {
 | 718 |  
 | 
          
            | 821 |         question_bank::get_qtype($qtype)->move_files($questionid, $oldcontextid, $newcontextid);
 | 719 |         question_bank::get_qtype($qtype)->move_files($questionid, $oldcontextid, $newcontextid);
 | 
          
            | Línea 822... | Línea 720... | 
          
            | 822 |         // Purge this question from the cache.
 | 720 |         // Purge this question from the cache.
 | 
          
            | 823 |         question_bank::notify_question_edited($questionid);
 | 721 |         question_bank::notify_question_edited($questionid);
 | 
          
            | 824 |  
 | 722 |  
 | 
          
            | 825 |         $questions[] = (object) [
 | 723 |         $questions[] = (object) [
 | 
          
            | 826 |             'id' => $questionid,
 | 724 |             'id' => $questionid,
 | 
          
            | Línea 827... | Línea -... | 
          
            | 827 |             'contextid' => $oldcontextid
 | - |   | 
          
            | 828 |         ];
 | 725 |             'contextid' => $oldcontextid
 | 
          
            | Línea 829... | Línea 726... | 
          
            | 829 |     }
 | 726 |         ];
 | 
          
            | 830 |  
 | 727 |     }
 | 
          
            | - |   | 728 |  
 | 
          
            | 831 |     $newcontext = context::instance_by_id($newcontextid);
 | 729 |     question_move_question_tags_to_new_context($questions, $newcontext);
 | 
          
            | 832 |     question_move_question_tags_to_new_context($questions, $newcontext);
 | 730 |  
 | 
          
            | 833 |  
 | 731 |     $subcatids = $DB->get_records_menu('question_categories', ['parent' => $categoryid], '', 'id,1');
 | 
          
            | 834 |     $subcatids = $DB->get_records_menu('question_categories', ['parent' => $categoryid], '', 'id,1');
 | 732 |     foreach ($subcatids as $subcatid => $notused) {
 | 
          
            | Línea 935... | Línea 833... | 
          
            | 935 |  * Private function to factor common code out of get_question_options().
 | 833 |  * Private function to factor common code out of get_question_options().
 | 
          
            | 936 |  *
 | 834 |  *
 | 
          
            | 937 |  * @param object $question the question to tidy.
 | 835 |  * @param object $question the question to tidy.
 | 
          
            | 938 |  * @param stdClass $category The question_categories record for the given $question.
 | 836 |  * @param stdClass $category The question_categories record for the given $question.
 | 
          
            | 939 |  * @param \core_tag_tag[]|null $tagobjects The tags for the given $question.
 | 837 |  * @param \core_tag_tag[]|null $tagobjects The tags for the given $question.
 | 
          
            | 940 |  * @param stdClass[]|null $filtercourses The courses to filter the course tags by.
 | 838 |  * @param stdClass[]|null $filtercourses deprecated argument and should not be used
 | 
          
            | 941 |  */
 | 839 |  */
 | 
          
            | 942 | function _tidy_question($question, $category, array $tagobjects = null, array $filtercourses = null): void {
 | 840 | function _tidy_question($question, $category, ?array $tagobjects = null, ?array $filtercourses = null): void {
 | 
          
            | - |   | 841 |  
 | 
          
            | - |   | 842 |     if ($filtercourses !== null) {
 | 
          
            | - |   | 843 |         debugging("Filtercourses is a deprecated argument in " . __FUNCTION__, DEBUG_DEVELOPER);
 | 
          
            | - |   | 844 |     }
 | 
          
            | - |   | 845 |  
 | 
          
            | 943 |     // Convert numeric fields to float. This prevents these being displayed as 1.0000000.
 | 846 |     // Convert numeric fields to float. This prevents these being displayed as 1.0000000.
 | 
          
            | 944 |     $question->defaultmark += 0;
 | 847 |     $question->defaultmark += 0;
 | 
          
            | 945 |     $question->penalty += 0;
 | 848 |     $question->penalty += 0;
 | 
          
            | Línea 946... | Línea 849... | 
          
            | 946 |  
 | 849 |  
 | 
          
            | Línea 953... | Línea 856... | 
          
            | 953 |  
 | 856 |  
 | 
          
            | 954 |     // Add any tags we have been passed.
 | 857 |     // Add any tags we have been passed.
 | 
          
            | 955 |     if (!is_null($tagobjects)) {
 | 858 |     if (!is_null($tagobjects)) {
 | 
          
            | 956 |         $categorycontext = context::instance_by_id($category->contextid);
 | 859 |         $categorycontext = context::instance_by_id($category->contextid);
 | 
          
            | 957 |         $sortedtagobjects = question_sort_tags($tagobjects, $categorycontext, $filtercourses);
 | - |   | 
          
            | 958 |         $question->coursetagobjects = $sortedtagobjects->coursetagobjects;
 | - |   | 
          
            | 959 |         $question->coursetags = $sortedtagobjects->coursetags;
 | 860 |         $sortedtagobjects = question_sort_tags($tagobjects, $categorycontext, $filtercourses);
 | 
          
            | 960 |         $question->tagobjects = $sortedtagobjects->tagobjects;
 | 861 |         $question->tagobjects = $sortedtagobjects->tagobjects;
 | 
          
            | 961 |         $question->tags = $sortedtagobjects->tags;
 | 862 |         $question->tags = $sortedtagobjects->tags;
 | 
          
            | Línea 962... | Línea 863... | 
          
            | 962 |     }
 | 863 |     }
 | 
          
            | Línea 978... | Línea 879... | 
          
            | 978 |  * question object.
 | 879 |  * question object.
 | 
          
            | 979 |  *
 | 880 |  *
 | 
          
            | 980 |  * @param mixed $questions Either an array of question objects to be updated
 | 881 |  * @param mixed $questions Either an array of question objects to be updated
 | 
          
            | 981 |  *         or just a single question object
 | 882 |  *         or just a single question object
 | 
          
            | 982 |  * @param bool $loadtags load the question tags from the tags table. Optional, default false.
 | 883 |  * @param bool $loadtags load the question tags from the tags table. Optional, default false.
 | 
          
            | 983 |  * @param stdClass[] $filtercourses The courses to filter the course tags by.
 | 884 |  * @param stdClass[] $filtercourses deprecated argument and should not be used
 | 
          
            | 984 |  * @return bool Indicates success or failure.
 | 885 |  * @return bool Indicates success or failure.
 | 
          
            | 985 |  */
 | 886 |  */
 | 
          
            | 986 | function get_question_options(&$questions, $loadtags = false, $filtercourses = null) {
 | 887 | function get_question_options(&$questions, $loadtags = false, $filtercourses = null) {
 | 
          
            | 987 |     global $DB;
 | 888 |     global $DB;
 | 
          
            | Línea -... | Línea 889... | 
          
            | - |   | 889 |  
 | 
          
            | - |   | 890 |     if ($filtercourses !== null) {
 | 
          
            | - |   | 891 |         debugging("Filtercourses is a deprecated argument in " . __FUNCTION__, DEBUG_DEVELOPER);
 | 
          
            | - |   | 892 |     }
 | 
          
            | 988 |  
 | 893 |  
 | 
          
            | 989 |     $questionlist = is_array($questions) ? $questions : [$questions];
 | 894 |     $questionlist = is_array($questions) ? $questions : [$questions];
 | 
          
            | 990 |     $categoryids = [];
 | 895 |     $categoryids = [];
 | 
          
            | Línea 991... | Línea 896... | 
          
            | 991 |     $questionids = [];
 | 896 |     $questionids = [];
 | 
          
            | Línea 1037... | Línea 942... | 
          
            | 1037 |  * This function also search tag instances that may have a context id that don't match either a course or
 | 942 |  * This function also search tag instances that may have a context id that don't match either a course or
 | 
          
            | 1038 |  * question context and fix the data setting the correct context id.
 | 943 |  * question context and fix the data setting the correct context id.
 | 
          
            | 1039 |  *
 | 944 |  *
 | 
          
            | 1040 |  * @param \core_tag_tag[] $tagobjects The tags for the given $question.
 | 945 |  * @param \core_tag_tag[] $tagobjects The tags for the given $question.
 | 
          
            | 1041 |  * @param stdClass $categorycontext The question categories context.
 | 946 |  * @param stdClass $categorycontext The question categories context.
 | 
          
            | 1042 |  * @param stdClass[]|null $filtercourses The courses to filter the course tags by.
 | 947 |  * @param stdClass[]|null $filtercourses deprecated argument and should not be used.
 | 
          
            | 1043 |  * @return stdClass $sortedtagobjects Sorted tag objects.
 | 948 |  * @return stdClass $sortedtagobjects Sorted tag objects.
 | 
          
            | 1044 |  */
 | 949 |  */
 | 
          
            | 1045 | function question_sort_tags($tagobjects, $categorycontext, $filtercourses = null): stdClass {
 | 950 | function question_sort_tags($tagobjects, $categorycontext, $filtercourses = null): stdClass {
 | 
          
            | Línea 1046... | Línea 951... | 
          
            | 1046 |  
 | 951 |  
 | 
          
            | 1047 |     // Questions can have two sets of tag instances. One set at the
 | 952 |     if ($filtercourses !== null) {
 | 
          
            | 1048 |     // course context level and another at the context the question
 | 953 |         debugging("Filtercourses is a deprecated argument in " . __FUNCTION__, DEBUG_DEVELOPER);
 | 
          
            | - |   | 954 |     }
 | 
          
            | 1049 |     // belongs to (e.g. course category, system etc).
 | 955 |  
 | 
          
            | 1050 |     $sortedtagobjects = new stdClass();
 | - |   | 
          
            | 1051 |     $sortedtagobjects->coursetagobjects = [];
 | - |   | 
          
            | 1052 |     $sortedtagobjects->coursetags = [];
 | 956 |     $sortedtagobjects = new stdClass();
 | 
          
            | 1053 |     $sortedtagobjects->tagobjects = [];
 | 957 |     $sortedtagobjects->tagobjects = [];
 | 
          
            | 1054 |     $sortedtagobjects->tags = [];
 | 958 |     $sortedtagobjects->tags = [];
 | 
          
            | 1055 |     $taginstanceidstonormalise = [];
 | - |   | 
          
            | 1056 |     $filtercoursecontextids = [];
 | - |   | 
          
            | 1057 |     $hasfiltercourses = !empty($filtercourses);
 | - |   | 
          
            | 1058 |  
 | - |   | 
          
            | 1059 |     if ($hasfiltercourses) {
 | - |   | 
          
            | 1060 |         // If we're being asked to filter the course tags by a set of courses
 | - |   | 
          
            | 1061 |         // then get the context ids to filter below.
 | - |   | 
          
            | 1062 |         $filtercoursecontextids = array_map(function($course) {
 | - |   | 
          
            | 1063 |             $coursecontext = context_course::instance($course->id);
 | - |   | 
          
            | 1064 |             return $coursecontext->id;
 | - |   | 
          
            | 1065 |         }, $filtercourses);
 | - |   | 
          
            | Línea 1066... | Línea 959... | 
          
            | 1066 |     }
 | 959 |     $taginstanceidstonormalise = [];
 | 
          
            | 1067 |  
 | 960 |  
 | 
          
            | 1068 |     foreach ($tagobjects as $tagobject) {
 | 961 |     foreach ($tagobjects as $tagobject) {
 | 
          
            | 1069 |         $tagcontextid = $tagobject->taginstancecontextid;
 | - |   | 
          
            | 1070 |         $tagcontext = context::instance_by_id($tagcontextid);
 | - |   | 
          
            | 1071 |         $tagcoursecontext = $tagcontext->get_course_context(false);
 | - |   | 
          
            | 1072 |         // This is a course tag if the tag context is a course context which
 | - |   | 
          
            | 1073 |         // doesn't match the question's context. Any tag in the question context
 | - |   | 
          
            | 1074 |         // is not considered a course tag, it belongs to the question.
 | - |   | 
          
            | 1075 |         $iscoursetag = $tagcoursecontext
 | - |   | 
          
            | 1076 |             && $tagcontext->id == $tagcoursecontext->id
 | - |   | 
          
            | 1077 |             && $tagcontext->id != $categorycontext->id;
 | - |   | 
          
            | 1078 |  
 | - |   | 
          
            | 1079 |         if ($iscoursetag) {
 | - |   | 
          
            | 1080 |             // Any tag instance in a course context level is considered a course tag.
 | - |   | 
          
            | 1081 |             if (!$hasfiltercourses || in_array($tagcontextid, $filtercoursecontextids)) {
 | - |   | 
          
            | 1082 |                 // Add the tag to the list of course tags if we aren't being
 | - |   | 
          
            | 1083 |                 // asked to filter or if this tag is in the list of courses
 | - |   | 
          
            | 1084 |                 // we're being asked to filter by.
 | - |   | 
          
            | 1085 |                 $sortedtagobjects->coursetagobjects[] = $tagobject;
 | - |   | 
          
            | 1086 |                 $sortedtagobjects->coursetags[$tagobject->id] = $tagobject->get_display_name();
 | - |   | 
          
            | 1087 |             }
 | - |   | 
          
            | 1088 |         } else {
 | 962 |         $tagcontextid = $tagobject->taginstancecontextid;
 | 
          
            | 1089 |             // All non course context level tag instances or tags in the question
 | 963 |         $tagcontext = context::instance_by_id($tagcontextid);
 | 
          
            | 1090 |             // context belong to the context that the question was created in.
 | 964 |             // All tag instances belong to the context that the question was created in.
 | 
          
            | Línea 1091... | Línea 965... | 
          
            | 1091 |             $sortedtagobjects->tagobjects[] = $tagobject;
 | 965 |             $sortedtagobjects->tagobjects[] = $tagobject;
 | 
          
            | 1092 |             $sortedtagobjects->tags[$tagobject->id] = $tagobject->get_display_name();
 | 966 |             $sortedtagobjects->tags[$tagobject->id] = $tagobject->get_display_name();
 | 
          
            | Línea 1100... | Línea 974... | 
          
            | 1100 |                 $taginstanceidstonormalise[] = $tagobject->taginstanceid;
 | 974 |                 $taginstanceidstonormalise[] = $tagobject->taginstanceid;
 | 
          
            | 1101 |                 // Update the object properties to reflect the DB update that will
 | 975 |                 // Update the object properties to reflect the DB update that will
 | 
          
            | 1102 |                 // happen below.
 | 976 |                 // happen below.
 | 
          
            | 1103 |                 $tagobject->taginstancecontextid = $categorycontext->id;
 | 977 |                 $tagobject->taginstancecontextid = $categorycontext->id;
 | 
          
            | 1104 |             }
 | 978 |             }
 | 
          
            | 1105 |         }
 | 979 |  
 | 
          
            | 1106 |     }
 | 980 |     }
 | 
          
            | Línea 1107... | Línea 981... | 
          
            | 1107 |  
 | 981 |  
 | 
          
            | 1108 |     if (!empty($taginstanceidstonormalise)) {
 | 982 |     if (!empty($taginstanceidstonormalise)) {
 | 
          
            | 1109 |         // If we found any tag instances with incorrect context id data then we can
 | 983 |         // If we found any tag instances with incorrect context id data then we can
 | 
          
            | Línea 1176... | Línea 1050... | 
          
            | 1176 |     }
 | 1050 |     }
 | 
          
            | 1177 |     return $children;
 | 1051 |     return $children;
 | 
          
            | 1178 | }
 | 1052 | }
 | 
          
            | Línea 1179... | Línea 1053... | 
          
            | 1179 |  
 | 1053 |  
 | 
          
            | 1180 | /**
 | 1054 | /**
 | 
          
            | 1181 |  * Get the default category for the context.
 | 1055 |  * Get the default category for the context. Optionally create one if it does not exist.
 | 
          
            | 1182 |  *
 | 1056 |  *
 | 
          
            | - |   | 1057 |  * @param int $contextid a context id.
 | 
          
            | 1183 |  * @param integer $contextid a context id.
 | 1058 |  * @param bool $createifnotexists create the default catagory if it does not exist.
 | 
          
            | 1184 |  * @return object|bool the default question category for that context, or false if none.
 | 1059 |  * @return stdClass|bool the default question category for that context, or false if none.
 | 
          
            | 1185 |  */
 | 1060 |  */
 | 
          
            | 1186 | function question_get_default_category($contextid) {
 | 1061 | function question_get_default_category($contextid, bool $createifnotexists = false) {
 | 
          
            | - |   | 1062 |     global $DB;
 | 
          
            | 1187 |     global $DB;
 | 1063 |  
 | 
          
            | 1188 |     $category = $DB->get_records_select('question_categories', 'contextid = ? AND parent <> 0',
 | 1064 |     $context = \core\context::instance_by_id($contextid);
 | 
          
            | 1189 |                                         [$contextid], 'id', '*', 0, 1);
 | 1065 |     if ($context->contextlevel !== CONTEXT_MODULE) {
 | 
          
            | - |   | 1066 |         debugging(
 | 
          
            | 1190 |     if (!empty($category)) {
 | 1067 |             "Invalid context level {$context->contextlevel} for default category. Please use CONTEXT_MODULE",
 | 
          
            | 1191 |         return reset($category);
 | 1068 |             DEBUG_DEVELOPER
 | 
          
            | 1192 |     } else {
 | 1069 |         );
 | 
          
            | 1193 |         return false;
 | 1070 |         return false;
 | 
          
            | - |   | 1071 |     }
 | 
          
            | - |   | 1072 |  
 | 
          
            | - |   | 1073 |     $defaultcats = $DB->get_records_select('question_categories', 'contextid = ? AND parent <> 0', [$contextid], 'id', '*', 0, 1);
 | 
          
            | - |   | 1074 |  
 | 
          
            | - |   | 1075 |     $defaultcat = reset($defaultcats);
 | 
          
            | - |   | 1076 |  
 | 
          
            | - |   | 1077 |     if (empty($defaultcat) && $createifnotexists) {
 | 
          
            | - |   | 1078 |  
 | 
          
            | - |   | 1079 |         // We need to make a top category first if it doesn't exist.
 | 
          
            | - |   | 1080 |         $topcategory = question_get_top_category($context->id, true);
 | 
          
            | - |   | 1081 |  
 | 
          
            | - |   | 1082 |         // We don't have one, so we need to make one.
 | 
          
            | - |   | 1083 |         $defaultcat = new stdClass();
 | 
          
            | - |   | 1084 |         $contextname = $context->get_context_name(false, true);
 | 
          
            | - |   | 1085 |         // Max length of name field is 255.
 | 
          
            | - |   | 1086 |         $defaultcat->name = shorten_text(get_string('defaultfor', 'question', $contextname), 255);
 | 
          
            | - |   | 1087 |         $defaultcat->info = get_string('defaultinfofor', 'question', $contextname);
 | 
          
            | - |   | 1088 |         $defaultcat->contextid = $context->id;
 | 
          
            | - |   | 1089 |         $defaultcat->parent = $topcategory->id;
 | 
          
            | - |   | 1090 |         // By default, all categories get this number, and are sorted alphabetically.
 | 
          
            | - |   | 1091 |         $defaultcat->sortorder = 999;
 | 
          
            | - |   | 1092 |         $defaultcat->stamp = make_unique_id_code();
 | 
          
            | - |   | 1093 |         $defaultcat->id = $DB->insert_record('question_categories', $defaultcat);
 | 
          
            | - |   | 1094 |     }
 | 
          
            | - |   | 1095 |  
 | 
          
            | 1194 |     }
 | 1096 |     return $defaultcat;
 | 
          
            | Línea 1195... | Línea 1097... | 
          
            | 1195 | }
 | 1097 | }
 | 
          
            | 1196 |  
 | 1098 |  
 | 
          
            | 1197 | /**
 | 1099 | /**
 | 
          
            | Línea 1204... | Línea 1106... | 
          
            | 1204 |  */
 | 1106 |  */
 | 
          
            | 1205 | function question_get_top_category($contextid, $create = false) {
 | 1107 | function question_get_top_category($contextid, $create = false) {
 | 
          
            | 1206 |     global $DB;
 | 1108 |     global $DB;
 | 
          
            | 1207 |     $category = $DB->get_record('question_categories', ['contextid' => $contextid, 'parent' => 0]);
 | 1109 |     $category = $DB->get_record('question_categories', ['contextid' => $contextid, 'parent' => 0]);
 | 
          
            | Línea -... | Línea 1110... | 
          
            | - |   | 1110 |  
 | 
          
            | - |   | 1111 |     $context = context::instance_by_id($contextid);
 | 
          
            | - |   | 1112 |     if ($context->contextlevel !== CONTEXT_MODULE) {
 | 
          
            | - |   | 1113 |         debugging(
 | 
          
            | - |   | 1114 |             "Invalid context level: {$context->contextlevel} for question_get_top_category, must be CONTEXT_MODULE",
 | 
          
            | - |   | 1115 |             DEBUG_DEVELOPER
 | 
          
            | - |   | 1116 |         );
 | 
          
            | - |   | 1117 |         return false;
 | 
          
            | - |   | 1118 |     }
 | 
          
            | 1208 |  
 | 1119 |  
 | 
          
            | 1209 |     if (!$category && $create) {
 | 1120 |     if (!$category && $create) {
 | 
          
            | 1210 |         // We need to make one.
 | 1121 |         // We need to make one.
 | 
          
            | 1211 |         $category = new stdClass();
 | 1122 |         $category = new stdClass();
 | 
          
            | 1212 |         $category->name = 'top'; // A non-real name for the top category. It will be localised at the display time.
 | 1123 |         $category->name = 'top'; // A non-real name for the top category. It will be localised at the display time.
 | 
          
            | Línea 1241... | Línea 1152... | 
          
            | 1241 |  
 | 1152 |  
 | 
          
            | 1242 |     return $topcategories;
 | 1153 |     return $topcategories;
 | 
          
            | Línea 1243... | Línea 1154... | 
          
            | 1243 | }
 | 1154 | }
 | 
          
            | 1244 |  
 | - |   | 
          
            | 1245 | /**
 | - |   | 
          
            | 1246 |  * Gets the default category in the most specific context.
 | - |   | 
          
            | 1247 |  * If no categories exist yet then default ones are created in all contexts.
 | - |   | 
          
            | 1248 |  *
 | - |   | 
          
            | 1249 |  * @param array $contexts  The context objects for this context and all parent contexts.
 | - |   | 
          
            | 1250 |  * @return object The default category - the category in the course context
 | - |   | 
          
            | 1251 |  */
 | - |   | 
          
            | 1252 | function question_make_default_categories($contexts): object {
 | - |   | 
          
            | 1253 |     global $DB;
 | - |   | 
          
            | 1254 |     static $preferredlevels = array(
 | - |   | 
          
            | 1255 |         CONTEXT_COURSE => 4,
 | - |   | 
          
            | 1256 |         CONTEXT_MODULE => 3,
 | - |   | 
          
            | 1257 |         CONTEXT_COURSECAT => 2,
 | - |   | 
          
            | 1258 |         CONTEXT_SYSTEM => 1,
 | - |   | 
          
            | 1259 |     );
 | - |   | 
          
            | 1260 |  
 | - |   | 
          
            | 1261 |     $toreturn = null;
 | - |   | 
          
            | 1262 |     $preferredness = 0;
 | - |   | 
          
            | 1263 |     // If it already exists, just return it.
 | - |   | 
          
            | 1264 |     foreach ($contexts as $key => $context) {
 | - |   | 
          
            | 1265 |         $topcategory = question_get_top_category($context->id, true);
 | - |   | 
          
            | 1266 |         if (!$exists = $DB->record_exists("question_categories",
 | - |   | 
          
            | 1267 |                 array('contextid' => $context->id, 'parent' => $topcategory->id))) {
 | - |   | 
          
            | 1268 |             // Otherwise, we need to make one.
 | - |   | 
          
            | 1269 |             $category = new stdClass();
 | - |   | 
          
            | 1270 |             $contextname = $context->get_context_name(false, true);
 | - |   | 
          
            | 1271 |             // Max length of name field is 255.
 | - |   | 
          
            | 1272 |             $category->name = shorten_text(get_string('defaultfor', 'question', $contextname), 255);
 | - |   | 
          
            | 1273 |             $category->info = get_string('defaultinfofor', 'question', $contextname);
 | - |   | 
          
            | 1274 |             $category->contextid = $context->id;
 | - |   | 
          
            | 1275 |             $category->parent = $topcategory->id;
 | - |   | 
          
            | 1276 |             // By default, all categories get this number, and are sorted alphabetically.
 | - |   | 
          
            | 1277 |             $category->sortorder = 999;
 | - |   | 
          
            | 1278 |             $category->stamp = make_unique_id_code();
 | - |   | 
          
            | 1279 |             $category->id = $DB->insert_record('question_categories', $category);
 | - |   | 
          
            | 1280 |         } else {
 | - |   | 
          
            | 1281 |             $category = question_get_default_category($context->id);
 | - |   | 
          
            | 1282 |         }
 | - |   | 
          
            | 1283 |         $thispreferredness = $preferredlevels[$context->contextlevel];
 | - |   | 
          
            | 1284 |         if (has_any_capability(array('moodle/question:usemine', 'moodle/question:useall'), $context)) {
 | - |   | 
          
            | 1285 |             $thispreferredness += 10;
 | - |   | 
          
            | 1286 |         }
 | - |   | 
          
            | 1287 |         if ($thispreferredness > $preferredness) {
 | - |   | 
          
            | 1288 |             $toreturn = $category;
 | - |   | 
          
            | 1289 |             $preferredness = $thispreferredness;
 | - |   | 
          
            | 1290 |         }
 | - |   | 
          
            | 1291 |     }
 | - |   | 
          
            | 1292 |  
 | - |   | 
          
            | 1293 |     if (!is_null($toreturn)) {
 | - |   | 
          
            | 1294 |         $toreturn = clone($toreturn);
 | - |   | 
          
            | 1295 |     }
 | - |   | 
          
            | 1296 |     return $toreturn;
 | - |   | 
          
            | 1297 | }
 | - |   | 
          
            | 1298 |  
 | 1155 |  
 | 
          
            | 1299 | /**
 | 1156 | /**
 | 
          
            | 1300 |  * Get the list of categories.
 | 1157 |  * Get the list of categories.
 | 
          
            | 1301 |  *
 | 1158 |  *
 | 
          
            | 1302 |  * @param int $categoryid
 | 1159 |  * @param int $categoryid
 | 
          
            | Línea 1420... | Línea 1277... | 
          
            | 1420 | /**
 | 1277 | /**
 | 
          
            | 1421 |  * Check capability on category.
 | 1278 |  * Check capability on category.
 | 
          
            | 1422 |  *
 | 1279 |  *
 | 
          
            | 1423 |  * @param int|stdClass|question_definition $questionorid object or id.
 | 1280 |  * @param int|stdClass|question_definition $questionorid object or id.
 | 
          
            | 1424 |  *      If an object is passed, it should include ->contextid and ->createdby.
 | 1281 |  *      If an object is passed, it should include ->contextid and ->createdby.
 | 
          
            | 1425 |  * @param string $cap 'add', 'edit', 'view', 'use', 'move' or 'tag'.
 | 1282 |  * @param string $cap 'add', 'edit', 'view', 'use', 'move', or 'tag'.
 | 
          
            | 1426 |  * @param int $notused no longer used.
 | 1283 |  * @param int $notused no longer used.
 | 
          
            | 1427 |  * @return bool this user has the capability $cap for this question $question?
 | 1284 |  * @return bool this user has the capability $cap for this question $question?
 | 
          
            | 1428 |  */
 | 1285 |  */
 | 
          
            | 1429 | function question_has_capability_on($questionorid, $cap, $notused = -1): bool {
 | 1286 | function question_has_capability_on($questionorid, $cap, $notused = -1): bool {
 | 
          
            | 1430 |     global $USER, $DB;
 | 1287 |     global $USER, $DB;
 | 
          
            | Línea 1511... | Línea 1368... | 
          
            | 1511 | function question_edit_url($context) {
 | 1368 | function question_edit_url($context) {
 | 
          
            | 1512 |     global $CFG, $SITE;
 | 1369 |     global $CFG, $SITE;
 | 
          
            | 1513 |     if (!has_any_capability(question_get_question_capabilities(), $context)) {
 | 1370 |     if (!has_any_capability(question_get_question_capabilities(), $context)) {
 | 
          
            | 1514 |         return false;
 | 1371 |         return false;
 | 
          
            | 1515 |     }
 | 1372 |     }
 | 
          
            | - |   | 1373 |  
 | 
          
            | - |   | 1374 |     if ($context->contextlevel !== CONTEXT_MODULE) {
 | 
          
            | - |   | 1375 |         debugging(
 | 
          
            | - |   | 1376 |             "Invalid contextlevel: {$context->contextlevel} provided for question_edit_url, must be CONTEXT_MODULE",
 | 
          
            | - |   | 1377 |             DEBUG_DEVELOPER
 | 
          
            | - |   | 1378 |         );
 | 
          
            | - |   | 1379 |         return false;
 | 
          
            | - |   | 1380 |     }
 | 
          
            | - |   | 1381 |  
 | 
          
            | 1516 |     $baseurl = $CFG->wwwroot . '/question/edit.php?';
 | 1382 |     $baseurl = $CFG->wwwroot . '/question/edit.php?';
 | 
          
            | 1517 |     $defaultcategory = question_get_default_category($context->id);
 | 1383 |     $defaultcategory = question_get_default_category($context->id, true);
 | 
          
            | 1518 |     if ($defaultcategory) {
 | 1384 |     if ($defaultcategory) {
 | 
          
            | 1519 |         $baseurl .= 'cat=' . $defaultcategory->id . ',' . $context->id . '&';
 | 1385 |         $baseurl .= 'cat=' . $defaultcategory->id . ',' . $context->id . '&';
 | 
          
            | 1520 |     }
 | 1386 |     }
 | 
          
            | 1521 |     switch ($context->contextlevel) {
 | 1387 |     switch ($context->contextlevel) {
 | 
          
            | 1522 |         case CONTEXT_SYSTEM:
 | 1388 |         case CONTEXT_SYSTEM:
 | 
          
            | Línea 1529... | Línea 1395... | 
          
            | 1529 |             return $baseurl . 'courseid=' . $context->instanceid;
 | 1395 |             return $baseurl . 'courseid=' . $context->instanceid;
 | 
          
            | 1530 |         case CONTEXT_MODULE:
 | 1396 |         case CONTEXT_MODULE:
 | 
          
            | 1531 |             return $baseurl . 'cmid=' . $context->instanceid;
 | 1397 |             return $baseurl . 'cmid=' . $context->instanceid;
 | 
          
            | 1532 |     }
 | 1398 |     }
 | 
          
            | Línea -... | Línea 1399... | 
          
            | - |   | 1399 |  
 | 
          
            | 1533 |  
 | 1400 |     return $baseurl . 'cmid=' . $context->instanceid;
 | 
          
            | Línea 1534... | Línea 1401... | 
          
            | 1534 | }
 | 1401 | }
 | 
          
            | 1535 |  
 | 1402 |  
 | 
          
            | 1536 | /**
 | 1403 | /**
 | 
          
            | Línea 1545... | Línea 1412... | 
          
            | 1545 |  * @return navigation_node Returns the question branch that was added
 | 1412 |  * @return navigation_node Returns the question branch that was added
 | 
          
            | 1546 |  */
 | 1413 |  */
 | 
          
            | 1547 | function question_extend_settings_navigation(navigation_node $navigationnode, $context, $baseurl = '/question/edit.php') {
 | 1414 | function question_extend_settings_navigation(navigation_node $navigationnode, $context, $baseurl = '/question/edit.php') {
 | 
          
            | 1548 |     global $PAGE;
 | 1415 |     global $PAGE;
 | 
          
            | Línea 1549... | Línea 1416... | 
          
            | 1549 |  
 | 1416 |  
 | 
          
            | - |   | 1417 |     $iscourse = $context->contextlevel === CONTEXT_COURSE;
 | 
          
            | - |   | 1418 |  
 | 
          
            | 1550 |     if ($context->contextlevel == CONTEXT_COURSE) {
 | 1419 |     if ($iscourse) {
 | 
          
            | 1551 |         $params = ['courseid' => $context->instanceid];
 | 1420 |         $params = ['courseid' => $context->instanceid];
 | 
          
            | 1552 |     } else if ($context->contextlevel == CONTEXT_MODULE) {
 | 1421 |     } else if ($context->contextlevel == CONTEXT_MODULE) {
 | 
          
            | 1553 |         $params = ['cmid' => $context->instanceid];
 | 1422 |         $params = ['cmid' => $context->instanceid];
 | 
          
            | 1554 |     } else {
 | 1423 |     } else {
 | 
          
            | Línea 1557... | Línea 1426... | 
          
            | 1557 |  
 | 1426 |  
 | 
          
            | 1558 |     if (($cat = $PAGE->url->param('cat')) && preg_match('~\d+,\d+~', $cat)) {
 | 1427 |     if (($cat = $PAGE->url->param('cat')) && preg_match('~\d+,\d+~', $cat)) {
 | 
          
            | 1559 |         $params['cat'] = $cat;
 | 1428 |         $params['cat'] = $cat;
 | 
          
            | Línea 1560... | Línea 1429... | 
          
            | 1560 |     }
 | 1429 |     }
 | 
          
            | 1561 |  
 | 1430 |  
 | 
          
            | Línea 1562... | Línea 1431... | 
          
            | 1562 |     $questionnode = $navigationnode->add(get_string('questionbank', 'question'),
 | 1431 |     $questionnode = $navigationnode->add(get_string($iscourse ? 'questionbank_plural' : 'questionbank', 'question'),
 | 
          
            | 1563 |             new moodle_url($baseurl, $params), navigation_node::TYPE_CONTAINER, null, 'questionbank');
 | 1432 |             new moodle_url($baseurl, $params), navigation_node::TYPE_CONTAINER, null, 'questionbank');
 | 
          
            | 1564 |  
 | 1433 |  
 | 
          
            | Línea 1690... | Línea 1559... | 
          
            | 1690 |  * @param int $itemid item ID
 | 1559 |  * @param int $itemid item ID
 | 
          
            | 1691 |  * @param array $options options
 | 1560 |  * @param array $options options
 | 
          
            | 1692 |  * @return string
 | 1561 |  * @return string
 | 
          
            | 1693 |  */
 | 1562 |  */
 | 
          
            | 1694 | function question_rewrite_question_urls($text, $file, $contextid, $component, $filearea,
 | 1563 | function question_rewrite_question_urls($text, $file, $contextid, $component, $filearea,
 | 
          
            | 1695 |                                         array $ids, $itemid, array $options=null): string {
 | 1564 |                                         array $ids, $itemid, ?array $options=null): string {
 | 
          
            | Línea 1696... | Línea 1565... | 
          
            | 1696 |  
 | 1565 |  
 | 
          
            | 1697 |     $idsstr = '';
 | 1566 |     $idsstr = '';
 | 
          
            | 1698 |     if (!empty($ids)) {
 | 1567 |     if (!empty($ids)) {
 | 
          
            | 1699 |         $idsstr .= implode('/', $ids);
 | 1568 |         $idsstr .= implode('/', $ids);
 |