MDL-61363 tag: allow tagging in different context to item

This commit is contained in:
Ryan Wyllie
2018-03-07 10:31:46 +08:00
parent 8376557c4c
commit 5436b0ed02
6 changed files with 122 additions and 9 deletions
Regular → Executable
+3 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20171222" COMMENT="XMLDB file for core Moodle tables"
<XMLDB PATH="lib/db" VERSION="20180222" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@@ -2019,6 +2019,7 @@
<FIELD NAME="callback" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="callbackfile" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="showstandard" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="multiplecontexts" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether the tag area allows tag instances to be created in multiple contexts."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
@@ -2081,7 +2082,7 @@
<KEY NAME="contextid" TYPE="foreign" FIELDS="contextid" REFTABLE="context" REFFIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="taggeditem" UNIQUE="true" FIELDS="component, itemtype, itemid, tiuserid, tagid"/>
<INDEX NAME="taggeditem" UNIQUE="true" FIELDS="component, itemtype, itemid, contextid, tiuserid, tagid"/>
<INDEX NAME="taglookup" UNIQUE="false" FIELDS="itemtype, component, tagid, contextid"/>
</INDEXES>
</TABLE>
+1
View File
@@ -69,6 +69,7 @@ $tagareas = array(
array(
'itemtype' => 'question', // Questions.
'component' => 'core_question',
'multiplecontexts' => true,
),
array(
'itemtype' => 'post', // Blog posts.
+39 -1
View File
@@ -1974,7 +1974,6 @@ function xmldb_main_upgrade($oldversion) {
}
if ($oldversion < 2018022800.01) {
// Fix old block configurations that use the deprecated (and now removed) object class.
upgrade_fix_block_instance_configuration();
@@ -1982,5 +1981,44 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2018022800.01);
}
if ($oldversion < 2018022800.02) {
// Define index taggeditem (unique) to be dropped form tag_instance.
$table = new xmldb_table('tag_instance');
$index = new xmldb_index('taggeditem', XMLDB_INDEX_UNIQUE, array('component',
'itemtype', 'itemid', 'tiuserid', 'tagid'));
// Conditionally launch drop index taggeditem.
if ($dbman->index_exists($table, $index)) {
$dbman->drop_index($table, $index);
}
$index = new xmldb_index('taggeditem', XMLDB_INDEX_UNIQUE, array('component',
'itemtype', 'itemid', 'contextid', 'tiuserid', 'tagid'));
// Conditionally launch add index taggeditem.
if (!$dbman->index_exists($table, $index)) {
$dbman->add_index($table, $index);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2018022800.02);
}
if ($oldversion < 2018022800.03) {
// Define field multiplecontexts to be added to tag_area.
$table = new xmldb_table('tag_area');
$field = new xmldb_field('multiplecontexts', XMLDB_TYPE_INTEGER, '1', null,
XMLDB_NOTNULL, null, '0', 'showstandard');
// Conditionally launch add field multiplecontexts.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2018022800.03);
}
return true;
}
+33 -2
View File
@@ -122,6 +122,32 @@ class core_tag_area {
return null;
}
/**
* Checks if the tag area allows items to be tagged in multiple different contexts.
*
* If true then it indicates that not all tag instance contexts must match the
* context of the item they are tagging. If false then all tag instance should
* match the context of the item they are tagging.
*
* Example use case for multi-context tagging:
* A question that exists in a course category context may be used by multiple
* child courses. The question tag area can allow tag instances to be created in
* multiple contexts which allows the tag API to tag the question at the course
* category context and then seperately in each of the child course contexts.
*
* @param string $component component responsible for tagging
* @param string $itemtype what is being tagged, for example, 'post', 'course', 'user', etc.
* @return bool
*/
public static function allows_tagging_in_multiple_contexts($component, $itemtype) {
$itemtypes = self::get_areas();
if (isset($itemtypes[$itemtype][$component])) {
$config = $itemtypes[$itemtype][$component];
return isset($config->multiplecontexts) ? $config->multiplecontexts : false;
}
return false;
}
/**
* Returns the id of the tag collection that should be used for storing tags of this itemtype
*
@@ -217,7 +243,8 @@ class core_tag_area {
'tagcollid' => $record->tagcollid,
'callback' => $record->callback,
'callbackfile' => $record->callbackfile,
'showstandard' => isset($record->showstandard) ? $record->showstandard : core_tag_tag::BOTH_STANDARD_AND_NOT));
'showstandard' => isset($record->showstandard) ? $record->showstandard : core_tag_tag::BOTH_STANDARD_AND_NOT,
'multiplecontexts' => isset($record->multiplecontexts) ? $record->multiplecontexts : 0));
// Reset cache.
cache::make('core', 'tags')->delete('tag_area');
@@ -233,7 +260,8 @@ class core_tag_area {
global $DB;
$data = array_intersect_key((array)$data,
array('enabled' => 1, 'tagcollid' => 1,
'callback' => 1, 'callbackfile' => 1, 'showstandard' => 1));
'callback' => 1, 'callbackfile' => 1, 'showstandard' => 1,
'multiplecontexts' => 1));
foreach ($data as $key => $value) {
if ($existing->$key == $value) {
unset($data[$key]);
@@ -310,6 +338,9 @@ class core_tag_area {
if (!isset($record->callbackfile)) {
$record->callbackfile = null;
}
if (!isset($record->multiplecontexts)) {
$record->multiplecontexts = false;
}
$itemtypes[$record->itemtype . ':' . $record->component] = $record;
}
}
+45 -3
View File
@@ -602,7 +602,7 @@ class core_tag_tag {
global $DB;
$this->ensure_fields_exist(array('name', 'rawname'), 'add_instance');
$taginstance = new StdClass;
$taginstance = new stdClass;
$taginstance->tagid = $this->id;
$taginstance->component = $component ? $component : '';
$taginstance->itemid = $itemid;
@@ -773,12 +773,35 @@ class core_tag_tag {
$tagobjects = array();
}
$allowmultiplecontexts = core_tag_area::allows_tagging_in_multiple_contexts($component, $itemtype);
$currenttags = static::get_item_tags($component, $itemtype, $itemid, self::BOTH_STANDARD_AND_NOT, $tiuserid);
$taginstanceidstomovecontext = [];
// For data coherence reasons, it's better to remove deleted tags
// before adding new data: ordering could be duplicated.
foreach ($currenttags as $currenttag) {
if (!array_key_exists($currenttag->name, $tagobjects)) {
$hasbeenrequested = array_key_exists($currenttag->name, $tagobjects);
$issamecontext = $currenttag->taginstancecontextid == $context->id;
if ($allowmultiplecontexts) {
// If the tag area allows multiple contexts then we should only be
// managing tags in the given $context. All other tags can be ignored.
$shoulddelete = $issamecontext && !$hasbeenrequested;
} else {
// If the tag area only allows tag instances in a single context then
// all tags that aren't in the requested tags should be deleted, regardless
// of their context, if they are not part of the new set of tags.
$shoulddelete = !$hasbeenrequested;
// If the tag instance isn't in the correct context (legacy data)
// then we should take this opportunity to update it with the correct
// context id.
if (!$shoulddelete && !$issamecontext) {
$currenttag->taginstancecontextid = $context->id;
$taginstanceidstomovecontext[] = $currenttag->taginstanceid;
}
}
if ($shoulddelete) {
$taginstance = (object)array('id' => $currenttag->taginstanceid,
'itemtype' => $itemtype, 'itemid' => $itemid,
'contextid' => $currenttag->taginstancecontextid, 'tiuserid' => $tiuserid);
@@ -786,11 +809,30 @@ class core_tag_tag {
}
}
if (!empty($taginstanceidstomovecontext)) {
static::change_instances_context($taginstanceidstomovecontext, $context);
}
$ordering = -1;
foreach ($tagobjects as $name => $tag) {
$ordering++;
foreach ($currenttags as $currenttag) {
if (strval($currenttag->name) === strval($name)) {
$namesmatch = strval($currenttag->name) === strval($name);
if ($allowmultiplecontexts) {
// If the tag area allows multiple contexts then we should only
// skip adding a new instance if the existing one is in the correct
// context.
$contextsmatch = $currenttag->taginstancecontextid == $context->id;
$shouldskipinstance = $namesmatch && $contextsmatch;
} else {
// The existing behaviour for single context tag areas is to
// skip adding a new instance regardless of whether the existing
// instance is in the same context as the provided $context.
$shouldskipinstance = $namesmatch;
}
if ($shouldskipinstance) {
if ($currenttag->ordering != $ordering) {
$currenttag->update_instance_ordering($currenttag->taginstanceid, $ordering);
}
+1 -1
View File
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2018022800.01; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2018022800.03; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.