MDL-61363 question: update tags on question context change

This commit is contained in:
Ryan Wyllie 2018-02-20 16:02:06 +08:00
parent 081eb156c5
commit 68beb75851

View File

@ -537,6 +537,120 @@ function question_delete_activity($cm, $feedback=true) {
return true;
}
/**
* This function will handle moving all tag instances to a new context for a
* given list of questions.
*
* Questions can be tagged in up to two contexts:
* 1.) The context the question exists in.
* 2.) The course context (if the question context is a higher context.
* E.g. course category context or system context.
*
* This means a question that exists in a higher context (e.g. course cat or
* system context) may have multiple groups of tags in any number of child
* course contexts.
*
* Questions in the course category context can be move "down" a context level
* into one of their child course contexts or activity contexts which affects the
* availability of that question in other courses / activities.
*
* In this case it makes the questions no longer available in the other course or
* activity contexts so we need to make sure that the tag instances in those other
* contexts are removed.
*
* @param stdClass[] $questions The list of question being moved (must include
* the id and contextid)
* @param context $newcontext The Moodle context the questions are being moved to
*/
function question_move_question_tags_to_new_context(array $questions, context $newcontext) {
// If the questions are moving to a new course/activity context then we need to
// find any existing tag instances from any unavailable course contexts and
// delete them because they will no longer be applicable (we don't support
// tagging questions across courses).
$instancestodelete = [];
$instancesfornewcontext = [];
$newcontextparentids = $newcontext->get_parent_context_ids();
$questionids = array_map(function($question) {
return $question->id;
}, $questions);
$questionstagobjects = core_tag_tag::get_items_tags('core_question', 'question', $questionids);
foreach ($questions as $question) {
$tagobjects = $questionstagobjects[$question->id];
foreach ($tagobjects as $tagobject) {
$tagid = $tagobject->taginstanceid;
$tagcontextid = $tagobject->taginstancecontextid;
$istaginnewcontext = $tagcontextid == $newcontext->id;
$istaginquestioncontext = $tagcontextid == $question->contextid;
if ($istaginnewcontext) {
// This tag instance is already in the correct context so we can
// ignore it.
continue;
}
if ($istaginquestioncontext) {
// This tag instance is in the question context so it needs to be
// updated.
$instancesfornewcontext[] = $tagid;
continue;
}
// These tag instances are in neither the new context nor the
// question context so we need to determine what to do based on
// the context they are in and the new question context.
$tagcontext = context::instance_by_id($tagcontextid);
$tagcoursecontext = $tagcontext->get_course_context(false);
// The tag is in a course context if get_course_context() returns
// itself.
$istaginstancecontextcourse = !empty($tagcoursecontext)
&& $tagcontext->id == $tagcoursecontext->id;
if ($istaginstancecontextcourse) {
// If the tag instance is in a course context we need to add some
// special handling.
$tagcontextparentids = $tagcontext->get_parent_context_ids();
$isnewcontextaparent = in_array($newcontext->id, $tagcontextparentids);
$isnewcontextachild = in_array($tagcontext->id, $newcontextparentids);
if ($isnewcontextaparent) {
// If the tag instance is a course context tag and the new
// context is still a parent context to the tag context then
// we can leave this tag where it is.
continue;
} else if ($isnewcontextachild) {
// If the new context is a child context (e.g. activity) of this
// tag instance then we should move all of this tag instance
// down into the activity context along with the question.
$instancesfornewcontext[] = $tagid;
} else {
// If the tag is in a course context that is no longer a parent
// or child of the new context then this tag instance should be
// removed.
$instancestodelete[] = $tagid;
}
} else {
// This is a catch all for any tag instances not in the question
// context or a course context. These tag instances should be
// updated to the new context id. This will clean up old invalid
// data.
$instancesfornewcontext[] = $tagid;
}
}
}
if (!empty($instancestodelete)) {
// Delete any course context tags that may no longer be valid.
core_tag_tag::delete_instances_by_id($instancestodelete);
}
if (!empty($instancesfornewcontext)) {
// Update the tag instances to the new context id.
core_tag_tag::change_instances_context($instancesfornewcontext, $newcontext);
}
}
/**
* This function should be considered private to the question bank, it is called from
* question/editlib.php question/contextmoveq.php and a few similar places to to the
@ -573,8 +687,8 @@ function question_move_questions_to_category($questionids, $newcategoryid) {
$DB->set_field_select('question', 'category', $newcategoryid,
"parent $questionidcondition", $params);
// Update the contextid for any tag instances that may exist for these questions.
core_tag_tag::change_items_context('core_question', 'question', $questionids, $newcontextid);
$newcontext = context::instance_by_id($newcontextid);
question_move_question_tags_to_new_context($questions, $newcontext);
// TODO Deal with datasets.
@ -597,6 +711,7 @@ function question_move_questions_to_category($questionids, $newcategoryid) {
function question_move_category_to_context($categoryid, $oldcontextid, $newcontextid) {
global $DB;
$questions = [];
$questionids = $DB->get_records_menu('question',
array('category' => $categoryid), '', 'id,qtype');
foreach ($questionids as $questionid => $qtype) {
@ -604,10 +719,15 @@ function question_move_category_to_context($categoryid, $oldcontextid, $newconte
$questionid, $oldcontextid, $newcontextid);
// Purge this question from the cache.
question_bank::notify_question_edited($questionid);
$questions[] = (object) [
'id' => $questionid,
'contextid' => $oldcontextid
];
}
core_tag_tag::change_items_context('core_question', 'question',
array_keys($questionids), $newcontextid);
$newcontext = context::instance_by_id($newcontextid);
question_move_question_tags_to_new_context($questions, $newcontext);
$subcatids = $DB->get_records_menu('question_categories',
array('parent' => $categoryid), '', 'id,1');