MDL-50851 core_tag: introduce tag collections
This commit is contained in:
+1
-2
@@ -1648,7 +1648,6 @@ function course_delete_module($cmid) {
|
||||
require_once($CFG->libdir.'/questionlib.php');
|
||||
require_once($CFG->dirroot.'/blog/lib.php');
|
||||
require_once($CFG->dirroot.'/calendar/lib.php');
|
||||
require_once($CFG->dirroot.'/tag/lib.php');
|
||||
|
||||
// Get the course module.
|
||||
if (!$cm = $DB->get_record('course_modules', array('id' => $cmid))) {
|
||||
@@ -1717,7 +1716,7 @@ function course_delete_module($cmid) {
|
||||
'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY));
|
||||
|
||||
// Delete all tag instances associated with the instance of this module.
|
||||
tag_delete_instances('mod_' . $modulename, $modcontext->id);
|
||||
core_tag::delete_instances('mod_' . $modulename, null, $modcontext->id);
|
||||
|
||||
// Delete the context.
|
||||
context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
|
||||
|
||||
@@ -60,6 +60,7 @@ $string['cachedef_plugin_manager'] = 'Plugin info manager';
|
||||
$string['cachedef_questiondata'] = 'Question definitions';
|
||||
$string['cachedef_repositories'] = 'Repositories instances data';
|
||||
$string['cachedef_string'] = 'Language string cache';
|
||||
$string['cachedef_tags'] = 'Tags collections and areas';
|
||||
$string['cachedef_userselections'] = 'Data used to persist user selections throughout Moodle';
|
||||
$string['cachedef_yuimodules'] = 'YUI Module definitions';
|
||||
$string['cachelock_file_default'] = 'Default file locking';
|
||||
|
||||
@@ -20,3 +20,4 @@ updated,core_tag
|
||||
withselectedtags,core_tag
|
||||
tag:create,core_role
|
||||
categoriesanditems,core_grades
|
||||
taggedwith,core_tag
|
||||
|
||||
@@ -421,6 +421,7 @@ $string['submissionoutofsequencefriendlymessage'] = "You have entered data outsi
|
||||
$string['submit'] = 'Submit';
|
||||
$string['submitandfinish'] = 'Submit and finish';
|
||||
$string['submitted'] = 'Submit: {$a}';
|
||||
$string['tagarea_question'] = 'Questions';
|
||||
$string['technicalinfo'] = 'Technical information';
|
||||
$string['technicalinfo_help'] = 'This technical information is probably only useful for developers working on new question types. It may also be helpful when trying to diagnose problems with questions.';
|
||||
$string['technicalinfominfraction'] = 'Minimum fraction: {$a}';
|
||||
|
||||
+39
-2
@@ -24,12 +24,19 @@
|
||||
|
||||
$string['added'] = 'Official tag(s) added';
|
||||
$string['addotags'] = 'Add official tags';
|
||||
$string['addtagcoll'] = 'Add tag collection';
|
||||
$string['addtagtomyinterests'] = 'Add "{$a}" to my interests';
|
||||
$string['alltagpages'] = 'All tag pages';
|
||||
$string['backtoallitems'] = 'Back to all items tagged with "{$a}"';
|
||||
$string['changessaved'] = 'Changes saved';
|
||||
$string['changetagcoll'] = 'Change tag collection of area {$a}';
|
||||
$string['collnameexplained'] = 'Leave the field empty to use the default value: {$a}';
|
||||
$string['component'] = 'Component';
|
||||
$string['confirmdeletetag'] = 'Are you sure you want to delete this tag?';
|
||||
$string['confirmdeletetags'] = 'Are you sure you want to delete selected tags?';
|
||||
$string['count'] = 'Count';
|
||||
$string['coursetags'] = 'Course tags';
|
||||
$string['defautltagcoll'] = 'Default collection';
|
||||
$string['delete'] = 'Delete';
|
||||
$string['deleteselected'] = 'Delete selected';
|
||||
$string['deleted'] = 'Tag(s) deleted';
|
||||
@@ -38,15 +45,20 @@ $string['description'] = 'Description';
|
||||
$string['editname'] = 'Edit tag name';
|
||||
$string['edittag'] = 'Edit this tag';
|
||||
$string['entertags'] = 'Enter tags...';
|
||||
$string['edittagcoll'] = 'Edit tag collection {$a}';
|
||||
$string['errortagfrontpage'] = 'Tagging the site main page is not allowed';
|
||||
$string['errorupdatingrecord'] = 'Error updating tag record';
|
||||
$string['eventtagadded'] = 'Tag added to an item';
|
||||
$string['eventtagcolldeleted'] = 'Tag collection deleted';
|
||||
$string['eventtagcollcreated'] = 'Tag collection created';
|
||||
$string['eventtagcollupdated'] = 'Tag collection updatedhp ';
|
||||
$string['eventtagcreated'] = 'Tag created';
|
||||
$string['eventtagdeleted'] = 'Tag deleted';
|
||||
$string['eventtagflagged'] = 'Tag flagged';
|
||||
$string['eventtagremoved'] = 'Tag removed from an item';
|
||||
$string['eventtagunflagged'] = 'Tag unflagged';
|
||||
$string['eventtagupdated'] = 'Tag updated';
|
||||
$string['exclusivemode'] = 'Show only tagged {$a->tagarea}';
|
||||
$string['flag'] = 'Flag';
|
||||
$string['flagged'] = 'Tag flagged';
|
||||
$string['flagasinappropriate'] = 'Flag as inappropriate';
|
||||
@@ -54,17 +66,25 @@ $string['helprelatedtags'] = 'Comma separated related tags';
|
||||
$string['changename'] = 'Change tag name';
|
||||
$string['changetype'] = 'Change tag type';
|
||||
$string['id'] = 'id';
|
||||
$string['inalltagcoll'] = 'Everywhere';
|
||||
$string['itemstaggedwith'] = '{$a->tagarea} tagged with "{$a->tag}"';
|
||||
$string['lesstags'] = 'less...';
|
||||
$string['manageofficialtags'] = 'Manage official tags';
|
||||
$string['managetags'] = 'Manage tags';
|
||||
$string['managetagcolls'] = 'Manage tag collections';
|
||||
$string['moretags'] = 'more...';
|
||||
$string['name'] = 'Tag name';
|
||||
$string['namesalreadybeeingused'] = 'Tag names already being used';
|
||||
$string['newnamefor'] = 'New name for tag {$a}';
|
||||
$string['nextpage'] = 'More';
|
||||
$string['notagsfound'] = 'No tags matching "{$a}" found';
|
||||
$string['noresultsfor'] = 'No results for "{$a}"';
|
||||
$string['nothingtoupdate'] = 'Nothing to update';
|
||||
$string['officialtag'] = 'Official';
|
||||
$string['otags'] = 'Official tags';
|
||||
$string['othertags'] = 'Other tags';
|
||||
$string['owner'] = 'Owner';
|
||||
$string['prevpage'] = 'Back';
|
||||
$string['ptags'] = 'User defined tags (Comma separated)';
|
||||
$string['relatedblogs'] = 'Most recent blog entries';
|
||||
$string['relatedtags'] = 'Related tags';
|
||||
@@ -75,16 +95,29 @@ $string['responsiblewillbenotified'] = 'The person responsible will be notified'
|
||||
$string['rssdesc'] = 'This RSS feed was automatically generated by Moodle and contains user generated tags for courses.';
|
||||
$string['rsstitle'] = 'Course tags RSS feed for user: {$a}';
|
||||
$string['search'] = 'Search';
|
||||
$string['searchable'] = 'Searchable';
|
||||
$string['searchable_help'] = 'Tags in this tag collection can be searched for on "Search tags" page. If unchecked, tags can still be accessed by clicking on them or via different search interfaces.';
|
||||
$string['searchresultsfor'] = 'Search results for "{$a}"';
|
||||
$string['searchtags'] = 'Search tags';
|
||||
$string['seeallblogs'] = 'See all blog entries tagged with "{$a}"...';
|
||||
$string['seeallblogs'] = 'See all blog entries tagged with "{$a}"';
|
||||
$string['select'] = 'Select';
|
||||
$string['selectcoll'] = 'Select tag collection';
|
||||
$string['selecttag'] = 'Select tag {$a}';
|
||||
$string['settypedefault'] = 'Remove from official tags';
|
||||
$string['settypeofficial'] = 'Make official';
|
||||
$string['showingfirsttags'] = 'Showing {$a} most popular tags';
|
||||
$string['suredeletecoll'] = 'Are you sure you want to delete tag collection "{$a}"?';
|
||||
$string['tag'] = 'Tag';
|
||||
$string['tagarea_blog_external'] = 'External blog posts';
|
||||
$string['tagarea_post'] = 'Blog posts';
|
||||
$string['tagarea_user'] = 'User interests';
|
||||
$string['tagarea_course'] = 'Courses';
|
||||
$string['tagareaenabled'] = 'Enabled';
|
||||
$string['tagareaname'] = 'Name';
|
||||
$string['tagareas'] = 'Tag areas';
|
||||
$string['tagcollection'] = 'Tag collection';
|
||||
$string['tagcollections'] = 'Tag collections';
|
||||
$string['tagdescription'] = 'Tag description';
|
||||
$string['taggedwith'] = 'tagged with "{$a}"';
|
||||
$string['tags'] = 'Tags';
|
||||
$string['tagsaredisabled'] = 'Tags are disabled';
|
||||
$string['tagtype'] = 'Tag type';
|
||||
@@ -107,3 +140,7 @@ $string['tagtype_official'] = 'Official';
|
||||
$string['thistaghasnodesc'] = 'This tag currently has no description.';
|
||||
$string['updated'] = 'Updated';
|
||||
$string['withselectedtags'] = 'With selected tags...';
|
||||
|
||||
// Deprecated since 3.1 .
|
||||
|
||||
$string['taggedwith'] = 'tagged with "{$a}"';
|
||||
|
||||
+2
-3
@@ -166,9 +166,8 @@ function uninstall_plugin($type, $name) {
|
||||
|
||||
echo $OUTPUT->heading($pluginname);
|
||||
|
||||
// Delete all tag instances associated with this plugin.
|
||||
require_once($CFG->dirroot . '/tag/lib.php');
|
||||
tag_delete_instances($component);
|
||||
// Delete all tag areas, collections and instances associated with this plugin.
|
||||
core_tag_area::uninstall($component);
|
||||
|
||||
// Custom plugin uninstall.
|
||||
$plugindirectory = core_component::get_plugin_directory($type, $name);
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/templates","core/notification","core/str","core/config"],function(a,b,c,d,e,f){return{init_manage_page:function(){var g=function(b){var c=b.closest("tr").get(0);if(c){var d=a(c).find("td.col-timemodified").get(0);e.get_string("now").done(function(b){a(d).html(b)})}};a(".tag-management-table").delegate(".tagtype","click",function(d){d.preventDefault();var e=a(this),f=e.attr("data-id"),h=e.attr("data-value"),i="1"===h?0:1,j=b.call([{methodname:"core_tag_update_tags",args:{tags:[{id:f,official:i}]}},{methodname:"core_tag_get_tags",args:{tags:[{id:f}]}}],!0);a.when.apply(a,j).done(function(a,b){void 0===a.warnings[0]&&void 0!==b.tags[0]&&c.render("core_tag/tagtype",b.tags[0]).done(function(a){g(e);var b=e.parent();e.replaceWith(a),b.find(".tagtype").get(0).focus()})})}),a(".tag-management-table").delegate(".tagflag","click",function(d){d.preventDefault();var e=a(this),f=e.attr("data-id"),h=e.attr("data-value"),i="0"===h?1:0,j=b.call([{methodname:"core_tag_update_tags",args:{tags:[{id:f,flag:i}]}},{methodname:"core_tag_get_tags",args:{tags:[{id:f}]}}],!0);a.when.apply(a,j).done(function(b,d){if(void 0===b.warnings[0]&&void 0!==d.tags[0]){var f=e.closest("tr").get(0);f&&(d.tags[0].flag?a(f).addClass("flagged-tag"):a(f).removeClass("flagged-tag")),c.render("core_tag/tagflag",d.tags[0]).done(function(a){g(e);var b=e.parent();e.replaceWith(a),b.find(".tagflag").get(0).focus()})}})}),a(".tag-management-table").delegate("a.tagdelete","click",function(b){b.preventDefault();var c=a(this).attr("href");e.get_strings([{key:"delete"},{key:"confirmdeletetag",component:"tag"},{key:"yes"},{key:"no"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],function(){window.location.href=c})})}),a("#tag-management-delete").click(function(b){var c=a(this).closest("form").get(0),f=a(c).find("input[type=checkbox]:checked").length;return f?(b.preventDefault(),void e.get_strings([{key:"delete"},{key:"confirmdeletetags",component:"tag"},{key:"yes"},{key:"no"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],function(){c.submit()})})):!1}),a(".tag-management-table").delegate(".tagnameedit","click keypress",function(h){if("keypress"!==h.type||13===h.keyCode){h.stopImmediatePropagation(),h.preventDefault();var i=a(this),j=a(i.closest("td").get(0)),k=a(j.find("input").get(0)),l=i.attr("data-id"),m=function(f,h){var i=b.call([{methodname:"core_tag_update_tags",args:{tags:[{id:f,rawname:h}]}},{methodname:"core_tag_get_tags",args:{tags:[{id:f}]}}],!0);a.when.apply(a,i).done(function(b,f){void 0!==b.warnings[0]?e.get_string("error").done(function(a){d.alert(a,b.warnings[0].message)}):void 0!==f.tags[0]&&c.render("core_tag/tagname",f.tags[0]).done(function(b){g(j),j.html(b),a(j.find(".tagnameedit").get(0)).focus()})})},n=function(){a(".tag-management-table td.tageditingon").each(function(){var b=a(this),c=a(b.find("input").get(0));c.off(),b.removeClass("tageditingon"),c.val(b.attr("data-value"))})};n(),j.addClass("tageditingon"),j.attr("data-value",k.val()),k.select(),k.on("keypress focusout",function(a){f.behatsiterunning&&"focusout"===a.type||("keypress"===a.type&&13===a.keyCode&&(m(l,k.val()),n()),("keypress"===a.type&&27===a.keyCode||"focusout"===a.type)&&n())})}})}}});
|
||||
define(["jquery","core/ajax","core/templates","core/notification","core/str","core/config"],function(a,b,c,d,e,f){return{init_tagindex_page:function(){a("body").delegate(".tagarea[data-ta] a[data-quickload=1]","click",function(d){d.preventDefault();var e=a(this),f=e.context.search.replace(/^\?/,""),g=e.closest(".tagarea[data-ta]"),h=f.split("&").reduce(function(a,b){var c=b.split("=");return a[c[0]]=decodeURIComponent(c[1]),a},{}),i=b.call([{methodname:"core_tag_get_tagindex",args:{tagindex:h}}],!0);a.when.apply(a,i).done(function(a){c.render("core_tag/index",a).done(function(a){g.replaceWith(a)})})})},init_manage_page:function(){var g=function(b){var c=b.closest("tr").get(0);if(c){var d=a(c).find("td.col-timemodified").get(0);e.get_string("now").done(function(b){a(d).html(b)})}};a(".tag-management-table").delegate(".tagtype","click",function(d){d.preventDefault();var e=a(this),f=e.attr("data-id"),h=e.attr("data-value"),i="1"===h?0:1,j=b.call([{methodname:"core_tag_update_tags",args:{tags:[{id:f,official:i}]}},{methodname:"core_tag_get_tags",args:{tags:[{id:f}]}}],!0);a.when.apply(a,j).done(function(a,b){void 0===a.warnings[0]&&void 0!==b.tags[0]&&c.render("core_tag/tagtype",b.tags[0]).done(function(a){g(e);var b=e.parent();e.replaceWith(a),b.find(".tagtype").get(0).focus()})})}),a(".tag-management-table").delegate(".tagflag","click",function(d){d.preventDefault();var e=a(this),f=e.attr("data-id"),h=e.attr("data-value"),i="0"===h?1:0,j=b.call([{methodname:"core_tag_update_tags",args:{tags:[{id:f,flag:i}]}},{methodname:"core_tag_get_tags",args:{tags:[{id:f}]}}],!0);a.when.apply(a,j).done(function(b,d){if(void 0===b.warnings[0]&&void 0!==d.tags[0]){var f=e.closest("tr").get(0);f&&(d.tags[0].flag?a(f).addClass("flagged-tag"):a(f).removeClass("flagged-tag")),c.render("core_tag/tagflag",d.tags[0]).done(function(a){g(e);var b=e.parent();e.replaceWith(a),b.find(".tagflag").get(0).focus()})}})}),a(".tag-management-table").delegate("a.tagdelete","click",function(b){b.preventDefault();var c=a(this).attr("href");e.get_strings([{key:"delete"},{key:"confirmdeletetag",component:"tag"},{key:"yes"},{key:"no"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],function(){window.location.href=c})})}),a("#tag-management-delete").click(function(b){var c=a(this).closest("form").get(0),f=a(c).find("input[type=checkbox]:checked").length;return f?(b.preventDefault(),void e.get_strings([{key:"delete"},{key:"confirmdeletetags",component:"tag"},{key:"yes"},{key:"no"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],function(){c.submit()})})):!1}),a(".tag-management-table").delegate(".tagnameedit","click keypress",function(h){if("keypress"!==h.type||13===h.keyCode){h.stopImmediatePropagation(),h.preventDefault();var i=a(this),j=a(i.closest("td").get(0)),k=a(j.find("input").get(0)),l=i.attr("data-id"),m=function(f,h){var i=b.call([{methodname:"core_tag_update_tags",args:{tags:[{id:f,rawname:h}]}},{methodname:"core_tag_get_tags",args:{tags:[{id:f}]}}],!0);a.when.apply(a,i).done(function(b,f){void 0!==b.warnings[0]?e.get_string("error").done(function(a){d.alert(a,b.warnings[0].message)}):void 0!==f.tags[0]&&c.render("core_tag/tagname",f.tags[0]).done(function(b){g(j),j.html(b),a(j.find(".tagnameedit").get(0)).focus()})})},n=function(){a(".tag-management-table td.tageditingon").each(function(){var b=a(this),c=a(b.find("input").get(0));c.off(),b.removeClass("tageditingon"),c.val(b.attr("data-value"))})};n(),j.addClass("tageditingon"),j.attr("data-value",k.val()),k.select(),k.on("keypress focusout",function(a){f.behatsiterunning&&"focusout"===a.type||("keypress"===a.type&&13===a.keyCode&&(m(l,k.val()),n()),("keypress"===a.type&&27===a.keyCode||"focusout"===a.type)&&n())})}})}}});
|
||||
+30
-2
@@ -27,9 +27,37 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str'
|
||||
return /** @alias module:core/tag */ {
|
||||
|
||||
/**
|
||||
* Initialises handlers for AJAX methods.
|
||||
* Initialises tag index page.
|
||||
*
|
||||
* @method init
|
||||
* @method init_tagindex_page
|
||||
*/
|
||||
init_tagindex_page: function() {
|
||||
// Click handler for changing tag type.
|
||||
$('body').delegate('.tagarea[data-ta] a[data-quickload=1]', 'click', function(e) {
|
||||
e.preventDefault();
|
||||
var target = $( this ),
|
||||
query = target.context.search.replace(/^\?/, ''),
|
||||
tagarea = target.closest('.tagarea[data-ta]'),
|
||||
args = query.split('&').reduce(function(s,c){var t=c.split('=');s[t[0]]=decodeURIComponent(t[1]);return s;},{});
|
||||
|
||||
var promises = ajax.call([{
|
||||
methodname: 'core_tag_get_tagindex',
|
||||
args: { tagindex: args }
|
||||
}], true);
|
||||
|
||||
$.when.apply($, promises)
|
||||
.done( function(data) {
|
||||
templates.render('core_tag/index', data).done(function(html) {
|
||||
tagarea.replaceWith(html);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialises tag management page.
|
||||
*
|
||||
* @method init_manage_page
|
||||
*/
|
||||
init_manage_page: function() {
|
||||
|
||||
|
||||
+2
-2
@@ -1497,10 +1497,10 @@ class block_manager {
|
||||
if ($bits[0] == 'tag' && !empty($this->page->subpage)) {
|
||||
// better navbar for tag pages
|
||||
$editpage->navbar->add(get_string('tags'), new moodle_url('/tag/'));
|
||||
$tag = tag_get('id', $this->page->subpage, '*');
|
||||
$tag = core_tag_tag::get($this->page->subpage);
|
||||
// tag search page doesn't have subpageid
|
||||
if ($tag) {
|
||||
$editpage->navbar->add($tag->name, new moodle_url('/tag/index.php', array('id'=>$tag->id)));
|
||||
$editpage->navbar->add($tag->get_display_name(), $tag->get_view_url());
|
||||
}
|
||||
}
|
||||
$editpage->navbar->add($block->get_title());
|
||||
|
||||
@@ -74,6 +74,34 @@ class tag_added extends base {
|
||||
s($this->other['itemtype']) . "' with id '{$this->other['itemid']}'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event from taginstance object
|
||||
*
|
||||
* @since Moodle 3.1
|
||||
* @param stdClass $taginstance
|
||||
* @param string $tagname
|
||||
* @param string $tagrawname
|
||||
* @param bool $addsnapshot trust that $taginstance has all necessary fields and add it as a record snapshot
|
||||
* @return tag_added
|
||||
*/
|
||||
public static function create_from_tag_instance($taginstance, $tagname, $tagrawname, $addsnapshot = false) {
|
||||
$event = self::create(array(
|
||||
'objectid' => $taginstance->id,
|
||||
'contextid' => $taginstance->contextid,
|
||||
'other' => array(
|
||||
'tagid' => $taginstance->tagid,
|
||||
'tagname' => $tagname,
|
||||
'tagrawname' => $tagrawname,
|
||||
'itemid' => $taginstance->itemid,
|
||||
'itemtype' => $taginstance->itemtype
|
||||
)
|
||||
));
|
||||
if ($addsnapshot) {
|
||||
$event->add_record_snapshot('tag_instance', $taginstance);
|
||||
}
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return legacy data for add_to_log().
|
||||
*
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tag collection created event.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core\event;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Tag collection created event class.
|
||||
*
|
||||
* @package core
|
||||
* @since Moodle 3.0
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tag_collection_created extends base {
|
||||
|
||||
/**
|
||||
* Init method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function init() {
|
||||
$this->data['objecttable'] = 'tag_coll';
|
||||
$this->data['crud'] = 'c';
|
||||
$this->data['edulevel'] = self::LEVEL_OTHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to create new event.
|
||||
*
|
||||
* @param object $tagcoll
|
||||
* @return user_graded
|
||||
*/
|
||||
public static function create_from_record($tagcoll) {
|
||||
$event = self::create(array(
|
||||
'objectid' => $tagcoll->id,
|
||||
'context' => \context_system::instance(),
|
||||
));
|
||||
$event->add_record_snapshot('tag_coll', $tagcoll);
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('eventtagcollcreated', 'core_tag');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description() {
|
||||
return "The user with id '$this->userid' created the tag collection with id '$this->objectid'";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tag collection deleted event.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core\event;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Tag collection deleted event class.
|
||||
*
|
||||
* @package core
|
||||
* @since Moodle 3.0
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tag_collection_deleted extends base {
|
||||
|
||||
/**
|
||||
* Init method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function init() {
|
||||
$this->data['objecttable'] = 'tag_coll';
|
||||
$this->data['crud'] = 'd';
|
||||
$this->data['edulevel'] = self::LEVEL_OTHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to create new event.
|
||||
*
|
||||
* @param object $tagcoll
|
||||
* @return user_graded
|
||||
*/
|
||||
public static function create_from_record($tagcoll) {
|
||||
$event = self::create(array(
|
||||
'objectid' => $tagcoll->id,
|
||||
'context' => \context_system::instance(),
|
||||
));
|
||||
$event->add_record_snapshot('tag_coll', $tagcoll);
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('eventtagcolldeleted', 'core_tag');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description() {
|
||||
return "The user with id '$this->userid' deleted the tag collection with id '$this->objectid'";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tag collection updated event.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core\event;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Tag collection updated event class.
|
||||
*
|
||||
* @package core
|
||||
* @since Moodle 3.0
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tag_collection_updated extends base {
|
||||
|
||||
/**
|
||||
* Init method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function init() {
|
||||
$this->data['objecttable'] = 'tag_coll';
|
||||
$this->data['crud'] = 'u';
|
||||
$this->data['edulevel'] = self::LEVEL_OTHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to create new event.
|
||||
*
|
||||
* @param object $tagcoll
|
||||
* @return user_graded
|
||||
*/
|
||||
public static function create_from_record($tagcoll) {
|
||||
$event = self::create(array(
|
||||
'objectid' => $tagcoll->id,
|
||||
'context' => \context_system::instance(),
|
||||
));
|
||||
$event->add_record_snapshot('tag_coll', $tagcoll);
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('eventtagcollupdated', 'core_tag');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description() {
|
||||
return "The user with id '$this->userid' updated the tag collection with id '$this->objectid'";
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,26 @@ class tag_created extends base {
|
||||
$this->data['edulevel'] = self::LEVEL_OTHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event from tag object
|
||||
*
|
||||
* @since Moodle 3.1
|
||||
* @param \core_tag_tag|\stdClass $tag
|
||||
* @return tag_created
|
||||
*/
|
||||
public static function create_from_tag($tag) {
|
||||
$event = self::create(array(
|
||||
'objectid' => $tag->id,
|
||||
'relateduserid' => $tag->userid,
|
||||
'context' => \context_system::instance(),
|
||||
'other' => array(
|
||||
'name' => $tag->name,
|
||||
'rawname' => $tag->rawname
|
||||
)
|
||||
));
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns localised general event name.
|
||||
*
|
||||
|
||||
@@ -74,6 +74,34 @@ class tag_removed extends base {
|
||||
s($this->other['itemtype']) . "' with id '{$this->other['itemid']}'.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event from taginstance object
|
||||
*
|
||||
* @since Moodle 3.1
|
||||
* @param stdClass $taginstance
|
||||
* @param string $tagname
|
||||
* @param string $tagrawname
|
||||
* @param bool $addsnapshot trust that $taginstance has all necessary fields and add it as a record snapshot
|
||||
* @return tag_removed
|
||||
*/
|
||||
public static function create_from_tag_instance($taginstance, $tagname, $tagrawname, $addsnapshot = false) {
|
||||
$event = self::create(array(
|
||||
'objectid' => $taginstance->id,
|
||||
'contextid' => $taginstance->contextid,
|
||||
'other' => array(
|
||||
'tagid' => $taginstance->tagid,
|
||||
'tagname' => $tagname,
|
||||
'tagrawname' => $tagrawname,
|
||||
'itemid' => $taginstance->itemid,
|
||||
'itemtype' => $taginstance->itemtype
|
||||
)
|
||||
));
|
||||
if ($addsnapshot) {
|
||||
$event->add_record_snapshot('tag_instance', $taginstance);
|
||||
}
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validation.
|
||||
*
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
*/
|
||||
namespace core\task;
|
||||
|
||||
use core_tag_collection, core_tag_tag, core_tag_area, stdClass;
|
||||
|
||||
/**
|
||||
* Simple task to run the tag cron.
|
||||
*/
|
||||
@@ -45,9 +47,224 @@ class tag_cron_task extends scheduled_task {
|
||||
global $CFG;
|
||||
|
||||
if (!empty($CFG->usetags)) {
|
||||
require_once($CFG->dirroot.'/tag/lib.php');
|
||||
tag_cron();
|
||||
$this->compute_correlations();
|
||||
$this->cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and stores the correlated tags of all tags.
|
||||
*
|
||||
* The correlations are stored in the 'tag_correlation' table.
|
||||
*
|
||||
* Two tags are correlated if they appear together a lot. Ex.: Users tagged with "computers"
|
||||
* will probably also be tagged with "algorithms".
|
||||
*
|
||||
* The rationale for the 'tag_correlation' table is performance. It works as a cache
|
||||
* for a potentially heavy load query done at the 'tag_instance' table. So, the
|
||||
* 'tag_correlation' table stores redundant information derived from the 'tag_instance' table.
|
||||
*
|
||||
* @param int $mincorrelation Only tags with more than $mincorrelation correlations will be identified.
|
||||
*/
|
||||
public function compute_correlations($mincorrelation = 2) {
|
||||
global $DB;
|
||||
|
||||
// This mighty one line query fetches a row from the database for every
|
||||
// individual tag correlation. We then need to process the rows collecting
|
||||
// the correlations for each tag id.
|
||||
// The fields used by this query are as follows:
|
||||
// tagid : This is the tag id, there should be at least $mincorrelation
|
||||
// rows for each tag id.
|
||||
// correlation : This is the tag id that correlates to the above tagid field.
|
||||
// correlationid : This is the id of the row in the tag_correlation table that
|
||||
// relates to the tagid field and will be NULL if there are no
|
||||
// existing correlations.
|
||||
$sql = 'SELECT pairs.tagid, pairs.correlation, pairs.ocurrences, co.id AS correlationid
|
||||
FROM (
|
||||
SELECT ta.tagid, tb.tagid AS correlation, COUNT(*) AS ocurrences
|
||||
FROM {tag_instance} ta
|
||||
JOIN {tag} tga ON ta.tagid = tga.id
|
||||
JOIN {tag_instance} tb ON (ta.itemtype = tb.itemtype AND ta.component = tb.component
|
||||
AND ta.itemid = tb.itemid AND ta.tagid <> tb.tagid)
|
||||
JOIN {tag} tgb ON tb.tagid = tgb.id AND tgb.tagcollid = tga.tagcollid
|
||||
GROUP BY ta.tagid, tb.tagid
|
||||
HAVING COUNT(*) > :mincorrelation
|
||||
) pairs
|
||||
LEFT JOIN {tag_correlation} co ON co.tagid = pairs.tagid
|
||||
ORDER BY pairs.tagid ASC, pairs.ocurrences DESC, pairs.correlation ASC';
|
||||
$rs = $DB->get_recordset_sql($sql, array('mincorrelation' => $mincorrelation));
|
||||
|
||||
// Set up an empty tag correlation object.
|
||||
$tagcorrelation = new stdClass;
|
||||
$tagcorrelation->id = null;
|
||||
$tagcorrelation->tagid = null;
|
||||
$tagcorrelation->correlatedtags = array();
|
||||
|
||||
// We store each correlation id in this array so we can remove any correlations
|
||||
// that no longer exist.
|
||||
$correlations = array();
|
||||
|
||||
// Iterate each row of the result set and build them into tag correlations.
|
||||
// We add all of a tag's correlations to $tagcorrelation->correlatedtags[]
|
||||
// then save the $tagcorrelation object.
|
||||
foreach ($rs as $row) {
|
||||
if ($row->tagid != $tagcorrelation->tagid) {
|
||||
// The tag id has changed so we have all of the correlations for this tag.
|
||||
$tagcorrelationid = $this->process_computed_correlation($tagcorrelation);
|
||||
if ($tagcorrelationid) {
|
||||
$correlations[] = $tagcorrelationid;
|
||||
}
|
||||
// Now we reset the tag correlation object so we can reuse it and set it
|
||||
// up for the current record.
|
||||
$tagcorrelation = new stdClass;
|
||||
$tagcorrelation->id = $row->correlationid;
|
||||
$tagcorrelation->tagid = $row->tagid;
|
||||
$tagcorrelation->correlatedtags = array();
|
||||
}
|
||||
// Save the correlation on the tag correlation object.
|
||||
$tagcorrelation->correlatedtags[] = $row->correlation;
|
||||
}
|
||||
// Update the current correlation after the last record.
|
||||
$tagcorrelationid = $this->process_computed_correlation($tagcorrelation);
|
||||
if ($tagcorrelationid) {
|
||||
$correlations[] = $tagcorrelationid;
|
||||
}
|
||||
|
||||
// Close the recordset.
|
||||
$rs->close();
|
||||
|
||||
// Remove any correlations that weren't just identified.
|
||||
if (empty($correlations)) {
|
||||
// There are no tag correlations.
|
||||
$DB->delete_records('tag_correlation');
|
||||
} else {
|
||||
list($sql, $params) = $DB->get_in_or_equal($correlations,
|
||||
SQL_PARAMS_NAMED, 'param0000', false);
|
||||
$DB->delete_records_select('tag_correlation', 'id '.$sql, $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the tag tables, making sure all tagged object still exists.
|
||||
*
|
||||
* This method is called from cron.
|
||||
*
|
||||
* This should normally not be necessary, but in case related tags are not deleted
|
||||
* when the tagged record is removed, this should be done once in a while, perhaps
|
||||
* on an occasional cron run. On a site with lots of tags, this could become an
|
||||
* expensive function to call.
|
||||
*/
|
||||
public function cleanup() {
|
||||
global $DB;
|
||||
|
||||
// Get ids to delete from instances where the tag has been deleted. This should never happen apparently.
|
||||
$sql = "SELECT ti.id
|
||||
FROM {tag_instance} ti
|
||||
LEFT JOIN {tag} t ON t.id = ti.tagid
|
||||
WHERE t.id IS null";
|
||||
$tagids = $DB->get_records_sql($sql);
|
||||
$tagarray = array();
|
||||
foreach ($tagids as $tagid) {
|
||||
$tagarray[] = $tagid->id;
|
||||
}
|
||||
|
||||
// Next get ids from instances that have an owner that has been deleted.
|
||||
$sql = "SELECT ti.id
|
||||
FROM {tag_instance} ti, {user} u
|
||||
WHERE ti.itemid = u.id
|
||||
AND ti.itemtype = 'user'
|
||||
AND ti.component = 'core'
|
||||
AND u.deleted = 1";
|
||||
$tagids = $DB->get_records_sql($sql);
|
||||
foreach ($tagids as $tagid) {
|
||||
$tagarray[] = $tagid->id;
|
||||
}
|
||||
|
||||
// Get the other itemtypes.
|
||||
$sql = "SELECT DISTINCT component, itemtype
|
||||
FROM {tag_instance}
|
||||
WHERE itemtype <> 'user' or component <> 'core'";
|
||||
$tagareas = $DB->get_records_sql($sql);
|
||||
foreach ($tagareas as $tagarea) {
|
||||
$sql = 'SELECT ti.id
|
||||
FROM {tag_instance} ti
|
||||
LEFT JOIN {' . $tagarea->itemtype . '} it ON it.id = ti.itemid
|
||||
WHERE it.id IS null
|
||||
AND ti.itemtype = ? AND ti.component = ?';
|
||||
$tagids = $DB->get_records_sql($sql, array($tagarea->itemtype, $tagarea->component));
|
||||
foreach ($tagids as $tagid) {
|
||||
$tagarray[] = $tagid->id;
|
||||
}
|
||||
}
|
||||
|
||||
// Get instances for each of the ids to be deleted.
|
||||
if (count($tagarray) > 0) {
|
||||
list($sqlin, $params) = $DB->get_in_or_equal($tagarray);
|
||||
$sql = "SELECT ti.*, COALESCE(t.name, 'deleted') AS name, COALESCE(t.rawname, 'deleted') AS rawname
|
||||
FROM {tag_instance} ti
|
||||
LEFT JOIN {tag} t ON t.id = ti.tagid
|
||||
WHERE ti.id $sqlin";
|
||||
$instances = $DB->get_records_sql($sql, $params);
|
||||
$this->bulk_delete_instances($instances);
|
||||
}
|
||||
|
||||
core_tag_collection::cleanup_unused_tags();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function processes a tag correlation and makes changes in the database as required.
|
||||
*
|
||||
* The tag correlation object needs have both a tagid property and a correlatedtags property that is an array.
|
||||
*
|
||||
* @param stdClass $tagcorrelation
|
||||
* @return int/bool The id of the tag correlation that was just processed or false.
|
||||
*/
|
||||
public function process_computed_correlation(stdClass $tagcorrelation) {
|
||||
global $DB;
|
||||
|
||||
// You must provide a tagid and correlatedtags must be set and be an array.
|
||||
if (empty($tagcorrelation->tagid) || !isset($tagcorrelation->correlatedtags) ||
|
||||
!is_array($tagcorrelation->correlatedtags)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tagcorrelation->correlatedtags = join(',', $tagcorrelation->correlatedtags);
|
||||
if (!empty($tagcorrelation->id)) {
|
||||
// The tag correlation already exists so update it.
|
||||
$DB->update_record('tag_correlation', $tagcorrelation);
|
||||
} else {
|
||||
// This is a new correlation to insert.
|
||||
$tagcorrelation->id = $DB->insert_record('tag_correlation', $tagcorrelation);
|
||||
}
|
||||
return $tagcorrelation->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will delete numerous tag instances efficiently.
|
||||
* This removes tag instances only. It doesn't check to see if it is the last use of a tag.
|
||||
*
|
||||
* @param array $instances An array of tag instance objects with the addition of the tagname and tagrawname
|
||||
* (used for recording a delete event).
|
||||
*/
|
||||
public function bulk_delete_instances($instances) {
|
||||
global $DB;
|
||||
|
||||
$instanceids = array();
|
||||
foreach ($instances as $instance) {
|
||||
$instanceids[] = $instance->id;
|
||||
}
|
||||
|
||||
// This is a multi db compatible method of creating the correct sql when using the 'IN' value.
|
||||
// $insql is the sql statement, $params are the id numbers.
|
||||
list($insql, $params) = $DB->get_in_or_equal($instanceids);
|
||||
$sql = 'id ' . $insql;
|
||||
$DB->delete_records_select('tag_instance', $sql, $params);
|
||||
|
||||
// Now go through and record each tag individually with the event system.
|
||||
foreach ($instances as $instance) {
|
||||
// Trigger tag removed event (i.e. The tag instance has been removed).
|
||||
\core\event\tag_removed::create_from_tag_instance($instance, $instance->name,
|
||||
$instance->rawname, true)->trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,6 +252,12 @@ $definitions = array(
|
||||
'simpledata' => true,
|
||||
'staticacceleration' => true,
|
||||
'staticaccelerationsize' => 5
|
||||
),
|
||||
|
||||
// Caches data about tag collections and areas.
|
||||
'tags' => array(
|
||||
'mode' => cache_store::MODE_REQUEST,
|
||||
'simplekeys' => true,
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
+41
-5
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<XMLDB PATH="lib/db" VERSION="20150922" COMMENT="XMLDB file for core Moodle tables"
|
||||
<XMLDB PATH="lib/db" VERSION="20160111" COMMENT="XMLDB file for core Moodle tables"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
|
||||
>
|
||||
@@ -1958,10 +1958,43 @@
|
||||
<KEY NAME="importer" TYPE="foreign" FIELDS="importer" REFTABLE="user" REFFIELDS="id" COMMENT="user who is importing"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="tag_coll" COMMENT="Defines different set of tags">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="isdefault" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="sortorder" TYPE="int" LENGTH="5" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="searchable" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="Whether the tag collection is searchable"/>
|
||||
<FIELD NAME="customurl" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Custom URL for the tag page instead of /tag/index.php"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="tag_area" COMMENT="Defines various tag areas, one area is identified by component and itemtype">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="itemtype" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="enabled" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
|
||||
<FIELD NAME="tagcollid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="callback" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="callbackfile" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="tagcollid" TYPE="foreign" FIELDS="tagcollid" REFTABLE="tag_coll" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
<INDEXES>
|
||||
<INDEX NAME="compitemtype" UNIQUE="true" FIELDS="component, itemtype"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
<TABLE NAME="tag" COMMENT="Tag table - this generic table will replace the old "tags" table.">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="tagcollid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="rawname" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The raw, unnormalised name for the tag as entered by users"/>
|
||||
<FIELD NAME="tagtype" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
|
||||
@@ -1973,9 +2006,11 @@
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
|
||||
<KEY NAME="tagcollid" TYPE="foreign" FIELDS="tagcollid" REFTABLE="tag_coll" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
<INDEXES>
|
||||
<INDEX NAME="name" UNIQUE="true" FIELDS="name" COMMENT="tag names are unique"/>
|
||||
<INDEX NAME="tagcollname" UNIQUE="true" FIELDS="tagcollid, name"/>
|
||||
<INDEX NAME="tagcolltype" UNIQUE="false" FIELDS="tagcollid, tagtype"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
<TABLE NAME="tag_correlation" COMMENT="The rationale for the 'tag_correlation' table is performance. It works as a cache for a potentially heavy load query done at the 'tag_instance' table. So, the 'tag_correlation' table stores redundant information derived from the 'tag_instance' table">
|
||||
@@ -1993,8 +2028,8 @@
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="tagid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false" COMMENT="Defines the Moodle component which the tag was added to"/>
|
||||
<FIELD NAME="itemtype" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false" COMMENT="Defines the Moodle component which the tag was added to"/>
|
||||
<FIELD NAME="itemtype" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="The context id of the item that was tagged"/>
|
||||
<FIELD NAME="tiuserid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
@@ -2008,7 +2043,8 @@
|
||||
<KEY NAME="contextid" TYPE="foreign" FIELDS="contextid" REFTABLE="context" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
<INDEXES>
|
||||
<INDEX NAME="itemtype-itemid-tagid-tiuserid" UNIQUE="true" FIELDS="itemtype, itemid, tagid, tiuserid"/>
|
||||
<INDEX NAME="taggeditem" UNIQUE="true" FIELDS="component, itemtype, itemid, tiuserid, tagid"/>
|
||||
<INDEX NAME="taglookup" UNIQUE="false" FIELDS="itemtype, component, tagid, contextid"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
<TABLE NAME="groups" COMMENT="Each record represents a group.">
|
||||
|
||||
@@ -1148,6 +1148,14 @@ $functions = array(
|
||||
'ajax' => true
|
||||
),
|
||||
|
||||
'core_tag_get_tagindex' => array(
|
||||
'classname' => 'core_tag_external',
|
||||
'methodname' => 'get_tagindex',
|
||||
'description' => 'Gets tag index page for one tag and one tag area',
|
||||
'type' => 'read',
|
||||
'ajax' => true
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
$services = array(
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tag area definitions
|
||||
*
|
||||
* File db/tag.php lists all available tag areas in core or a plugin.
|
||||
*
|
||||
* Each tag area may have the following attributes:
|
||||
* - itemtype (required) - what is tagged. Must be name of the existing DB table
|
||||
* - component - component responsible for tagging, if the tag area is inside a
|
||||
* plugin the component must be the full frankenstyle name of the plugin
|
||||
* - collection - name of the custom tag collection that will be used to store
|
||||
* tags in this area. If specified aministrator will be able to neither add
|
||||
* any other tag areas to this collection nor move this tag area elsewhere
|
||||
* - searchable (only if collection is specified) - wether the tag collection
|
||||
* should be searchable on /tag/search.php
|
||||
* - customurl (only if collection is specified) - custom url to use instead of
|
||||
* /tag/search.php to display information about one tag
|
||||
* - callback - name of the function that returns items tagged with this tag,
|
||||
* see core_tag_tag::get_tag_index() and existing callbacks for more details,
|
||||
* callback should return instance of core_tag\output\tagindex
|
||||
* - callbackfile - file where callback is located (if not an autoloaded location)
|
||||
*
|
||||
* Language file must contain the human-readable names of the tag areas and
|
||||
* collections (either in plugin language file or in component language file or
|
||||
* lang/en/tag.php in case of core):
|
||||
* - for item type "user":
|
||||
* $string['tagarea_user'] = 'Users';
|
||||
* - for tag collection "mycollection":
|
||||
* $string['tagcollection_mycollection'] = 'My tag collection';
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$tagareas = array(
|
||||
array(
|
||||
'itemtype' => 'user', // Users.
|
||||
'component' => 'core',
|
||||
'callback' => 'user_get_tagged_users',
|
||||
'callbackfile' => '/user/lib.php',
|
||||
),
|
||||
array(
|
||||
'itemtype' => 'course', // Courses.
|
||||
'component' => 'core',
|
||||
'callback' => 'course_get_tagged_courses',
|
||||
'callbackfile' => '/course/lib.php',
|
||||
),
|
||||
array(
|
||||
'itemtype' => 'question', // Questions.
|
||||
'component' => 'core_question',
|
||||
),
|
||||
array(
|
||||
'itemtype' => 'post', // Blog posts.
|
||||
'component' => 'core',
|
||||
'callback' => 'blog_get_tagged_posts',
|
||||
'callbackfile' => '/blog/lib.php',
|
||||
),
|
||||
array(
|
||||
'itemtype' => 'blog_external', // External blogs.
|
||||
'component' => 'core',
|
||||
),
|
||||
);
|
||||
+2
-2
@@ -71,8 +71,8 @@ $tasks = array(
|
||||
array(
|
||||
'classname' => 'core\task\tag_cron_task',
|
||||
'blocking' => 0,
|
||||
'minute' => '20',
|
||||
'hour' => '*',
|
||||
'minute' => 'R',
|
||||
'hour' => '3',
|
||||
'day' => '*',
|
||||
'dayofweek' => '*',
|
||||
'month' => '*'
|
||||
|
||||
@@ -4609,5 +4609,191 @@ function xmldb_main_upgrade($oldversion) {
|
||||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2016011100.00) {
|
||||
|
||||
// This is a big upgrade script. We create new table tag_coll and the field
|
||||
// tag.tagcollid pointing to it.
|
||||
|
||||
// Define table tag_coll to be created.
|
||||
$table = new xmldb_table('tag_coll');
|
||||
|
||||
// Adding fields to table tagcloud.
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
$table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
|
||||
$table->add_field('isdefault', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0');
|
||||
$table->add_field('component', XMLDB_TYPE_CHAR, '100', null, null, null, null);
|
||||
$table->add_field('sortorder', XMLDB_TYPE_INTEGER, '5', null, XMLDB_NOTNULL, null, '0');
|
||||
$table->add_field('searchable', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1');
|
||||
$table->add_field('customurl', XMLDB_TYPE_CHAR, '255', null, null, null, null);
|
||||
|
||||
// Adding keys to table tagcloud.
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
|
||||
|
||||
// Conditionally launch create table for tagcloud.
|
||||
if (!$dbman->table_exists($table)) {
|
||||
$dbman->create_table($table);
|
||||
}
|
||||
|
||||
// Table {tag}.
|
||||
// Define index name (unique) to be dropped form tag - we will replace it with index on (tagcollid,name) later.
|
||||
$table = new xmldb_table('tag');
|
||||
$index = new xmldb_index('name', XMLDB_INDEX_UNIQUE, array('name'));
|
||||
|
||||
// Conditionally launch drop index name.
|
||||
if ($dbman->index_exists($table, $index)) {
|
||||
$dbman->drop_index($table, $index);
|
||||
}
|
||||
|
||||
// Define field tagcollid to be added to tag, we create it as null first and will change to notnull later.
|
||||
$table = new xmldb_table('tag');
|
||||
$field = new xmldb_field('tagcollid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'userid');
|
||||
|
||||
// Conditionally launch add field tagcloudid.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2016011100.00);
|
||||
}
|
||||
|
||||
if ($oldversion < 2016011100.02) {
|
||||
// Create a default tag collection if not exists and update the field tag.tagcollid to point to it.
|
||||
if (!$tcid = $DB->get_field_sql('SELECT id FROM {tag_coll} ORDER BY isdefault DESC, sortorder, id', null,
|
||||
IGNORE_MULTIPLE)) {
|
||||
$tcid = $DB->insert_record('tag_coll', array('isdefault' => 1, 'sortorder' => 0));
|
||||
}
|
||||
$DB->execute('UPDATE {tag} SET tagcollid = ? WHERE tagcollid IS NULL', array($tcid));
|
||||
|
||||
// Define index tagcollname (unique) to be added to tag.
|
||||
$table = new xmldb_table('tag');
|
||||
$index = new xmldb_index('tagcollname', XMLDB_INDEX_UNIQUE, array('tagcollid', 'name'));
|
||||
$field = new xmldb_field('tagcollid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'userid');
|
||||
|
||||
// Conditionally launch add index tagcollname.
|
||||
if (!$dbman->index_exists($table, $index)) {
|
||||
// Launch change of nullability for field tagcollid.
|
||||
$dbman->change_field_notnull($table, $field);
|
||||
$dbman->add_index($table, $index);
|
||||
}
|
||||
|
||||
// Define key tagcollid (foreign) to be added to tag.
|
||||
$table = new xmldb_table('tag');
|
||||
$key = new xmldb_key('tagcollid', XMLDB_KEY_FOREIGN, array('tagcollid'), 'tag_coll', array('id'));
|
||||
|
||||
// Launch add key tagcloudid.
|
||||
$dbman->add_key($table, $key);
|
||||
|
||||
// Define index tagcolltype (not unique) to be added to tag.
|
||||
$table = new xmldb_table('tag');
|
||||
$index = new xmldb_index('tagcolltype', XMLDB_INDEX_NOTUNIQUE, array('tagcollid', 'tagtype'));
|
||||
|
||||
// Conditionally launch add index tagcolltype.
|
||||
if (!$dbman->index_exists($table, $index)) {
|
||||
$dbman->add_index($table, $index);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2016011100.02);
|
||||
}
|
||||
|
||||
if ($oldversion < 2016011100.03) {
|
||||
|
||||
// Define table tag_area to be created.
|
||||
$table = new xmldb_table('tag_area');
|
||||
|
||||
// Adding fields to table tag_area.
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
$table->add_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
|
||||
$table->add_field('itemtype', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
|
||||
$table->add_field('enabled', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1');
|
||||
$table->add_field('tagcollid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
|
||||
$table->add_field('callback', XMLDB_TYPE_CHAR, '100', null, null, null, null);
|
||||
$table->add_field('callbackfile', XMLDB_TYPE_CHAR, '100', null, null, null, null);
|
||||
|
||||
// Adding keys to table tag_area.
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
|
||||
$table->add_key('tagcollid', XMLDB_KEY_FOREIGN, array('tagcollid'), 'tag_coll', array('id'));
|
||||
|
||||
// Adding indexes to table tag_area.
|
||||
$table->add_index('compitemtype', XMLDB_INDEX_UNIQUE, array('component', 'itemtype'));
|
||||
|
||||
// Conditionally launch create table for tag_area.
|
||||
if (!$dbman->table_exists($table)) {
|
||||
$dbman->create_table($table);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2016011100.03);
|
||||
}
|
||||
|
||||
if ($oldversion < 2016011100.12) {
|
||||
|
||||
// Define index itemtype-itemid-tagid-tiuserid (unique) to be dropped form tag_instance.
|
||||
$table = new xmldb_table('tag_instance');
|
||||
$index = new xmldb_index('itemtype-itemid-tagid-tiuserid', XMLDB_INDEX_UNIQUE,
|
||||
array('itemtype', 'itemid', 'tagid', 'tiuserid'));
|
||||
|
||||
// Conditionally launch drop index itemtype-itemid-tagid-tiuserid.
|
||||
if ($dbman->index_exists($table, $index)) {
|
||||
$dbman->drop_index($table, $index);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2016011100.12);
|
||||
}
|
||||
|
||||
if ($oldversion < 2016011100.13) {
|
||||
|
||||
$DB->execute("UPDATE {tag_instance} SET component = ? WHERE component IS NULL", array(''));
|
||||
|
||||
// Changing nullability of field component on table tag_instance to not null.
|
||||
$table = new xmldb_table('tag_instance');
|
||||
$field = new xmldb_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, 'tagid');
|
||||
|
||||
// Launch change of nullability for field component.
|
||||
$dbman->change_field_notnull($table, $field);
|
||||
|
||||
// Changing type of field itemtype on table tag_instance to char.
|
||||
$table = new xmldb_table('tag_instance');
|
||||
$field = new xmldb_field('itemtype', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, 'component');
|
||||
|
||||
// Launch change of type for field itemtype.
|
||||
$dbman->change_field_type($table, $field);
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2016011100.13);
|
||||
}
|
||||
|
||||
if ($oldversion < 2016011100.14) {
|
||||
|
||||
// Define index taggeditem (unique) to be added to tag_instance.
|
||||
$table = new xmldb_table('tag_instance');
|
||||
$index = new xmldb_index('taggeditem', XMLDB_INDEX_UNIQUE, array('component', 'itemtype', 'itemid', '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, 2016011100.14);
|
||||
}
|
||||
|
||||
if ($oldversion < 2016011100.15) {
|
||||
|
||||
// Define index taglookup (not unique) to be added to tag_instance.
|
||||
$table = new xmldb_table('tag_instance');
|
||||
$index = new xmldb_index('taglookup', XMLDB_INDEX_NOTUNIQUE, array('itemtype', 'component', 'tagid', 'contextid'));
|
||||
|
||||
// Conditionally launch add index taglookup.
|
||||
if (!$dbman->index_exists($table, $index)) {
|
||||
$dbman->add_index($table, $index);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2016011100.15);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+1261
-31
File diff suppressed because it is too large
Load Diff
+122
-33
@@ -63,6 +63,12 @@ class MoodleQuickForm_tags extends MoodleQuickForm_autocomplete {
|
||||
*/
|
||||
protected $showingofficial = false;
|
||||
|
||||
/**
|
||||
* Options passed when creating an element.
|
||||
* @var array
|
||||
*/
|
||||
protected $tagsoptions = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@@ -72,29 +78,66 @@ class MoodleQuickForm_tags extends MoodleQuickForm_autocomplete {
|
||||
* @param mixed $attributes Either a typical HTML attribute string or an associative array.
|
||||
*/
|
||||
public function __construct($elementName = null, $elementLabel = null, $options = array(), $attributes = null) {
|
||||
if (!isset($options['display'])) {
|
||||
$options['display'] = self::DEFAULTUI;
|
||||
}
|
||||
|
||||
$this->showingofficial = $options['display'] != MoodleQuickForm_tags::NOOFFICIAL;
|
||||
|
||||
$validoptions = array();
|
||||
if ($this->showingofficial) {
|
||||
$validoptions = $this->load_official_tags();
|
||||
|
||||
if (!empty($options)) {
|
||||
// Only execute it when the element was created and $options has values set by user.
|
||||
// In onQuickFormEvent() we make sure that $options is not empty even if developer left it empty.
|
||||
if (empty($options['display'])) {
|
||||
$options['display'] = self::DEFAULTUI;
|
||||
}
|
||||
$this->tagsoptions = $options;
|
||||
|
||||
$this->showingofficial = $options['display'] != self::NOOFFICIAL;
|
||||
|
||||
if ($this->showingofficial) {
|
||||
$validoptions = $this->load_official_tags();
|
||||
}
|
||||
// Option 'tags' allows us to type new tags.
|
||||
if ($options['display'] == self::ONLYOFFICIAL) {
|
||||
$attributes['tags'] = false;
|
||||
} else {
|
||||
$attributes['tags'] = true;
|
||||
}
|
||||
$attributes['multiple'] = 'multiple';
|
||||
$attributes['placeholder'] = get_string('entertags', 'tag');
|
||||
$attributes['showsuggestions'] = $this->showingofficial;
|
||||
}
|
||||
// 'tags' option allows us to type new tags.
|
||||
if ($options['display'] == MoodleQuickForm_tags::ONLYOFFICIAL) {
|
||||
$attributes['tags'] = false;
|
||||
} else {
|
||||
$attributes['tags'] = true;
|
||||
}
|
||||
$attributes['multiple'] = 'multiple';
|
||||
$attributes['placeholder'] = get_string('entertags', 'tag');
|
||||
$attributes['showsuggestions'] = $this->showingofficial;
|
||||
|
||||
parent::__construct($elementName, $elementLabel, $validoptions, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by HTML_QuickForm whenever form event is made on this element
|
||||
*
|
||||
* @param string $event Name of event
|
||||
* @param mixed $arg event arguments
|
||||
* @param object $caller calling object
|
||||
* @return bool
|
||||
*/
|
||||
public function onQuickFormEvent($event, $arg, &$caller) {
|
||||
if ($event === 'createElement') {
|
||||
$arg[2] += array('itemtype' => '', 'component' => '');
|
||||
}
|
||||
return parent::onQuickFormEvent($event, $arg, $caller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if tagging is enabled for this itemtype
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_tagging_enabled() {
|
||||
if (!empty($this->tagsoptions['itemtype']) && !empty($this->tagsoptions['component'])) {
|
||||
$enabled = core_tag_tag::is_enabled($this->tagsoptions['component'], $this->tagsoptions['itemtype']);
|
||||
if ($enabled === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Backward compatibility with code developed before Moodle 3.0 where itemtype/component were not specified.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Old syntax of class constructor. Deprecated in PHP7.
|
||||
*
|
||||
@@ -105,21 +148,41 @@ class MoodleQuickForm_tags extends MoodleQuickForm_autocomplete {
|
||||
self::__construct($elementName, $elementLabel, $options, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the tag collection to use for official tag selector
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function get_tag_collection() {
|
||||
if (empty($this->tagsoptions['tagcollid']) && (empty($this->tagsoptions['itemtype']) ||
|
||||
empty($this->tagsoptions['component']))) {
|
||||
debugging('You need to specify \'itemtype\' and \'component\' of the tagged '
|
||||
. 'area in the tags form element options',
|
||||
DEBUG_DEVELOPER);
|
||||
}
|
||||
if (!empty($this->tagsoptions['tagcollid'])) {
|
||||
return $this->tagsoptions['tagcollid'];
|
||||
}
|
||||
if ($this->tagsoptions['itemtype']) {
|
||||
$this->tagsoptions['tagcollid'] = core_tag_area::get_collection($this->tagsoptions['component'],
|
||||
$this->tagsoptions['itemtype']);
|
||||
} else {
|
||||
$this->tagsoptions['tagcollid'] = core_tag_collection::get_default();
|
||||
}
|
||||
return $this->tagsoptions['tagcollid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for select form element.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function toHtml(){
|
||||
global $CFG, $OUTPUT;
|
||||
|
||||
if (empty($CFG->usetags)) {
|
||||
debugging('A tags formslib field has been created even thought $CFG->usetags is false.', DEBUG_DEVELOPER);
|
||||
}
|
||||
global $OUTPUT;
|
||||
|
||||
$managelink = '';
|
||||
if (has_capability('moodle/tag:manage', context_system::instance()) && $this->showingofficial) {
|
||||
$url = $CFG->wwwroot .'/tag/manage.php';
|
||||
$url = new moodle_url('/tag/manage.php', array('tc' => $this->get_tag_collection()));
|
||||
$managelink = ' ' . $OUTPUT->action_link($url, get_string('manageofficialtags', 'tag'));
|
||||
}
|
||||
|
||||
@@ -127,21 +190,47 @@ class MoodleQuickForm_tags extends MoodleQuickForm_autocomplete {
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to load official tags
|
||||
* Accepts a renderer
|
||||
*
|
||||
* @access protected
|
||||
* @param HTML_QuickForm_Renderer $renderer An HTML_QuickForm_Renderer object
|
||||
* @param bool $required Whether a group is required
|
||||
* @param string $error An error message associated with a group
|
||||
*/
|
||||
public function accept(&$renderer, $required = false, $error = null) {
|
||||
if ($this->is_tagging_enabled()) {
|
||||
$renderer->renderElement($this, $required, $error);
|
||||
} else {
|
||||
$renderer->renderHidden($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to load official tags
|
||||
*/
|
||||
protected function load_official_tags() {
|
||||
global $CFG, $DB;
|
||||
|
||||
$namefield = empty($CFG->keeptagnamecase) ? 'name' : 'rawname';
|
||||
$records = $DB->get_records('tag', array('tagtype' => 'official'), $namefield, 'id,' . $namefield);
|
||||
$tags = array();
|
||||
|
||||
foreach ($records as $record) {
|
||||
$tags[$record->$namefield] = $record->$namefield;
|
||||
if (!$this->is_tagging_enabled()) {
|
||||
return array();
|
||||
}
|
||||
return $tags;
|
||||
$namefield = empty($CFG->keeptagnamecase) ? 'name' : 'rawname';
|
||||
$tags = $DB->get_records_menu('tag',
|
||||
array('tagtype' => 'official', 'tagcollid' => $this->get_tag_collection()),
|
||||
$namefield, 'id,' . $namefield);
|
||||
return array_combine($tags, $tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 'safe' element's value
|
||||
*
|
||||
* @param array $submitValues array of submitted values to search
|
||||
* @param bool $assoc whether to return the value as associative array
|
||||
* @return mixed
|
||||
*/
|
||||
public function exportValue(&$submitValues, $assoc = false) {
|
||||
if (!$this->is_tagging_enabled()) {
|
||||
return $assoc ? array($this->getName() => array()) : array();
|
||||
}
|
||||
|
||||
return parent::exportValue($submitValues, $assoc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4103,6 +4103,23 @@ EOD;
|
||||
$html .= html_writer::end_tag('header');
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the list of tags associated with an entry
|
||||
*
|
||||
* @param array $tags list of instances of core_tag or stdClass
|
||||
* @param string $label label to display in front, by default 'Tags' (get_string('tags')), set to null
|
||||
* to use default, set to '' (empty string) to omit the label completely
|
||||
* @param string $classes additional classes for the enclosing div element
|
||||
* @param int $limit limit the number of tags to display, if size of $tags is more than this limit the "more" link
|
||||
* will be appended to the end, JS will toggle the rest of the tags
|
||||
* @param context $pagecontext specify if needed to overwrite the current page context for the view tag link
|
||||
* @return string
|
||||
*/
|
||||
public function tag_list($tags, $label = null, $classes = '', $limit = 10, $pagecontext = null) {
|
||||
$list = new \core_tag\output\taglist($tags, $label, $classes, $limit, $pagecontext);
|
||||
return $this->render_from_template('core_tag/taglist', $list->export_for_template($this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -847,6 +847,10 @@ EOD;
|
||||
$record['tagtype'] = 'default';
|
||||
}
|
||||
|
||||
if (!isset($record['tagcollid'])) {
|
||||
$record['tagcollid'] = core_tag_collection::get_default();
|
||||
}
|
||||
|
||||
if (!isset($record['description'])) {
|
||||
$record['description'] = 'Tag description';
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ class core_test_generator_testcase extends advanced_testcase {
|
||||
$this->assertEquals($course->id, $section->course);
|
||||
|
||||
$course = $generator->create_course(array('tags' => 'Cat, Dog'));
|
||||
$this->assertEquals('Cat, Dog', tag_get_tags_csv('course', $course->id, TAG_RETURN_TEXT));
|
||||
$this->assertEquals(array('Cat', 'Dog'), array_values(core_tag_tag::get_item_tags_array('core', 'course', $course->id)));
|
||||
|
||||
$scale = $generator->create_scale();
|
||||
$this->assertNotEmpty($scale);
|
||||
|
||||
+3
-2
@@ -31,8 +31,9 @@ information provided here is intended especially for developers.
|
||||
custom user filters. Similar deprecations in existing user_filter_* classes.
|
||||
* table_default_export_format_parent::table_default_export_format_parent() is
|
||||
deprecated, use parent::__construct() in extending classes.
|
||||
* groups_delete_group_members() $showfeedback parameter has been removed and is no longer
|
||||
respected. Users of this function should output their own feedback if required.
|
||||
* groups_delete_group_members() $showfeedback parameter has been removed and is no longer
|
||||
respected. Users of this function should output their own feedback if required.
|
||||
* Number of changes to Tags API, see tag/upgrade.txt for more details
|
||||
|
||||
=== 3.0 ===
|
||||
|
||||
|
||||
@@ -494,6 +494,7 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) {
|
||||
message_update_processors($plug);
|
||||
}
|
||||
upgrade_plugin_mnet_functions($component);
|
||||
core_tag_area::reset_definitions_for_component($component);
|
||||
$endcallback($component, true, $verbose);
|
||||
}
|
||||
}
|
||||
@@ -532,6 +533,7 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) {
|
||||
message_update_processors($plug);
|
||||
}
|
||||
upgrade_plugin_mnet_functions($component);
|
||||
core_tag_area::reset_definitions_for_component($component);
|
||||
$endcallback($component, true, $verbose);
|
||||
|
||||
} else if ($installedversion < $plugin->version) { // upgrade
|
||||
@@ -566,6 +568,7 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) {
|
||||
message_update_processors($plug);
|
||||
}
|
||||
upgrade_plugin_mnet_functions($component);
|
||||
core_tag_area::reset_definitions_for_component($component);
|
||||
$endcallback($component, false, $verbose);
|
||||
|
||||
} else if ($installedversion > $plugin->version) {
|
||||
@@ -669,6 +672,7 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) {
|
||||
message_update_providers($component);
|
||||
\core\message\inbound\manager::update_handlers_for_component($component);
|
||||
upgrade_plugin_mnet_functions($component);
|
||||
core_tag_area::reset_definitions_for_component($component);
|
||||
$endcallback($component, true, $verbose);
|
||||
}
|
||||
}
|
||||
@@ -703,6 +707,7 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) {
|
||||
message_update_providers($component);
|
||||
\core\message\inbound\manager::update_handlers_for_component($component);
|
||||
upgrade_plugin_mnet_functions($component);
|
||||
core_tag_area::reset_definitions_for_component($component);
|
||||
|
||||
$endcallback($component, true, $verbose);
|
||||
|
||||
@@ -739,6 +744,7 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) {
|
||||
message_update_providers($component);
|
||||
\core\message\inbound\manager::update_handlers_for_component($component);
|
||||
upgrade_plugin_mnet_functions($component);
|
||||
core_tag_area::reset_definitions_for_component($component);
|
||||
|
||||
$endcallback($component, false, $verbose);
|
||||
|
||||
@@ -860,6 +866,7 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) {
|
||||
message_update_providers($component);
|
||||
\core\message\inbound\manager::update_handlers_for_component($component);
|
||||
upgrade_plugin_mnet_functions($component);
|
||||
core_tag_area::reset_definitions_for_component($component);
|
||||
$endcallback($component, true, $verbose);
|
||||
}
|
||||
}
|
||||
@@ -899,6 +906,7 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) {
|
||||
\core\task\manager::reset_scheduled_tasks_for_component($component);
|
||||
message_update_providers($component);
|
||||
\core\message\inbound\manager::update_handlers_for_component($component);
|
||||
core_tag_area::reset_definitions_for_component($component);
|
||||
upgrade_plugin_mnet_functions($component);
|
||||
|
||||
$endcallback($component, true, $verbose);
|
||||
@@ -935,6 +943,7 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) {
|
||||
message_update_providers($component);
|
||||
\core\message\inbound\manager::update_handlers_for_component($component);
|
||||
upgrade_plugin_mnet_functions($component);
|
||||
core_tag_area::reset_definitions_for_component($component);
|
||||
|
||||
$endcallback($component, false, $verbose);
|
||||
|
||||
@@ -1536,6 +1545,7 @@ function install_core($version, $verbose) {
|
||||
\core\task\manager::reset_scheduled_tasks_for_component('moodle');
|
||||
message_update_providers('moodle');
|
||||
\core\message\inbound\manager::update_handlers_for_component('moodle');
|
||||
core_tag_area::reset_definitions_for_component('moodle');
|
||||
|
||||
// Write default settings unconditionally
|
||||
admin_apply_default_settings(NULL, true);
|
||||
@@ -1603,6 +1613,7 @@ function upgrade_core($version, $verbose) {
|
||||
\core\task\manager::reset_scheduled_tasks_for_component('moodle');
|
||||
message_update_providers('moodle');
|
||||
\core\message\inbound\manager::update_handlers_for_component('moodle');
|
||||
core_tag_area::reset_definitions_for_component('moodle');
|
||||
// Update core definitions.
|
||||
cache_helper::update_definitions(true);
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ Feature: Edited wiki pages handle tags correctly
|
||||
And I expand "Site administration" node
|
||||
And I expand "Appearance" node
|
||||
And I follow "Manage tags"
|
||||
And I follow "Default collection"
|
||||
And I set the field "otagsadd" to "OT1, OT2, OT3"
|
||||
And I press "Add official tags"
|
||||
And I log out
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class core_tag_area for managing tag areas
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class to manage tag areas
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_tag_area {
|
||||
|
||||
/**
|
||||
* Returns the list of areas indexed by itemtype and component
|
||||
*
|
||||
* @param int $tagcollid return only areas in this tag collection
|
||||
* @param bool $enabledonly return only enabled tag areas
|
||||
* @return array itemtype=>component=>tagarea object
|
||||
*/
|
||||
public static function get_areas($tagcollid = null, $enabledonly = false) {
|
||||
global $DB;
|
||||
$cache = cache::make('core', 'tags');
|
||||
if (($itemtypes = $cache->get('tag_area')) === false) {
|
||||
$colls = core_tag_collection::get_collections();
|
||||
$defaultcoll = reset($colls);
|
||||
$itemtypes = array();
|
||||
$areas = $DB->get_records('tag_area', array(), 'component,itemtype');
|
||||
foreach ($areas as $area) {
|
||||
if ($colls[$area->tagcollid]->component) {
|
||||
$area->locked = true;
|
||||
}
|
||||
$itemtypes[$area->itemtype][$area->component] = $area;
|
||||
}
|
||||
$cache->set('tag_area', $itemtypes);
|
||||
}
|
||||
if ($tagcollid || $enabledonly) {
|
||||
$rv = array();
|
||||
foreach ($itemtypes as $itemtype => $it) {
|
||||
foreach ($it as $component => $v) {
|
||||
if (($v->tagcollid == $tagcollid || !$tagcollid) && (!$enabledonly || $v->enabled)) {
|
||||
$rv[$itemtype][$component] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $rv;
|
||||
}
|
||||
return $itemtypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves info about one tag area
|
||||
*
|
||||
* @param int $tagareaid
|
||||
* @return stdClass
|
||||
*/
|
||||
public static function get_by_id($tagareaid) {
|
||||
$tagareas = self::get_areas();
|
||||
foreach ($tagareas as $itemtype => $it) {
|
||||
foreach ($it as $component => $v) {
|
||||
if ($v->id == $tagareaid) {
|
||||
return $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display name for this area
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $itemtype
|
||||
* @return lang_string
|
||||
*/
|
||||
public static function display_name($component, $itemtype) {
|
||||
$identifier = 'tagarea_' . clean_param($itemtype, PARAM_STRINGID);
|
||||
if ($component === 'core') {
|
||||
$component = 'tag';
|
||||
}
|
||||
return new lang_string($identifier, $component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the tag area is enabled
|
||||
*
|
||||
* @param string $component component responsible for tagging
|
||||
* @param string $itemtype what is being tagged, for example, 'post', 'course', 'user', etc.
|
||||
* @return bool|null
|
||||
*/
|
||||
public static function is_enabled($component, $itemtype) {
|
||||
global $CFG;
|
||||
if (empty($CFG->usetags)) {
|
||||
return false;
|
||||
}
|
||||
$itemtypes = self::get_areas();
|
||||
if (isset($itemtypes[$itemtype][$component])) {
|
||||
return $itemtypes[$itemtype][$component]->enabled ? true : false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the tag collection that should be used for storing tags of this itemtype
|
||||
*
|
||||
* @param string $component component responsible for tagging
|
||||
* @param string $itemtype what is being tagged, for example, 'post', 'course', 'user', etc.
|
||||
* @return int
|
||||
*/
|
||||
public static function get_collection($component, $itemtype) {
|
||||
$itemtypes = self::get_areas();
|
||||
if (array_key_exists($itemtype, $itemtypes)) {
|
||||
if (!array_key_exists($component, $itemtypes[$itemtype])) {
|
||||
$component = key($itemtypes[$itemtype]);
|
||||
}
|
||||
return $itemtypes[$itemtype][$component]->tagcollid;
|
||||
}
|
||||
return core_tag_collection::get_default();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all tag areas and collections that are currently cached in DB for this component
|
||||
*
|
||||
* @param string $componentname
|
||||
* @return array first element is the list of areas and the second list of collections
|
||||
*/
|
||||
protected static function get_definitions_for_component($componentname) {
|
||||
global $DB;
|
||||
list($a, $b) = core_component::normalize_component($componentname);
|
||||
$component = $b ? ($a . '_' . $b) : $a;
|
||||
$sql = 'component = :component';
|
||||
$params = array('component' => $component);
|
||||
if ($component === 'core') {
|
||||
$sql .= ' OR component LIKE :coreprefix';
|
||||
$params['coreprefix'] = 'core_%';
|
||||
}
|
||||
$fields = $DB->sql_concat_join("':'", array('itemtype', 'component'));
|
||||
$existingareas = $DB->get_records_sql(
|
||||
"SELECT $fields AS returnkey, a.* FROM {tag_area} a WHERE $sql", $params);
|
||||
$fields = $DB->sql_concat_join("':'", array('name', 'component'));
|
||||
$existingcolls = $DB->get_records_sql(
|
||||
"SELECT $fields AS returnkey, t.* FROM {tag_coll} t WHERE $sql", $params);
|
||||
return array($existingareas, $existingcolls);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely delete a tag area and all instances inside it
|
||||
*
|
||||
* @param stdClass $record
|
||||
*/
|
||||
protected static function delete($record) {
|
||||
global $DB;
|
||||
|
||||
core_tag_tag::delete_instances($record->component, $record->itemtype);
|
||||
|
||||
$DB->delete_records('tag_area',
|
||||
array('itemtype' => $record->itemtype,
|
||||
'component' => $record->component));
|
||||
|
||||
// Reset cache.
|
||||
cache::make('core', 'tags')->delete('tag_area');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new tag area
|
||||
*
|
||||
* @param stdClass $record
|
||||
*/
|
||||
protected static function create($record) {
|
||||
global $DB;
|
||||
if (empty($record->tagcollid)) {
|
||||
$record->tagcollid = core_tag_collection::get_default();
|
||||
}
|
||||
$DB->insert_record('tag_area', array('component' => $record->component,
|
||||
'itemtype' => $record->itemtype,
|
||||
'tagcollid' => $record->tagcollid,
|
||||
'callback' => $record->callback,
|
||||
'callbackfile' => $record->callbackfile));
|
||||
|
||||
// Reset cache.
|
||||
cache::make('core', 'tags')->delete('tag_area');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the tag area
|
||||
*
|
||||
* @param stdClass $existing current record from DB table tag_area
|
||||
* @param array|stdClass $data fields that need updating
|
||||
*/
|
||||
public static function update($existing, $data) {
|
||||
global $DB;
|
||||
$data = array_intersect_key((array)$data,
|
||||
array('enabled' => 1, 'tagcollid' => 1,
|
||||
'callback' => 1, 'callbackfile' => 1));
|
||||
foreach ($data as $key => $value) {
|
||||
if ($existing->$key == $value) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
if (!$data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($data['tagcollid'])) {
|
||||
self::move_tags($existing->component, $existing->itemtype, $data['tagcollid']);
|
||||
}
|
||||
|
||||
$data['id'] = $existing->id;
|
||||
$DB->update_record('tag_area', $data);
|
||||
|
||||
// Reset cache.
|
||||
cache::make('core', 'tags')->delete('tag_area');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the database to contain a list of tagged areas for a component.
|
||||
* The list of tagged areas is read from [plugindir]/db/tag.php
|
||||
*
|
||||
* @param string $componentname - The frankenstyle component name.
|
||||
*/
|
||||
public static function reset_definitions_for_component($componentname) {
|
||||
global $DB;
|
||||
$dir = core_component::get_component_directory($componentname);
|
||||
$file = $dir . '/db/tag.php';
|
||||
$tagareas = null;
|
||||
if (file_exists($file)) {
|
||||
require_once($file);
|
||||
}
|
||||
|
||||
list($a, $b) = core_component::normalize_component($componentname);
|
||||
$component = $b ? ($a . '_' . $b) : $a;
|
||||
|
||||
list($existingareas, $existingcolls) = self::get_definitions_for_component($componentname);
|
||||
|
||||
$itemtypes = array();
|
||||
$collections = array();
|
||||
$needcleanup = false;
|
||||
if ($tagareas) {
|
||||
foreach ($tagareas as $tagarea) {
|
||||
$record = (object)$tagarea;
|
||||
if ($component !== 'core' || empty($record->component)) {
|
||||
if (isset($record->component) && $record->component !== $component) {
|
||||
debugging("Item type {$record->itemtype} has illegal component {$record->component}", DEBUG_DEVELOPER);
|
||||
}
|
||||
$record->component = $component;
|
||||
}
|
||||
unset($record->tagcollid);
|
||||
if (!empty($record->collection)) {
|
||||
// Create collection if it does not exist, or update 'searchable' and/or 'customurl' if needed.
|
||||
$key = $record->collection . ':' . $record->component;
|
||||
$collectiondata = array_intersect_key((array)$record,
|
||||
array('component' => 1, 'searchable' => 1, 'customurl' => 1));
|
||||
$collectiondata['name'] = $record->collection;
|
||||
if (!array_key_exists($key, $existingcolls)) {
|
||||
$existingcolls[$key] = core_tag_collection::create($collectiondata);
|
||||
} else {
|
||||
core_tag_collection::update($existingcolls[$key], $collectiondata);
|
||||
}
|
||||
$record->tagcollid = $existingcolls[$key]->id;
|
||||
$collections[$key] = $existingcolls[$key];
|
||||
unset($record->collection);
|
||||
}
|
||||
unset($record->searchable);
|
||||
unset($record->customurl);
|
||||
if (!isset($record->callback)) {
|
||||
$record->callback = null;
|
||||
}
|
||||
if (!isset($record->callbackfile)) {
|
||||
$record->callbackfile = null;
|
||||
}
|
||||
$itemtypes[$record->itemtype . ':' . $record->component] = $record;
|
||||
}
|
||||
}
|
||||
$todeletearea = array_diff_key($existingareas, $itemtypes);
|
||||
$todeletecoll = array_diff_key($existingcolls, $collections);
|
||||
|
||||
// Delete tag areas that are no longer needed.
|
||||
foreach ($todeletearea as $key => $record) {
|
||||
self::delete($record);
|
||||
}
|
||||
|
||||
// Update tag areas if changed.
|
||||
$toupdatearea = array_intersect_key($existingareas, $itemtypes);
|
||||
foreach ($toupdatearea as $key => $tagarea) {
|
||||
if (!isset($itemtypes[$key]->tagcollid)) {
|
||||
foreach ($todeletecoll as $tagcoll) {
|
||||
if ($tagcoll->id == $tagarea->tagcollid) {
|
||||
$itemtypes[$key]->tagcollid = core_tag_collection::get_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
self::update($tagarea, $itemtypes[$key]);
|
||||
}
|
||||
|
||||
// Create new tag areas.
|
||||
$toaddarea = array_diff_key($itemtypes, $existingareas);
|
||||
foreach ($toaddarea as $record) {
|
||||
self::create($record);
|
||||
}
|
||||
|
||||
// Delete tag collections that are no longer needed.
|
||||
foreach ($todeletecoll as $key => $tagcoll) {
|
||||
core_tag_collection::delete($tagcoll);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all tag areas, collections and instances associated with the plugin.
|
||||
*
|
||||
* @param string $pluginname
|
||||
*/
|
||||
public static function uninstall($pluginname) {
|
||||
global $DB;
|
||||
|
||||
list($a, $b) = core_component::normalize_component($pluginname);
|
||||
if (empty($b) || $a === 'core') {
|
||||
throw new coding_exception('Core component can not be uninstalled');
|
||||
}
|
||||
$component = $a . '_' . $b;
|
||||
|
||||
core_tag_tag::delete_instances($component);
|
||||
|
||||
$DB->delete_records('tag_area', array('component' => $component));
|
||||
$DB->delete_records('tag_coll', array('component' => $component));
|
||||
cache::make('core', 'tags')->delete_many(array('tag_area', 'tag_coll'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves existing tags associated with an item type to another tag collection
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $itemtype
|
||||
* @param int $tagcollid
|
||||
*/
|
||||
public static function move_tags($component, $itemtype, $tagcollid) {
|
||||
global $DB;
|
||||
$params = array('itemtype1' => $itemtype, 'component1' => $component,
|
||||
'itemtype2' => $itemtype, 'component2' => $component,
|
||||
'tagcollid1' => $tagcollid, 'tagcollid2' => $tagcollid);
|
||||
|
||||
// Find all collections that need to be cleaned later.
|
||||
$sql = "SELECT DISTINCT t.tagcollid " .
|
||||
"FROM {tag_instance} ti " .
|
||||
"JOIN {tag} t ON t.id = ti.tagid AND t.tagcollid <> :tagcollid1 " .
|
||||
"WHERE ti.itemtype = :itemtype2 AND ti.component = :component2 ";
|
||||
$cleanupcollections = $DB->get_fieldset_sql($sql, $params);
|
||||
|
||||
// Find all tags that are related to the tags being moved and make sure they are present in the target tagcoll.
|
||||
$sql = "SELECT DISTINCT r.name, r.rawname, r.description, r.descriptionformat, ".
|
||||
" r.userid, r.tagtype, r.flag ".
|
||||
"FROM {tag_instance} ti ". // Instances that need moving.
|
||||
"JOIN {tag} t ON t.id = ti.tagid AND t.tagcollid <> :tagcollid1 ". // Tags that need moving.
|
||||
"JOIN {tag_instance} tr ON tr.itemtype = 'tag' and tr.component = 'core' AND tr.itemid = t.id ".
|
||||
"JOIN {tag} r ON r.id = tr.tagid ". // Tags related to the tags that need moving.
|
||||
"LEFT JOIN {tag} re ON re.name = r.name AND re.tagcollid = :tagcollid2 ". // Existing tags in the target tagcoll with the same name as related tags.
|
||||
"WHERE ti.itemtype = :itemtype2 AND ti.component = :component2 ".
|
||||
" AND re.id IS NULL"; // We need related tags that ARE NOT present in the target tagcoll.
|
||||
$result = $DB->get_records_sql($sql, $params);
|
||||
foreach ($result as $tag) {
|
||||
$tag->tagcollid = $tagcollid;
|
||||
$tag->id = $DB->insert_record('tag', $tag);
|
||||
\core\event\tag_created::create_from_tag($tag);
|
||||
}
|
||||
|
||||
// Find all tags that need moving and have related tags, remember their related tags.
|
||||
$sql = "SELECT t.name AS tagname, r.rawname AS relatedtag ".
|
||||
"FROM {tag_instance} ti ". // Instances that need moving.
|
||||
"JOIN {tag} t ON t.id = ti.tagid AND t.tagcollid <> :tagcollid1 ". // Tags that need moving.
|
||||
"JOIN {tag_instance} tr ON t.id = tr.tagid AND tr.itemtype = 'tag' and tr.component = 'core' ".
|
||||
"JOIN {tag} r ON r.id = tr.itemid ". // Tags related to the tags that need moving.
|
||||
"WHERE ti.itemtype = :itemtype2 AND ti.component = :component2 ".
|
||||
"ORDER BY t.id, tr.ordering ";
|
||||
$relatedtags = array();
|
||||
$result = $DB->get_recordset_sql($sql, $params);
|
||||
foreach ($result as $record) {
|
||||
$relatedtags[$record->tagname][] = $record->relatedtag;
|
||||
}
|
||||
$result->close();
|
||||
|
||||
// Find all tags that are used for this itemtype/component and are not present in the target tag collection.
|
||||
$sql = "SELECT DISTINCT t.id, t.name, t.rawname, t.description, t.descriptionformat,
|
||||
t.userid, t.tagtype, t.flag
|
||||
FROM {tag_instance} ti
|
||||
JOIN {tag} t ON t.id = ti.tagid AND t.tagcollid <> :tagcollid1
|
||||
LEFT JOIN {tag} tt ON tt.name = t.name AND tt.tagcollid = :tagcollid2
|
||||
WHERE ti.itemtype = :itemtype2 AND ti.component = :component2
|
||||
AND tt.id IS NULL";
|
||||
$todelete = array();
|
||||
$result = $DB->get_records_sql($sql, $params);
|
||||
foreach ($result as $tag) {
|
||||
$originaltagid = $tag->id;
|
||||
unset($tag->id);
|
||||
$tag->tagcollid = $tagcollid;
|
||||
$tag->id = $DB->insert_record('tag', $tag);
|
||||
\core\event\tag_created::create_from_tag($tag);
|
||||
$DB->execute("UPDATE {tag_instance} SET tagid = ? WHERE tagid = ? AND itemtype = ? AND component = ?",
|
||||
array($tag->id, $originaltagid, $itemtype, $component));
|
||||
}
|
||||
|
||||
// Find all tags that are used for this itemtype/component and are already present in the target tag collection.
|
||||
$sql = "SELECT DISTINCT t.id, tt.id AS targettagid
|
||||
FROM {tag_instance} ti
|
||||
JOIN {tag} t ON t.id = ti.tagid AND t.tagcollid <> :tagcollid1
|
||||
JOIN {tag} tt ON tt.name = t.name AND tt.tagcollid = :tagcollid2
|
||||
WHERE ti.itemtype = :itemtype2 AND ti.component = :component2";
|
||||
$result = $DB->get_records_sql($sql, $params);
|
||||
foreach ($result as $tag) {
|
||||
$DB->execute("UPDATE {tag_instance} SET tagid = ? WHERE tagid = ? AND itemtype = ? AND component = ?",
|
||||
array($tag->targettagid, $tag->id, $itemtype, $component));
|
||||
}
|
||||
|
||||
// Add related tags to the moved tags.
|
||||
if ($relatedtags) {
|
||||
$tags = core_tag_tag::get_by_name_bulk($tagcollid, array_keys($relatedtags));
|
||||
foreach ($tags as $tag) {
|
||||
$tag->add_related_tags($relatedtags[$tag->name]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($cleanupcollections) {
|
||||
core_tag_collection::cleanup_unused_tags($cleanupcollections);
|
||||
}
|
||||
|
||||
// Reset caches.
|
||||
cache::make('core', 'tags')->delete('tag_area');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains class core_tag_areas_table
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Table with the list of available tag areas for "Manage tags" page.
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_tag_areas_table extends html_table {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string|moodle_url $pageurl
|
||||
*/
|
||||
public function __construct($pageurl) {
|
||||
global $OUTPUT;
|
||||
parent::__construct();
|
||||
|
||||
$this->attributes['class'] = 'generaltable tag-areas-table';
|
||||
|
||||
$this->head = array(
|
||||
get_string('tagareaname', 'core_tag'),
|
||||
get_string('component', 'tag'),
|
||||
get_string('tagareaenabled', 'core_tag'),
|
||||
get_string('tagcollection', 'tag'),
|
||||
);
|
||||
|
||||
$this->data = array();
|
||||
$this->rowclasses = array();
|
||||
|
||||
$tagareas = core_tag_area::get_areas();
|
||||
$tagcollections = core_tag_collection::get_collections_menu(true);
|
||||
$tagcollectionsall = core_tag_collection::get_collections_menu();
|
||||
|
||||
foreach ($tagareas as $itemtype => $it) {
|
||||
foreach ($it as $component => $record) {
|
||||
$areaname = core_tag_area::display_name($record->component, $record->itemtype);
|
||||
$baseurl = new moodle_url($pageurl, array('ta' => $record->id, 'sesskey' => sesskey()));
|
||||
if ($record->enabled) {
|
||||
$enableurl = new moodle_url($baseurl, array('action' => 'areadisable'));
|
||||
$enabled = html_writer::link($enableurl, $OUTPUT->pix_icon('i/hide', get_string('disable')));
|
||||
} else {
|
||||
$enableurl = new moodle_url($baseurl, array('action' => 'areaenable'));
|
||||
$enabled = html_writer::link($enableurl, $OUTPUT->pix_icon('i/show', get_string('enable')));
|
||||
}
|
||||
|
||||
if ($record->enabled && empty($record->locked) && count($tagcollections) > 1) {
|
||||
$changecollurl = new moodle_url($baseurl, array('action' => 'areasetcoll'));
|
||||
|
||||
$select = new single_select($changecollurl, 'areacollid', $tagcollections, $record->tagcollid, null);
|
||||
$select->set_label(get_string('changetagcoll', 'core_tag', $areaname), array('class' => 'accesshide'));
|
||||
$collectionselect = $OUTPUT->render($select);
|
||||
} else {
|
||||
$collectionselect = $tagcollectionsall[$record->tagcollid];
|
||||
}
|
||||
$this->data[] = array(
|
||||
$areaname,
|
||||
($record->component === 'core' || preg_match('/^core_/', $record->component)) ?
|
||||
get_string('coresystem') : get_string('pluginname', $record->component),
|
||||
$enabled,
|
||||
$collectionselect
|
||||
);
|
||||
$this->rowclasses[] = $record->enabled ? '' : 'dimmed_text';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class to manage tag collections
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class to manage tag collections
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_tag_collection {
|
||||
|
||||
/** @var string used for function cloud_sort() */
|
||||
public static $cloudsortfield = 'name';
|
||||
|
||||
/**
|
||||
* Returns the list of tag collections defined in the system.
|
||||
*
|
||||
* @param bool $onlysearchable only return collections that can be searched.
|
||||
* @return array array of objects where each object has properties: id, name, isdefault, itemtypes, sortorder
|
||||
*/
|
||||
public static function get_collections($onlysearchable = false) {
|
||||
global $DB;
|
||||
$cache = cache::make('core', 'tags');
|
||||
if (($tagcolls = $cache->get('tag_coll')) === false) {
|
||||
// Retrieve records from DB and create a default one if it is not present.
|
||||
$tagcolls = $DB->get_records('tag_coll', null, 'isdefault DESC, sortorder, id');
|
||||
if (empty($tagcolls)) {
|
||||
// When this method is called for the first time it automatically creates the default tag collection.
|
||||
$DB->insert_record('tag_coll', array('isdefault' => 1, 'sortorder' => 0));
|
||||
$tagcolls = $DB->get_records('tag_coll');
|
||||
} else {
|
||||
// Make sure sortorder is correct.
|
||||
$idx = 0;
|
||||
foreach ($tagcolls as $id => $tagcoll) {
|
||||
if ($tagcoll->sortorder != $idx) {
|
||||
$DB->update_record('tag_coll', array('sortorder' => $idx, 'id' => $id));
|
||||
$tagcolls[$id]->sortorder = $idx;
|
||||
}
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
$cache->set('tag_coll', $tagcolls);
|
||||
}
|
||||
if ($onlysearchable) {
|
||||
$rv = array();
|
||||
foreach ($tagcolls as $id => $tagcoll) {
|
||||
if ($tagcoll->searchable) {
|
||||
$rv[$id] = $tagcoll;
|
||||
}
|
||||
}
|
||||
return $rv;
|
||||
}
|
||||
return $tagcolls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tag collection object
|
||||
*
|
||||
* @param int $tagcollid
|
||||
* @return stdClass
|
||||
*/
|
||||
public static function get_by_id($tagcollid) {
|
||||
$tagcolls = self::get_collections();
|
||||
if (array_key_exists($tagcollid, $tagcolls)) {
|
||||
return $tagcolls[$tagcollid];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of existing tag collections as id=>name
|
||||
*
|
||||
* @param bool $unlockedonly
|
||||
* @param bool $onlysearchable
|
||||
* @param string $selectalllabel
|
||||
* @return array
|
||||
*/
|
||||
public static function get_collections_menu($unlockedonly = false, $onlysearchable = false,
|
||||
$selectalllabel = null) {
|
||||
$tagcolls = self::get_collections($onlysearchable);
|
||||
$options = array();
|
||||
foreach ($tagcolls as $id => $tagcoll) {
|
||||
if (!$unlockedonly || empty($tagcoll->component)) {
|
||||
$options[$id] = self::display_name($tagcoll);
|
||||
}
|
||||
}
|
||||
if (count($options) > 1 && $selectalllabel) {
|
||||
$options = array(0 => $selectalllabel) + $options;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns id of the default tag collection
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function get_default() {
|
||||
$collections = self::get_collections();
|
||||
$keys = array_keys($collections);
|
||||
return $keys[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns formatted name of the tag collection
|
||||
*
|
||||
* @param stdClass $record record from DB table tag_coll
|
||||
* @return string
|
||||
*/
|
||||
public static function display_name($record) {
|
||||
$syscontext = context_system::instance();
|
||||
if (!empty($record->component)) {
|
||||
$identifier = 'tagcollection_' .
|
||||
clean_param($record->name, PARAM_STRINGID);
|
||||
$component = $record->component;
|
||||
if ($component === 'core') {
|
||||
$component = 'tag';
|
||||
}
|
||||
return get_string($identifier, $component);
|
||||
}
|
||||
if (!empty($record->name)) {
|
||||
return format_string($record->name, true, $syscontext);
|
||||
} else if ($record->isdefault) {
|
||||
return get_string('defautltagcoll', 'tag');
|
||||
} else {
|
||||
return $record->id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all tag areas in the given tag collection
|
||||
*
|
||||
* @param int $tagcollid
|
||||
* @return array
|
||||
*/
|
||||
public static function get_areas($tagcollid) {
|
||||
$allitemtypes = core_tag_area::get_areas($tagcollid, true);
|
||||
$itemtypes = array();
|
||||
foreach ($allitemtypes as $itemtype => $it) {
|
||||
foreach ($it as $component => $v) {
|
||||
$itemtypes[$v->id] = $v;
|
||||
}
|
||||
}
|
||||
return $itemtypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of names of areas (enabled only) that are in this collection.
|
||||
*
|
||||
* @param int $tagcollid
|
||||
* @return array
|
||||
*/
|
||||
public static function get_areas_names($tagcollid) {
|
||||
$allitemtypes = core_tag_area::get_areas($tagcollid, true);
|
||||
$itemtypes = array();
|
||||
foreach ($allitemtypes as $itemtype => $it) {
|
||||
foreach ($it as $component => $v) {
|
||||
$itemtypes[] = core_tag_area::display_name($component, $itemtype);
|
||||
}
|
||||
}
|
||||
return $itemtypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tag collection
|
||||
*
|
||||
* @param stdClass $data data from form core_tag_collection_form
|
||||
* @return int|false id of created tag collection or false if failed
|
||||
*/
|
||||
public static function create($data) {
|
||||
global $DB;
|
||||
$data = (object)$data;
|
||||
$tagcolls = self::get_collections();
|
||||
$tagcoll = (object)array(
|
||||
'name' => $data->name,
|
||||
'isdefault' => 0,
|
||||
'component' => !empty($data->component) ? $data->component : null,
|
||||
'sortorder' => count($tagcolls),
|
||||
'searchable' => isset($data->searchable) ? (int)(bool)$data->searchable : 1,
|
||||
'customurl' => !empty($data->customurl) ? $data->customurl : null,
|
||||
);
|
||||
$tagcoll->id = $DB->insert_record('tag_coll', $tagcoll);
|
||||
|
||||
// Reset cache.
|
||||
cache::make('core', 'tags')->delete('tag_coll');
|
||||
|
||||
\core\event\tag_collection_created::create_from_record($tagcoll)->trigger();
|
||||
return $tagcoll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the tag collection information
|
||||
*
|
||||
* @param stdClass $tagcoll existing record in DB table tag_coll
|
||||
* @param stdClass $data data from form core_tag_collection_form
|
||||
* @return bool wether the record was updated
|
||||
*/
|
||||
public static function update($tagcoll, $data) {
|
||||
global $DB;
|
||||
$defaulttagcollid = self::get_default();
|
||||
$allowedfields = array('name', 'searchable', 'customurl');
|
||||
if ($tagcoll->id == $defaulttagcollid) {
|
||||
$allowedfields = array('name');
|
||||
}
|
||||
|
||||
$updatedata = array();
|
||||
$data = (array)$data;
|
||||
foreach ($allowedfields as $key) {
|
||||
if (array_key_exists($key, $data) && $data[$key] !== $tagcoll->$key) {
|
||||
$updatedata[$key] = $data[$key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$updatedata) {
|
||||
// Nothing to update.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($updatedata['searchable'])) {
|
||||
$updatedata['searchable'] = (int)(bool)$updatedata['searchable'];
|
||||
}
|
||||
foreach ($updatedata as $key => $value) {
|
||||
$tagcoll->$key = $value;
|
||||
}
|
||||
$updatedata['id'] = $tagcoll->id;
|
||||
$DB->update_record('tag_coll', $updatedata);
|
||||
|
||||
// Reset cache.
|
||||
cache::make('core', 'tags')->delete('tag_coll');
|
||||
|
||||
\core\event\tag_collection_updated::create_from_record($tagcoll)->trigger();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a custom tag collection
|
||||
*
|
||||
* @param stdClass $tagcoll existing record in DB table tag_coll
|
||||
* @return bool wether the tag collection was deleted
|
||||
*/
|
||||
public static function delete($tagcoll) {
|
||||
global $DB, $CFG;
|
||||
|
||||
$defaulttagcollid = self::get_default();
|
||||
if ($tagcoll->id == $defaulttagcollid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move all tags from this tag collection to the default one.
|
||||
$allitemtypes = core_tag_area::get_areas($tagcoll->id);
|
||||
foreach ($allitemtypes as $it) {
|
||||
foreach ($it as $v) {
|
||||
core_tag_area::update($v, array('tagcollid' => $defaulttagcollid));
|
||||
}
|
||||
}
|
||||
|
||||
// Delete tags from this tag_coll.
|
||||
core_tag_tag::delete_tags($DB->get_fieldset_select('tag', 'id', 'tagcollid = ?', array($tagcoll->id)));
|
||||
|
||||
// Delete the tag collection.
|
||||
$DB->delete_records('tag_coll', array('id' => $tagcoll->id));
|
||||
|
||||
// Reset cache.
|
||||
cache::make('core', 'tags')->delete('tag_coll');
|
||||
|
||||
\core\event\tag_collection_deleted::create_from_record($tagcoll)->trigger();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the tag collection in the list one position up or down
|
||||
*
|
||||
* @param stdClass $tagcoll existing record in DB table tag_coll
|
||||
* @param int $direction move direction: +1 or -1
|
||||
* @return bool
|
||||
*/
|
||||
public static function change_sortorder($tagcoll, $direction) {
|
||||
global $DB;
|
||||
if ($direction != -1 && $direction != 1) {
|
||||
throw coding_exception('Second argument in tag_coll_change_sortorder() can be only 1 or -1');
|
||||
}
|
||||
$tagcolls = self::get_collections();
|
||||
$keys = array_keys($tagcolls);
|
||||
$idx = array_search($tagcoll->id, $keys);
|
||||
if ($idx === false || $idx == 0 || $idx + $direction < 1 || $idx + $direction >= count($tagcolls)) {
|
||||
return false;
|
||||
}
|
||||
$otherid = $keys[$idx + $direction];
|
||||
$DB->update_record('tag_coll', array('id' => $tagcoll->id, 'sortorder' => $idx + $direction));
|
||||
$DB->update_record('tag_coll', array('id' => $otherid, 'sortorder' => $idx));
|
||||
// Reset cache.
|
||||
cache::make('core', 'tags')->delete('tag_coll');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permanently deletes all non-official tags that no longer have any instances pointing to them
|
||||
*
|
||||
* @param array $collections optional list of tag collections ids to cleanup
|
||||
*/
|
||||
public static function cleanup_unused_tags($collections = null) {
|
||||
global $DB, $CFG;
|
||||
|
||||
$params = array();
|
||||
$sql = "SELECT tg.id FROM {tag} tg LEFT OUTER JOIN {tag_instance} ti ON ti.tagid = tg.id
|
||||
WHERE ti.id IS NULL AND tg.tagtype = 'default'";
|
||||
if ($collections) {
|
||||
list($sqlcoll, $params) = $DB->get_in_or_equal($collections);
|
||||
$sql .= " AND tg.tagcollid " . $sqlcoll;
|
||||
}
|
||||
if ($unusedtags = $DB->get_fieldset_sql($sql, $params)) {
|
||||
core_tag_tag::delete_tags($unusedtags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of tags with number of items tagged
|
||||
*
|
||||
* @param int $tagcollid
|
||||
* @param string $tagtype possible values 'official', 'default' or empty for any tag type
|
||||
* @param int $limit maximum number of tags to retrieve, tags are sorted by the instance count
|
||||
* descending here regardless of $sort parameter
|
||||
* @param string $sort sort order for display, default 'name' - tags will be sorted after they are retrieved
|
||||
* @param string $search search string
|
||||
* @param int $fromctx context id where this tag cloud is displayed
|
||||
* @param int $ctx only retrieve tag instances in this context
|
||||
* @param int $rec retrieve tag instances in the $ctx context and it's children (default 1)
|
||||
* @return \core_tag\output\tagcloud
|
||||
*/
|
||||
public static function get_tag_cloud($tagcollid, $tagtype = '', $limit = 150, $sort = 'name',
|
||||
$search = '', $fromctx = 0, $ctx = 0, $rec = 1) {
|
||||
global $DB;
|
||||
|
||||
$fromclause = 'FROM {tag_instance} ti JOIN {tag} tg ON tg.id = ti.tagid';
|
||||
$whereclause = 'WHERE ti.itemtype <> \'tag\'';
|
||||
list($sql, $params) = $DB->get_in_or_equal($tagcollid ? array($tagcollid) :
|
||||
array_keys(self::get_collections(true)));
|
||||
$whereclause .= ' AND tg.tagcollid ' . $sql;
|
||||
if (!empty($tagtype)) {
|
||||
$whereclause .= ' AND tg.tagtype = ?';
|
||||
$params[] = $tagtype;
|
||||
}
|
||||
$context = $ctx ? context::instance_by_id($ctx) : context_system::instance();
|
||||
if ($rec && $context->contextlevel != CONTEXT_SYSTEM) {
|
||||
$fromclause .= ' JOIN {context} ctx ON ctx.id = ti.contextid ';
|
||||
$whereclause .= ' AND ctx.path LIKE ?';
|
||||
$params[] = $context->path . '%';
|
||||
} else if (!$rec) {
|
||||
$whereclause .= ' AND ti.contextid = ?';
|
||||
$params[] = $context->id;
|
||||
}
|
||||
if (strval($search) !== '') {
|
||||
$whereclause .= ' AND tg.name LIKE ?';
|
||||
$params[] = '%' . core_text::strtolower($search) . '%';
|
||||
}
|
||||
$tagsincloud = $DB->get_records_sql(
|
||||
"SELECT tg.id, tg.rawname, tg.name, tg.tagtype, COUNT(ti.id) AS count, tg.flag, tg.tagcollid
|
||||
$fromclause
|
||||
$whereclause
|
||||
GROUP BY tg.id, tg.rawname, tg.name, tg.flag, tg.tagtype, tg.tagcollid
|
||||
ORDER BY count DESC, tg.name ASC",
|
||||
$params, 0, $limit);
|
||||
|
||||
$tagscount = count($tagsincloud);
|
||||
if ($tagscount == $limit) {
|
||||
$tagscount = $DB->get_field_sql("SELECT COUNT(DISTINCT tg.id) $fromclause $whereclause", $params);
|
||||
}
|
||||
|
||||
self::$cloudsortfield = $sort;
|
||||
usort($tagsincloud, "self::cloud_sort");
|
||||
|
||||
return new core_tag\output\tagcloud($tagsincloud, $tagscount, $fromctx, $ctx, $rec);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used to sort the tags in the cloud.
|
||||
*
|
||||
* @param string $a Tag name to compare against $b
|
||||
* @param string $b Tag name to compare against $a
|
||||
* @return int The result of the comparison/validation 1, 0 or -1
|
||||
*/
|
||||
public static function cloud_sort($a, $b) {
|
||||
$tagsort = self::$cloudsortfield ?: 'name';
|
||||
|
||||
if (is_numeric($a->$tagsort)) {
|
||||
return ($a->$tagsort == $b->$tagsort) ? 0 : ($a->$tagsort > $b->$tagsort) ? 1 : -1;
|
||||
} else if (is_string($a->$tagsort)) {
|
||||
return strcmp($a->$tagsort, $b->$tagsort);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains class core_tag_collection_form
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/formslib.php');
|
||||
|
||||
/**
|
||||
* Form for editing tag collection
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_tag_collection_form extends moodleform {
|
||||
|
||||
/**
|
||||
* Form definition
|
||||
*/
|
||||
public function definition() {
|
||||
$data = fullclone($this->_customdata);
|
||||
if (isset($data->id)) {
|
||||
$data->tc = $data->id;
|
||||
$data->action = 'colledit';
|
||||
} else {
|
||||
$data = new stdClass();
|
||||
$data->action = 'colladd';
|
||||
$data->isdefault = false;
|
||||
}
|
||||
|
||||
$mform = $this->_form;
|
||||
$mform->addElement('hidden', 'tc');
|
||||
$mform->setType('tc', PARAM_INT);
|
||||
$mform->addElement('hidden', 'action');
|
||||
$mform->setType('action', PARAM_ALPHA);
|
||||
|
||||
$mform->addElement('text', 'name', get_string('name'));
|
||||
$mform->setType('name', PARAM_NOTAGS);
|
||||
$mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
|
||||
if (empty($data->isdefault)) {
|
||||
$mform->addRule('name', get_string('required'), 'required', null, 'client');
|
||||
} else {
|
||||
$mform->addElement('static', 'collnameexplained', '', get_string('collnameexplained', 'tag',
|
||||
get_string('defautltagcoll', 'tag')));
|
||||
}
|
||||
|
||||
$mform->addElement('advcheckbox', 'searchable', get_string('searchable', 'tag'));
|
||||
$mform->addHelpButton('searchable', 'searchable', 'tag');
|
||||
$mform->setDefault('searchable', 1);
|
||||
if (!empty($data->isdefault)) {
|
||||
$mform->freeze('searchable');
|
||||
}
|
||||
|
||||
$this->add_action_buttons();
|
||||
|
||||
$this->set_data($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains class core_tag_collections_table
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Table with the list of tag collections for "Manage tags" page.
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_tag_collections_table extends html_table {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param string|moodle_url $pageurl
|
||||
*/
|
||||
public function __construct($pageurl) {
|
||||
global $OUTPUT;
|
||||
parent::__construct();
|
||||
|
||||
$this->attributes['class'] = 'generaltable tag-collections-table';
|
||||
|
||||
$this->head = array(
|
||||
get_string('name'),
|
||||
get_string('component', 'tag'),
|
||||
get_string('tagareas', 'tag'),
|
||||
get_string('searchable', 'tag') . $OUTPUT->help_icon('searchable', 'tag'),
|
||||
''
|
||||
);
|
||||
|
||||
$this->data = array();
|
||||
|
||||
$tagcolls = core_tag_collection::get_collections();
|
||||
$idx = 0;
|
||||
foreach ($tagcolls as $tagcoll) {
|
||||
$actions = '';
|
||||
$name = core_tag_collection::display_name($tagcoll);
|
||||
$url = new moodle_url($pageurl, array('sesskey' => sesskey(), 'tc' => $tagcoll->id));
|
||||
if (!$tagcoll->isdefault) {
|
||||
// Move up.
|
||||
if ($idx > 1) {
|
||||
$url->param('action', 'collmoveup');
|
||||
$actions .= $OUTPUT->action_icon($url, new pix_icon('t/up', get_string('moveup')));
|
||||
}
|
||||
// Move down.
|
||||
if ($idx < count($tagcolls) - 1) {
|
||||
$url->param('action', 'collmovedown');
|
||||
$actions .= $OUTPUT->action_icon($url, new pix_icon('t/down', get_string('movedown')));
|
||||
}
|
||||
}
|
||||
if (empty($tagcoll->component)) {
|
||||
// Edit.
|
||||
$url->param('action', 'colledit');
|
||||
$actions .= $OUTPUT->action_icon($url, new pix_icon('t/edit', get_string('edittagcoll', 'tag', $name)));
|
||||
}
|
||||
if (!$tagcoll->isdefault && empty($tagcoll->component)) {
|
||||
// Delete.
|
||||
$url->param('action', 'colldelete');
|
||||
$actions .= $OUTPUT->action_icon($url, new pix_icon('t/delete', get_string('delete')));
|
||||
}
|
||||
$manageurl = new moodle_url('/tag/manage.php', array('tc' => $tagcoll->id));
|
||||
$component = '';
|
||||
if ($tagcoll->component) {
|
||||
$component = ($tagcoll->component === 'core' || preg_match('/^core_/', $tagcoll->component)) ?
|
||||
get_string('coresystem') : get_string('pluginname', $tagcoll->component);
|
||||
}
|
||||
$this->data[] = array(
|
||||
html_writer::link($manageurl, $name),
|
||||
$component,
|
||||
join(', ', core_tag_collection::get_areas_names($tagcoll->id)),
|
||||
$tagcoll->searchable ? get_string('yes') : '-',
|
||||
$actions);
|
||||
$idx++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+91
-22
@@ -66,7 +66,6 @@ class core_tag_external extends external_api {
|
||||
*/
|
||||
public static function update_tags($tags) {
|
||||
global $CFG, $PAGE, $DB;
|
||||
require_once($CFG->dirroot.'/tag/lib.php');
|
||||
|
||||
// Validate and normalize parameters.
|
||||
$tags = self::validate_parameters(self::update_tags_parameters(), array('tags' => $tags));
|
||||
@@ -87,8 +86,6 @@ class core_tag_external extends external_api {
|
||||
$tag['rawname'] = clean_param($tag['rawname'], PARAM_TAG);
|
||||
if (empty($tag['rawname'])) {
|
||||
unset($tag['rawname']);
|
||||
} else {
|
||||
$tag['name'] = core_text::strtolower($tag['rawname']);
|
||||
}
|
||||
}
|
||||
if (!$canmanage) {
|
||||
@@ -109,7 +106,7 @@ class core_tag_external extends external_api {
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (!$tagobject = $DB->get_record('tag', array('id' => $tag['id']))) {
|
||||
if (!$tagobject = core_tag_tag::get($tag['id'], '*')) {
|
||||
$warnings[] = array(
|
||||
'item' => $tag['id'],
|
||||
'warningcode' => 'tagnotfound',
|
||||
@@ -118,7 +115,7 @@ class core_tag_external extends external_api {
|
||||
continue;
|
||||
}
|
||||
// First check if new tag name is allowed.
|
||||
if (!empty($tag['name']) && ($existing = $DB->get_record('tag', array('name' => $tag['name']), 'id'))) {
|
||||
if (!empty($tag['rawname']) && ($existing = core_tag_tag::get_by_name($tagobject->tagcollid, $tag['rawname']))) {
|
||||
if ($existing->id != $tag['id']) {
|
||||
$warnings[] = array(
|
||||
'item' => $tag['id'],
|
||||
@@ -132,23 +129,18 @@ class core_tag_external extends external_api {
|
||||
$tag['tagtype'] = $tag['official'] ? 'official' : 'default';
|
||||
unset($tag['official']);
|
||||
}
|
||||
$tag['timemodified'] = time();
|
||||
$DB->update_record('tag', $tag);
|
||||
|
||||
foreach ($tag as $key => $value) {
|
||||
$tagobject->$key = $value;
|
||||
if (isset($tag['flag'])) {
|
||||
if ($tag['flag']) {
|
||||
$tagobject->flag();
|
||||
} else {
|
||||
$tagobject->reset_flag();
|
||||
}
|
||||
unset($tag['flag']);
|
||||
}
|
||||
unset($tag['id']);
|
||||
if (count($tag)) {
|
||||
$tagobject->update($tag);
|
||||
}
|
||||
|
||||
$event = \core\event\tag_updated::create(array(
|
||||
'objectid' => $tagobject->id,
|
||||
'relateduserid' => $tagobject->userid,
|
||||
'context' => context_system::instance(),
|
||||
'other' => array(
|
||||
'name' => $tagobject->name,
|
||||
'rawname' => $tagobject->rawname
|
||||
)
|
||||
));
|
||||
$event->trigger();
|
||||
}
|
||||
return array('warnings' => $warnings);
|
||||
}
|
||||
@@ -192,7 +184,6 @@ class core_tag_external extends external_api {
|
||||
*/
|
||||
public static function get_tags($tags) {
|
||||
global $CFG, $PAGE, $DB;
|
||||
require_once($CFG->dirroot.'/tag/lib.php');
|
||||
|
||||
// Validate and normalize parameters.
|
||||
$tags = self::validate_parameters(self::get_tags_parameters(), array('tags' => $tags));
|
||||
@@ -248,6 +239,7 @@ class core_tag_external extends external_api {
|
||||
'tags' => new external_multiple_structure( new external_single_structure(
|
||||
array(
|
||||
'id' => new external_value(PARAM_INT, 'tag id'),
|
||||
'tagcollid' => new external_value(PARAM_INT, 'tag collection id'),
|
||||
'name' => new external_value(PARAM_TAG, 'name'),
|
||||
'rawname' => new external_value(PARAM_RAW, 'tag raw name (may contain capital letters)'),
|
||||
'description' => new external_value(PARAM_RAW, 'tag description'),
|
||||
@@ -263,4 +255,81 @@ class core_tag_external extends external_api {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for function get_tagindex()
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function get_tagindex_parameters() {
|
||||
return new external_function_parameters(
|
||||
array(
|
||||
'tagindex' => new external_single_structure(array(
|
||||
'tag' => new external_value(PARAM_TAG, 'tag name'),
|
||||
'tc' => new external_value(PARAM_INT, 'tag collection id'),
|
||||
'ta' => new external_value(PARAM_INT, 'tag area id'),
|
||||
'excl' => new external_value(PARAM_BOOL, 'exlusive mode for this tag area', VALUE_OPTIONAL, 0),
|
||||
'from' => new external_value(PARAM_INT, 'context id where the link was displayed', VALUE_OPTIONAL, 0),
|
||||
'ctx' => new external_value(PARAM_INT, 'context id where to search for items', VALUE_OPTIONAL, 0),
|
||||
'rec' => new external_value(PARAM_INT, 'search in the context recursive', VALUE_OPTIONAL, 1),
|
||||
'page' => new external_value(PARAM_INT, 'page number (0-based)', VALUE_OPTIONAL, 0),
|
||||
), 'parameters')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tags by their ids
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public static function get_tagindex($params) {
|
||||
global $PAGE;
|
||||
// Validate and normalize parameters.
|
||||
$tagindex = self::validate_parameters(
|
||||
self::get_tagindex_parameters(), array('tagindex' => $params));
|
||||
$params = $tagindex['tagindex'] + array(
|
||||
'excl' => 0,
|
||||
'from' => 0,
|
||||
'ctx' => 0,
|
||||
'rec' => 1,
|
||||
'page' => 0
|
||||
);
|
||||
|
||||
// Login to the course / module if applicable.
|
||||
$context = $params['ctx'] ? context::instance_by_id($params['ctx']) : context_system::instance();
|
||||
require_login(null, false, null, false, true);
|
||||
self::validate_context($context);
|
||||
|
||||
$tag = core_tag_tag::get_by_name($params['tc'], $params['tag'], '*', MUST_EXIST);
|
||||
$tagareas = core_tag_collection::get_areas($params['tc']);
|
||||
$tagindex = $tag->get_tag_index($tagareas[$params['ta']], $params['excl'], $params['from'],
|
||||
$params['ctx'], $params['rec'], $params['page']);
|
||||
$renderer = $PAGE->get_renderer('core');
|
||||
return $tagindex->export_for_template($renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return structure for get_tag()
|
||||
*
|
||||
* @return external_description
|
||||
*/
|
||||
public static function get_tagindex_returns() {
|
||||
return new external_single_structure(
|
||||
array(
|
||||
'tagid' => new external_value(PARAM_INT, 'tag id'),
|
||||
'ta' => new external_value(PARAM_INT, 'tag area id'),
|
||||
'component' => new external_value(PARAM_COMPONENT, 'component'),
|
||||
'itemtype' => new external_value(PARAM_NOTAGS, 'itemtype'),
|
||||
'nextpageurl' => new external_value(PARAM_URL, 'URL for the next page', VALUE_OPTIONAL),
|
||||
'prevpageurl' => new external_value(PARAM_URL, 'URL for the next page', VALUE_OPTIONAL),
|
||||
'exclusiveurl' => new external_value(PARAM_URL, 'URL for exclusive link', VALUE_OPTIONAL),
|
||||
'exclusivetext' => new external_value(PARAM_TEXT, 'text for exclusive link', VALUE_OPTIONAL),
|
||||
'title' => new external_value(PARAM_RAW, 'title'),
|
||||
'content' => new external_value(PARAM_RAW, 'title'),
|
||||
'hascontent' => new external_value(PARAM_INT, 'whether the content is present'),
|
||||
'anchor' => new external_value(PARAM_TEXT, 'name of anchor', VALUE_OPTIONAL),
|
||||
), 'tag index'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,16 +38,24 @@ class core_tag_manage_table extends table_sql {
|
||||
/** @var int stores the total number of found tags */
|
||||
public $totalcount = null;
|
||||
|
||||
/** @var int */
|
||||
protected $tagcollid;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param int $tagcollid
|
||||
*/
|
||||
public function __construct() {
|
||||
public function __construct($tagcollid) {
|
||||
global $USER, $CFG, $PAGE;
|
||||
parent::__construct('tag-management-list-'.$USER->id);
|
||||
|
||||
$this->tagcollid = $tagcollid;
|
||||
|
||||
$perpage = optional_param('perpage', DEFAULT_PAGE_SIZE, PARAM_INT);
|
||||
$page = optional_param('page', 0, PARAM_INT);
|
||||
$baseurl = new moodle_url('/tag/manage.php', array('perpage' => $perpage, 'page' => $page));
|
||||
$baseurl = new moodle_url('/tag/manage.php', array('tc' => $tagcollid,
|
||||
'perpage' => $perpage, 'page' => $page));
|
||||
|
||||
$tablecolumns = array('select', 'name', 'fullname', 'count', 'flag', 'timemodified', 'tagtype', 'controls');
|
||||
$tableheaders = array(get_string('select', 'tag'),
|
||||
@@ -80,8 +88,10 @@ class core_tag_manage_table extends table_sql {
|
||||
$this->set_attribute('id', 'tag-management-list');
|
||||
$this->set_attribute('class', 'admintable generaltable tag-management-table');
|
||||
|
||||
$totalcount = "SELECT COUNT(id) FROM {tag}";
|
||||
$params = array();
|
||||
$totalcount = "SELECT COUNT(id)
|
||||
FROM {tag}
|
||||
WHERE tagcollid = :tagcollid";
|
||||
$params = array('tagcollid' => $this->tagcollid);
|
||||
|
||||
$this->set_count_sql($totalcount, $params);
|
||||
|
||||
@@ -135,13 +145,13 @@ class core_tag_manage_table extends table_sql {
|
||||
$sql = "
|
||||
SELECT tg.id, tg.name, tg.rawname, tg.tagtype, tg.flag, tg.timemodified,
|
||||
u.id AS owner, $allusernames,
|
||||
COUNT(ti.id) AS count
|
||||
COUNT(ti.id) AS count, tg.tagcollid
|
||||
FROM {tag} tg
|
||||
LEFT JOIN {tag_instance} ti ON ti.tagid = tg.id
|
||||
LEFT JOIN {user} u ON u.id = tg.userid
|
||||
WHERE 1 = 1 $where
|
||||
WHERE tagcollid = :tagcollid $where
|
||||
GROUP BY tg.id, tg.name, tg.rawname, tg.tagtype, tg.flag, tg.timemodified,
|
||||
u.id, $allusernames
|
||||
u.id, $allusernames, tg.tagcollid
|
||||
ORDER BY $sort";
|
||||
|
||||
if (!$this->is_downloading()) {
|
||||
|
||||
@@ -39,15 +39,19 @@ use moodle_url;
|
||||
*/
|
||||
class tag implements renderable, templatable {
|
||||
|
||||
/** @var stdClass */
|
||||
/** @var \core_tag_tag|stdClass */
|
||||
protected $record;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param stdClass $tag
|
||||
* @param \core_tag_tag|stdClass $tag
|
||||
*/
|
||||
public function __construct($tag) {
|
||||
if ($tag instanceof \core_tag_tag) {
|
||||
$this->record = $tag;
|
||||
return;
|
||||
}
|
||||
$tag = (array)$tag +
|
||||
array(
|
||||
'name' => '',
|
||||
@@ -56,7 +60,8 @@ class tag implements renderable, templatable {
|
||||
'descriptionformat' => FORMAT_HTML,
|
||||
'flag' => 0,
|
||||
'tagtype' => 'default',
|
||||
'id' => 0
|
||||
'id' => 0,
|
||||
'tagcollid' => 0,
|
||||
);
|
||||
$this->record = (object)$tag;
|
||||
}
|
||||
@@ -73,6 +78,7 @@ class tag implements renderable, templatable {
|
||||
|
||||
$r = new stdClass();
|
||||
$r->id = (int)$this->record->id;
|
||||
$r->tagcollid = clean_param($this->record->tagcollid, PARAM_INT);
|
||||
$r->rawname = clean_param($this->record->rawname, PARAM_TAG);
|
||||
$r->name = clean_param($this->record->name, PARAM_TAG);
|
||||
$format = clean_param($this->record->descriptionformat, PARAM_INT);
|
||||
@@ -85,7 +91,7 @@ class tag implements renderable, templatable {
|
||||
$r->official = ($this->record->tagtype === 'official') ? 1 : 0;
|
||||
}
|
||||
|
||||
$url = new moodle_url('/tag/index.php', array('id' => $this->record->id));
|
||||
$url = \core_tag_tag::make_url($r->tagcollid, $r->rawname);
|
||||
$r->viewurl = $url->out(false);
|
||||
|
||||
$manageurl = new moodle_url('/tag/manage.php', array('sesskey' => sesskey(),
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains class core_tag\output\tagindex
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_tag\output;
|
||||
|
||||
use renderable;
|
||||
use templatable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use moodle_url;
|
||||
use core_tag_tag;
|
||||
|
||||
/**
|
||||
* Class to display a tag cloud - set of tags where each has a weight.
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tagcloud implements templatable {
|
||||
|
||||
/** @var array */
|
||||
protected $tagset;
|
||||
|
||||
/** @var int */
|
||||
protected $totalcount;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $tagset array of core_tag or stdClass elements, each of them must have attributes:
|
||||
* name, rawname, tagcollid
|
||||
* preferrably also have attributes:
|
||||
* tagtype, count, flag
|
||||
* @param int $totalcount total count of tags (for example to indicate that there are more tags than the count of tagset)
|
||||
* leave 0 if count of tagset is the actual count of tags
|
||||
* @param int $fromctx context id where this tag cloud is displayed
|
||||
* @param int $ctx context id for tag view link
|
||||
* @param int $rec recursive argument for tag view link
|
||||
*/
|
||||
public function __construct($tagset, $totalcount = 0, $fromctx = 0, $ctx = 0, $rec = 1) {
|
||||
$canmanagetags = has_capability('moodle/tag:manage', \context_system::instance());
|
||||
|
||||
$maxcount = 1;
|
||||
foreach ($tagset as $tag) {
|
||||
if (isset($tag->count) && $tag->count > $maxcount) {
|
||||
$maxcount = $tag->count;
|
||||
}
|
||||
}
|
||||
|
||||
$this->tagset = array();
|
||||
foreach ($tagset as $idx => $tag) {
|
||||
$this->tagset[$idx] = new stdClass();
|
||||
|
||||
$this->tagset[$idx]->name = core_tag_tag::make_display_name($tag, false);
|
||||
|
||||
if ($canmanagetags && !empty($tag->flag)) {
|
||||
$this->tagset[$idx]->flag = 1;
|
||||
}
|
||||
|
||||
$viewurl = core_tag_tag::make_url($tag->tagcollid, $tag->rawname, 0, $fromctx, $ctx, $rec);
|
||||
$this->tagset[$idx]->viewurl = $viewurl->out(false);
|
||||
|
||||
if (!empty($tag->tagtype)) {
|
||||
$this->tagset[$idx]->tagtype = $tag->tagtype;
|
||||
}
|
||||
|
||||
if (!empty($tag->count)) {
|
||||
$this->tagset[$idx]->count = $tag->count;
|
||||
$this->tagset[$idx]->size = (int)($tag->count / $maxcount * 20);
|
||||
}
|
||||
}
|
||||
|
||||
$this->totalcount = $totalcount ? $totalcount : count($this->tagset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of tags in the cloud
|
||||
* @return int
|
||||
*/
|
||||
public function get_count() {
|
||||
return count($this->tagset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
$cnt = count($this->tagset);
|
||||
return (object)array(
|
||||
'tags' => $this->tagset,
|
||||
'tagscount' => $cnt,
|
||||
'totalcount' => $this->totalcount,
|
||||
'overflow' => ($this->totalcount > $cnt) ? 1 : 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains class core_tag\output\tagfeed
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_tag\output;
|
||||
|
||||
use templatable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class to display feed of tagged items
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tagfeed implements templatable {
|
||||
|
||||
/** @var array */
|
||||
protected $items;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Usually the most convenient way is to call constructor without arguments and
|
||||
* add items later using add() method.
|
||||
*
|
||||
* @param array $items
|
||||
*/
|
||||
public function __construct($items = array()) {
|
||||
$this->items = array();
|
||||
if ($items) {
|
||||
foreach ($items as $item) {
|
||||
$item = (array)$item + array('img' => '', 'heading' => '', 'details' => '');
|
||||
$this->add($item['img'], $item['heading'], $item['details']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one item to the tagfeed
|
||||
*
|
||||
* @param string $img HTML code representing image (or image wrapped in a link), note that
|
||||
* core_tag/tagfeed template expects image to be 35x35 px
|
||||
* @param string $heading HTML for item heading
|
||||
* @param string $details HTML for item details (keep short)
|
||||
*/
|
||||
public function add($img, $heading, $details = '') {
|
||||
$this->items[] = array('img' => $img, 'heading' => $heading, 'details' => $details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
return array('items' => $this->items);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains class core_tag\output\tagindex
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_tag\output;
|
||||
|
||||
use renderable;
|
||||
use templatable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use moodle_url;
|
||||
use core_tag_tag;
|
||||
|
||||
/**
|
||||
* Class to display items tagged with a specific tag
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tagindex implements templatable {
|
||||
|
||||
/** @var core_tag_tag|stdClass */
|
||||
protected $tag;
|
||||
|
||||
/** @var stdClass */
|
||||
protected $tagarea;
|
||||
|
||||
/** @var stdClass */
|
||||
protected $record;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param core_tag_tag|stdClass $tag
|
||||
* @param string $component
|
||||
* @param string $itemtype
|
||||
* @param string $content
|
||||
* @param bool $exclusivemode
|
||||
* @param int $fromctx context id where the link was displayed, may be used by callbacks
|
||||
* to display items in the same context first
|
||||
* @param int $ctx context id where we need to search for items
|
||||
* @param int $rec search items in sub contexts as well
|
||||
* @param int $page
|
||||
* @param bool $totalpages
|
||||
*/
|
||||
public function __construct($tag, $component, $itemtype, $content,
|
||||
$exclusivemode = false, $fromctx = 0, $ctx = 0, $rec = 1, $page = 0, $totalpages = 1) {
|
||||
$this->record = new stdClass();
|
||||
$this->tag = $tag;
|
||||
|
||||
$tagareas = \core_tag_area::get_areas();
|
||||
if (!isset($tagareas[$itemtype][$component])) {
|
||||
throw new \coding_exception('Tag area for component '.$component.' and itemtype '.$itemtype.' is not defined');
|
||||
}
|
||||
$this->tagarea = $tagareas[$itemtype][$component];
|
||||
$this->record->tagid = $tag->id;
|
||||
$this->record->ta = $this->tagarea->id;
|
||||
$this->record->itemtype = $itemtype;
|
||||
$this->record->component = $component;
|
||||
|
||||
$a = (object)array(
|
||||
'tagarea' => \core_tag_area::display_name($component, $itemtype),
|
||||
'tag' => \core_tag_tag::make_display_name($tag)
|
||||
);
|
||||
if ($exclusivemode) {
|
||||
$this->record->title = get_string('itemstaggedwith', 'tag', $a);
|
||||
} else {
|
||||
$this->record->title = (string)$a->tagarea;
|
||||
}
|
||||
$this->record->content = $content;
|
||||
|
||||
$this->record->nextpageurl = null;
|
||||
$this->record->prevpageurl = null;
|
||||
$this->record->exclusiveurl = null;
|
||||
|
||||
$url = core_tag_tag::make_url($tag->tagcollid, $tag->rawname, $exclusivemode, $fromctx, $ctx, $rec);
|
||||
$urlparams = array('ta' => $this->tagarea->id);
|
||||
if ($totalpages > $page + 1) {
|
||||
$this->record->nextpageurl = new moodle_url($url, $urlparams + array('page' => $page + 1));
|
||||
}
|
||||
if ($page > 0) {
|
||||
$this->record->prevpageurl = new moodle_url($url, $urlparams + array('page' => $page - 1));
|
||||
}
|
||||
if (!$exclusivemode && ($totalpages > 1 || $page)) {
|
||||
$this->record->exclusiveurl = new moodle_url($url, $urlparams + array('excl' => 1));
|
||||
}
|
||||
$this->record->exclusivetext = get_string('exclusivemode', 'tag', $a);
|
||||
$this->record->hascontent = ($totalpages > 1 || $page || $content);
|
||||
$this->record->anchor = $component . '_' . $itemtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic setter
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set($name, $value) {
|
||||
$this->record->$name = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic getter
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name) {
|
||||
return $this->record->$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic isset
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name) {
|
||||
return isset($this->record->$name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
if ($this->record->nextpageurl && $this->record->nextpageurl instanceof moodle_url) {
|
||||
$this->record->nextpageurl = $this->record->nextpageurl->out(false);
|
||||
}
|
||||
if ($this->record->prevpageurl && $this->record->prevpageurl instanceof moodle_url) {
|
||||
$this->record->prevpageurl = $this->record->prevpageurl->out(false);
|
||||
}
|
||||
if ($this->record->exclusiveurl && $this->record->exclusiveurl instanceof moodle_url) {
|
||||
$this->record->exclusiveurl = $this->record->exclusiveurl->out(false);
|
||||
}
|
||||
return $this->record;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains class core_tag\output\taglist
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_tag\output;
|
||||
|
||||
use templatable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use core_tag_tag;
|
||||
use context;
|
||||
|
||||
/**
|
||||
* Class to preapare a list of tags for display, usually the list of tags some entry is tagged with.
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class taglist implements templatable {
|
||||
|
||||
/** @var array */
|
||||
protected $tags;
|
||||
|
||||
/** @var string */
|
||||
protected $label;
|
||||
|
||||
/** @var string */
|
||||
protected $classes;
|
||||
|
||||
/** @var int */
|
||||
protected $limit;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $tags list of instances of \core_tag_tag or \stdClass
|
||||
* @param string $label label to display in front, by default 'Tags' (get_string('tags')), set to null
|
||||
* to use default, set to '' (empty string) to omit the label completely
|
||||
* @param string $classes additional classes for the enclosing div element
|
||||
* @param int $limit limit the number of tags to display, if size of $tags is more than this limit the "more" link
|
||||
* will be appended to the end, JS will toggle the rest of the tags
|
||||
* @param context $pagecontext specify if needed to overwrite the current page context for the view tag link
|
||||
*/
|
||||
public function __construct($tags, $label = null, $classes = '', $limit = 10, $pagecontext = null) {
|
||||
global $PAGE;
|
||||
$canmanagetags = has_capability('moodle/tag:manage', \context_system::instance());
|
||||
|
||||
$this->label = ($label === null) ? get_string('tags') : $label;
|
||||
$this->classes = $classes;
|
||||
$fromctx = $pagecontext ? $pagecontext->id :
|
||||
(($PAGE->context->contextlevel == CONTEXT_SYSTEM) ? 0 : $PAGE->context->id);
|
||||
|
||||
$this->tags = array();
|
||||
foreach ($tags as $idx => $tag) {
|
||||
$this->tags[$idx] = new stdClass();
|
||||
|
||||
$this->tags[$idx]->name = core_tag_tag::make_display_name($tag, false);
|
||||
|
||||
if ($canmanagetags && !empty($tag->flag)) {
|
||||
$this->tags[$idx]->flag = 1;
|
||||
}
|
||||
|
||||
$viewurl = core_tag_tag::make_url($tag->tagcollid, $tag->rawname, 0, $fromctx);
|
||||
$this->tags[$idx]->viewurl = $viewurl->out(false);
|
||||
|
||||
if (!empty($tag->tagtype)) {
|
||||
$this->tags[$idx]->tagtype = $tag->tagtype;
|
||||
}
|
||||
|
||||
if ($limit && count($this->tags) > $limit) {
|
||||
$this->tags[$idx]->overlimit = 1;
|
||||
}
|
||||
}
|
||||
$this->limit = $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
$cnt = count($this->tags);
|
||||
return (object)array(
|
||||
'tags' => array_values($this->tags),
|
||||
'label' => $this->label,
|
||||
'tagscount' => $cnt,
|
||||
'overflow' => ($this->limit && $cnt > $this->limit) ? 1 : 0,
|
||||
'classes' => $this->classes,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains class core_tag_renderer
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class core_tag_renderer
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_tag_renderer extends plugin_renderer_base {
|
||||
|
||||
/**
|
||||
* Renders the tag search page
|
||||
*
|
||||
* @param string $query
|
||||
* @param int $tagcollid
|
||||
* @return string
|
||||
*/
|
||||
public function tag_search_page($query = '', $tagcollid = 0) {
|
||||
$rv = $this->output->heading(get_string('searchtags', 'tag'), 2);
|
||||
|
||||
$searchbox = $this->search_form($query, $tagcollid);
|
||||
$rv .= html_writer::div($searchbox, '', array('id' => 'tag-search-box'));
|
||||
|
||||
$tagcloud = core_tag_collection::get_tag_cloud($tagcollid, '', 150, 'name', $query);
|
||||
$searchresults = '';
|
||||
if ($tagcloud->get_count()) {
|
||||
$searchresults = $this->output->render_from_template('core_tag/tagcloud',
|
||||
$tagcloud->export_for_template($this->output));
|
||||
$rv .= html_writer::div($searchresults, '', array('id' => 'tag-search-results'));
|
||||
} else if (strval($query) !== '') {
|
||||
$rv .= '<div class="tag-search-empty">' . get_string('notagsfound', 'tag', s($query)) . '</div>';
|
||||
}
|
||||
|
||||
return $rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the tag index page
|
||||
*
|
||||
* @param core_tag_tag $tag
|
||||
* @param \core_tag\output\tagindex[] $entities
|
||||
* @param int $tagareaid
|
||||
* @param bool $exclusivemode if set to true it means that no other entities tagged with this tag
|
||||
* are displayed on the page and the per-page limit may be bigger
|
||||
* @param int $fromctx context id where the link was displayed, may be used by callbacks
|
||||
* to display items in the same context first
|
||||
* @param int $ctx context id where to search for records
|
||||
* @param bool $rec search in subcontexts as well
|
||||
* @param int $page 0-based number of page being displayed
|
||||
* @return string
|
||||
*/
|
||||
public function tag_index_page($tag, $entities, $tagareaid, $exclusivemode, $fromctx, $ctx, $rec, $page) {
|
||||
global $CFG, $OUTPUT;
|
||||
$this->page->requires->js_call_amd('core/tag', 'init_tagindex_page');
|
||||
|
||||
$tagname = $tag->get_display_name();
|
||||
$systemcontext = context_system::instance();
|
||||
|
||||
if ($tag->flag > 0 && has_capability('moodle/tag:manage', $systemcontext)) {
|
||||
$tagname = '<span class="flagged-tag">' . $tagname . '</span>';
|
||||
}
|
||||
|
||||
$rv = '';
|
||||
$rv .= $this->output->heading($tagname, 2);
|
||||
|
||||
$rv .= $this->tag_links($tag);
|
||||
|
||||
if ($desciption = $tag->get_formatted_description()) {
|
||||
$rv .= $this->output->box($desciption, 'generalbox tag-description');
|
||||
}
|
||||
|
||||
$relatedtagslimit = 10;
|
||||
$relatedtags = $tag->get_related_tags();
|
||||
$taglist = new \core_tag\output\taglist($relatedtags, get_string('relatedtags', 'tag'),
|
||||
'tag-relatedtags', $relatedtagslimit);
|
||||
$rv .= $OUTPUT->render_from_template('core_tag/taglist', $taglist->export_for_template($OUTPUT));
|
||||
|
||||
// Display quick menu of the item types (if more than one item type found).
|
||||
$entitylinks = array();
|
||||
foreach ($entities as $entity) {
|
||||
if (!empty($entity->hascontent)) {
|
||||
$entitylinks[] = '<li><a href="#'.$entity->anchor.'">' .
|
||||
core_tag_area::display_name($entity->component, $entity->itemtype) . '</a></li>';
|
||||
}
|
||||
}
|
||||
|
||||
if (count($entitylinks) > 1) {
|
||||
$rv .= '<div class="tag-index-toc"><ul class="inline-list">' . join('', $entitylinks) . '</ul></div>';
|
||||
} else if (!$entitylinks) {
|
||||
$rv .= '<div class="tag-noresults">' . get_string('noresultsfor', 'tag', $tagname) . '</div>';
|
||||
}
|
||||
|
||||
// Display entities tagged with the tag.
|
||||
$content = '';
|
||||
foreach ($entities as $entity) {
|
||||
if (!empty($entity->hascontent)) {
|
||||
$content .= $this->output->render_from_template('core_tag/index', $entity->export_for_template($this->output));
|
||||
}
|
||||
}
|
||||
|
||||
if ($exclusivemode) {
|
||||
$rv .= $content;
|
||||
} else if ($content) {
|
||||
$rv .= html_writer::div($content, 'tag-index-items');
|
||||
}
|
||||
|
||||
// Display back link if we are browsing one tag area.
|
||||
if ($tagareaid) {
|
||||
$url = $tag->get_view_url(0, $fromctx, $ctx, $rec);
|
||||
$rv .= '<div class="tag-backtoallitems">' .
|
||||
html_writer::link($url, get_string('backtoallitems', 'tag', $tag->get_display_name())) .
|
||||
'</div>';
|
||||
}
|
||||
|
||||
return $rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a box that contains the management links of a tag
|
||||
*
|
||||
* @param core_tag_tag $tag
|
||||
* @return string
|
||||
*/
|
||||
protected function tag_links($tag) {
|
||||
if ($links = $tag->get_links()) {
|
||||
$content = '<ul class="inline-list"><li>' . implode('</li> <li>', $links) . '</li></ul>';
|
||||
return html_writer::div($content, 'tag-management-box');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the tag search box
|
||||
*
|
||||
* @param string $query last search string
|
||||
* @param int $tagcollid last selected tag collection id
|
||||
* @return string
|
||||
*/
|
||||
protected function search_form($query = '', $tagcollid = 0) {
|
||||
$searchurl = new moodle_url('/tag/search.php');
|
||||
$output = '<form action="' . $searchurl . '">';
|
||||
$output .= '<label class="accesshide" for="searchform_query">' . get_string('searchtags', 'tag') . '</label>';
|
||||
$output .= '<input id="searchform_query" name="query" type="text" size="40" value="' . s($query) . '" />';
|
||||
$tagcolls = core_tag_collection::get_collections_menu(false, true, get_string('inalltagcoll', 'tag'));
|
||||
if (count($tagcolls) > 1) {
|
||||
$output .= '<label class="accesshide" for="searchform_tc">' . get_string('selectcoll', 'tag') . '</label>';
|
||||
$output .= html_writer::select($tagcolls, 'tc', $tagcollid, null, array('id' => 'searchform_tc'));
|
||||
}
|
||||
$output .= '<input name="go" type="submit" size="40" value="' . s(get_string('search', 'tag')) . '" />';
|
||||
$output .= '</form>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
}
|
||||
+1414
File diff suppressed because it is too large
Load Diff
+47
-70
@@ -26,8 +26,8 @@ require_once('../config.php');
|
||||
require_once('lib.php');
|
||||
require_once('edit_form.php');
|
||||
|
||||
$tag_id = optional_param('id', 0, PARAM_INT);
|
||||
$tag_name = optional_param('tag', '', PARAM_TAG);
|
||||
$tagid = optional_param('id', 0, PARAM_INT);
|
||||
$tagname = optional_param('tag', '', PARAM_TAG);
|
||||
$returnurl = optional_param('returnurl', '', PARAM_LOCALURL);
|
||||
|
||||
require_login();
|
||||
@@ -40,34 +40,50 @@ if (empty($CFG->usetags)) {
|
||||
$systemcontext = context_system::instance();
|
||||
require_capability('moodle/tag:edit', $systemcontext);
|
||||
|
||||
if ($tag_name) {
|
||||
$tag = tag_get('name', $tag_name, '*');
|
||||
} else if ($tag_id) {
|
||||
$tag = tag_get('id', $tag_id, '*');
|
||||
if ($tagname) {
|
||||
$tagcollid = optional_param('tc', 0, PARAM_INT);
|
||||
if (!$tagcollid) {
|
||||
// Tag name specified but tag collection was not. Try to guess it.
|
||||
$tags = core_tag_tag::guess_by_name($tagname, '*');
|
||||
if (count($tags) > 1) {
|
||||
// This tag was found in more than one collection, redirect to search.
|
||||
redirect(new moodle_url('/tag/search.php', array('tag' => $tagname)));
|
||||
} else if (count($tags) == 1) {
|
||||
$tag = reset($tags);
|
||||
}
|
||||
} else {
|
||||
if (!$tag = core_tag_tag::get_by_name($tagcollid, $tagname, '*')) {
|
||||
redirect(new moodle_url('/tag/search.php', array('tagcollid' => $tagcollid)));
|
||||
}
|
||||
}
|
||||
} else if ($tagid) {
|
||||
$tag = core_tag_tag::get($tagid, '*');
|
||||
}
|
||||
|
||||
if (empty($tag)) {
|
||||
redirect($CFG->wwwroot.'/tag/search.php');
|
||||
redirect(new moodle_url('/tag/search.php'));
|
||||
}
|
||||
|
||||
$PAGE->set_url('/tag/index.php', array('id' => $tag->id));
|
||||
$PAGE->set_url($tag->get_view_url());
|
||||
$PAGE->set_subpage($tag->id);
|
||||
$PAGE->set_context($systemcontext);
|
||||
$PAGE->set_blocks_editing_capability('moodle/tag:editblocks');
|
||||
$PAGE->set_pagelayout('standard');
|
||||
|
||||
$tagname = tag_display_name($tag);
|
||||
$tagname = $tag->get_display_name();
|
||||
$tagcollid = $tag->tagcollid;
|
||||
|
||||
// set the relatedtags field of the $tag object that will be passed to the form
|
||||
$tag->relatedtags = tag_get_tags_array('tag', $tag->id);
|
||||
$data = $tag->to_object();
|
||||
$data->relatedtags = core_tag_tag::get_item_tags_array('core', 'tag', $tag->id);
|
||||
|
||||
$options = new stdClass();
|
||||
$options->smiley = false;
|
||||
$options->filter = false;
|
||||
|
||||
// convert and remove any XSS
|
||||
$tag->description = format_text($tag->description, $tag->descriptionformat, $options);
|
||||
$tag->descriptionformat = FORMAT_HTML;
|
||||
$data->description = format_text($tag->description, $tag->descriptionformat, $options);
|
||||
$data->descriptionformat = FORMAT_HTML;
|
||||
|
||||
$errorstring = '';
|
||||
|
||||
@@ -78,76 +94,37 @@ $editoroptions = array(
|
||||
'context' => $systemcontext,
|
||||
'subdirs' => file_area_contains_subdirs($systemcontext, 'tag', 'description', $tag->id),
|
||||
);
|
||||
$tag = file_prepare_standard_editor($tag, 'description', $editoroptions, $systemcontext, 'tag', 'description', $tag->id);
|
||||
$data = file_prepare_standard_editor($data, 'description', $editoroptions, $systemcontext, 'tag', 'description', $data->id);
|
||||
|
||||
$tagform = new tag_edit_form(null, compact('editoroptions'));
|
||||
if ( $tag->tagtype == 'official' ) {
|
||||
$tag->tagtype = '1';
|
||||
} else {
|
||||
$tag->tagtype = '0';
|
||||
}
|
||||
$tagform = new tag_edit_form(null, array('editoroptions' => $editoroptions, 'tag' => $tag));
|
||||
$data->tagtype = ($data->tagtype === 'official') ? '1' : '0';
|
||||
$data->returnurl = $returnurl;
|
||||
|
||||
$tag->returnurl = $returnurl;
|
||||
$tagform->set_data($tag);
|
||||
$tagform->set_data($data);
|
||||
|
||||
// If new data has been sent, update the tag record
|
||||
if ($tagform->is_cancelled()) {
|
||||
redirect($returnurl ? new moodle_url($returnurl) :
|
||||
new moodle_url('/tag/index.php', array('tag' => $tag->name)));
|
||||
redirect($returnurl ? new moodle_url($returnurl) : $tag->get_view_url());
|
||||
} else if ($tagnew = $tagform->get_data()) {
|
||||
// If new data has been sent, update the tag record.
|
||||
$updatedata = array();
|
||||
|
||||
if (has_capability('moodle/tag:manage', $systemcontext)) {
|
||||
if (($tag->tagtype != 'default') && (!isset($tagnew->tagtype) || ($tagnew->tagtype != '1'))) {
|
||||
tag_type_set($tag->id, 'default');
|
||||
|
||||
} elseif (($tag->tagtype != 'official') && ($tagnew->tagtype == '1')) {
|
||||
tag_type_set($tag->id, 'official');
|
||||
}
|
||||
$updatedata['tagtype'] = empty($tagnew->tagtype) ? 'default' : 'official';
|
||||
$updatedata['rawname'] = $tagnew->rawname;
|
||||
}
|
||||
|
||||
if (!has_capability('moodle/tag:manage', $systemcontext)) {
|
||||
unset($tagnew->name);
|
||||
unset($tagnew->rawname);
|
||||
$tagnew = file_postupdate_standard_editor($tagnew, 'description', $editoroptions,
|
||||
$systemcontext, 'tag', 'description', $tag->id);
|
||||
$updatedata['description'] = $tagnew->description;
|
||||
$updatedata['descriptionformat'] = $tagnew->descriptionformat;
|
||||
|
||||
} else { // They might be trying to change the rawname, make sure it's a change that doesn't affect name
|
||||
$norm = tag_normalize($tagnew->rawname, TAG_CASE_LOWER);
|
||||
$tagnew->name = array_shift($norm);
|
||||
// Update name, description and official type.
|
||||
$tag->update($updatedata);
|
||||
|
||||
if ($tag->rawname !== $tagnew->rawname) { // The name has changed, let's make sure it's not another existing tag
|
||||
if (($id = tag_get_id($tagnew->name)) && $id != $tag->id) { // Something exists already, so flag an error.
|
||||
$errorstring = s($tagnew->rawname).': '.get_string('namesalreadybeeingused', 'tag');
|
||||
}
|
||||
}
|
||||
}
|
||||
// Updated related tags.
|
||||
$tag->set_related_tags($tagnew->relatedtags);
|
||||
|
||||
if (empty($errorstring)) { // All is OK, let's save it
|
||||
|
||||
$tagnew = file_postupdate_standard_editor($tagnew, 'description', $editoroptions, $systemcontext, 'tag', 'description', $tag->id);
|
||||
|
||||
if ($tag->description != $tagnew->description) {
|
||||
tag_description_set($tag_id, $tagnew->description, $tagnew->descriptionformat);
|
||||
}
|
||||
|
||||
$tagnew->timemodified = time();
|
||||
|
||||
if (has_capability('moodle/tag:manage', $systemcontext)) {
|
||||
// Check if we need to rename the tag.
|
||||
if (isset($tagnew->name) && ($tag->rawname != $tagnew->rawname)) {
|
||||
// Rename the tag.
|
||||
if (!tag_rename($tag->id, $tagnew->rawname)) {
|
||||
print_error('errorupdatingrecord', 'tag');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//updated related tags
|
||||
tag_set('tag', $tagnew->id, $tagnew->relatedtags, 'core', $systemcontext->id);
|
||||
//print_object($tagnew); die();
|
||||
|
||||
$tagname = isset($tagnew->rawname) ? $tagnew->rawname : $tag->rawname;
|
||||
redirect($returnurl ? new moodle_url($returnurl) :
|
||||
new moodle_url('/tag/index.php', array('tag' => $tagname)));
|
||||
}
|
||||
redirect($returnurl ? new moodle_url($returnurl) : $tag->get_view_url());
|
||||
}
|
||||
|
||||
navigation_node::override_active_url(new moodle_url('/tag/search.php'));
|
||||
|
||||
+27
-1
@@ -68,10 +68,36 @@ class tag_edit_form extends moodleform {
|
||||
$mform->addElement('checkbox', 'tagtype', get_string('officialtag', 'tag'));
|
||||
}
|
||||
|
||||
$mform->addElement('tags', 'relatedtags', get_string('relatedtags','tag'));
|
||||
$mform->addElement('tags', 'relatedtags', get_string('relatedtags', 'tag'),
|
||||
array('tagcollid' => $this->_customdata['tag']->tagcollid));
|
||||
|
||||
$this->add_action_buttons(true, get_string('updatetag', 'tag'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom form validation
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $files
|
||||
* @return array
|
||||
*/
|
||||
public function validation($data, $files) {
|
||||
$errors = parent::validation($data, $files);
|
||||
|
||||
if (isset($data['rawname'])) {
|
||||
$newname = core_text::strtolower($data['rawname']);
|
||||
$tag = $this->_customdata['tag'];
|
||||
if ($tag->name != $newname) {
|
||||
// The name has changed, let's make sure it's not another existing tag.
|
||||
if (core_tag_tag::get_by_name($tag->tagcollid, $newname)) {
|
||||
// Something exists already, so flag an error.
|
||||
$errors['rawname'] = get_string('namesalreadybeeingused', 'tag');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+67
-142
@@ -23,10 +23,8 @@
|
||||
*/
|
||||
|
||||
require_once('../config.php');
|
||||
require_once('lib.php');
|
||||
require_once('locallib.php');
|
||||
require_once($CFG->dirroot.'/lib/weblib.php');
|
||||
require_once($CFG->dirroot.'/blog/lib.php');
|
||||
require_once($CFG->dirroot . '/lib/weblib.php');
|
||||
require_once($CFG->dirroot . '/blog/lib.php');
|
||||
|
||||
require_login();
|
||||
|
||||
@@ -36,166 +34,93 @@ if (empty($CFG->usetags)) {
|
||||
|
||||
$tagid = optional_param('id', 0, PARAM_INT); // tag id
|
||||
$tagname = optional_param('tag', '', PARAM_TAG); // tag
|
||||
$tagareaid = optional_param('ta', 0, PARAM_INT); // Tag area id.
|
||||
$exclusivemode = optional_param('excl', 0, PARAM_BOOL); // Exclusive mode (show entities in one tag area only).
|
||||
$page = optional_param('page', 0, PARAM_INT); // Page to display.
|
||||
$fromctx = optional_param('from', null, PARAM_INT);
|
||||
$ctx = optional_param('ctx', null, PARAM_INT);
|
||||
$rec = optional_param('rec', 1, PARAM_INT);
|
||||
|
||||
$edit = optional_param('edit', -1, PARAM_BOOL);
|
||||
$userpage = optional_param('userpage', 0, PARAM_INT); // which page to show
|
||||
$perpage = optional_param('perpage', 24, PARAM_INT);
|
||||
|
||||
$systemcontext = context_system::instance();
|
||||
|
||||
if ($tagname) {
|
||||
$tag = tag_get('name', $tagname, '*');
|
||||
$tagcollid = optional_param('tc', 0, PARAM_INT);
|
||||
if (!$tagcollid) {
|
||||
// Tag name specified but tag collection was not. Try to guess it.
|
||||
$tags = core_tag_tag::guess_by_name($tagname, '*');
|
||||
if (count($tags) > 1) {
|
||||
// This tag was found in more than one collection, redirect to search.
|
||||
redirect(new moodle_url('/tag/search.php', array('query' => $tagname)));
|
||||
} else if (count($tags) == 1) {
|
||||
$tag = reset($tags);
|
||||
}
|
||||
} else {
|
||||
if (!$tag = core_tag_tag::get_by_name($tagcollid, $tagname, '*')) {
|
||||
redirect(new moodle_url('/tag/search.php', array('tc' => $tagcollid, 'query' => $tagname)));
|
||||
}
|
||||
}
|
||||
} else if ($tagid) {
|
||||
$tag = tag_get('id', $tagid, '*');
|
||||
$tag = core_tag_tag::get($tagid, '*');
|
||||
}
|
||||
unset($tagid);
|
||||
if (empty($tag)) {
|
||||
redirect($CFG->wwwroot.'/tag/search.php');
|
||||
redirect(new moodle_url('/tag/search.php'));
|
||||
}
|
||||
|
||||
$PAGE->set_url('/tag/index.php', array('id' => $tag->id));
|
||||
if ($ctx && ($context = context::instance_by_id($ctx, IGNORE_MISSING)) && $context->contextlevel >= CONTEXT_COURSE) {
|
||||
list($context, $course, $cm) = get_context_info_array($context->id);
|
||||
require_login($course, false, $cm, false, true);
|
||||
} else {
|
||||
$PAGE->set_context($systemcontext);
|
||||
}
|
||||
|
||||
$tagcollid = $tag->tagcollid;
|
||||
|
||||
$PAGE->set_url($tag->get_view_url($exclusivemode, $fromctx, $ctx, $rec));
|
||||
$PAGE->set_subpage($tag->id);
|
||||
$PAGE->set_context($systemcontext);
|
||||
$tagnode = $PAGE->navigation->find('tags', null);
|
||||
$tagnode->make_active();
|
||||
$PAGE->set_pagelayout('standard');
|
||||
$PAGE->set_blocks_editing_capability('moodle/tag:editblocks');
|
||||
|
||||
if (($edit != -1) and $PAGE->user_allowed_editing()) {
|
||||
$USER->editing = $edit;
|
||||
$buttons = '';
|
||||
if (has_capability('moodle/tag:manage', context_system::instance())) {
|
||||
$buttons .= $OUTPUT->single_button(new moodle_url('/tag/manage.php'),
|
||||
get_string('managetags', 'tag'), 'GET');
|
||||
}
|
||||
|
||||
$tagname = tag_display_name($tag);
|
||||
$title = get_string('tag', 'tag') .' - '. $tagname;
|
||||
|
||||
$button = '';
|
||||
if ($PAGE->user_allowed_editing() ) {
|
||||
$button = $OUTPUT->edit_button(new moodle_url("$CFG->wwwroot/tag/index.php", array('id' => $tag->id)));
|
||||
if ($PAGE->user_allowed_editing()) {
|
||||
if ($edit != -1) {
|
||||
$USER->editing = $edit;
|
||||
}
|
||||
$buttons .= $OUTPUT->edit_button(clone($PAGE->url));
|
||||
}
|
||||
|
||||
$PAGE->navbar->add($tagname);
|
||||
$PAGE->set_title($title);
|
||||
$PAGE->set_title(get_string('tag', 'tag') .' - '. $tag->get_display_name());
|
||||
$PAGE->set_heading($COURSE->fullname);
|
||||
$PAGE->set_button($button);
|
||||
$courserenderer = $PAGE->get_renderer('core', 'course');
|
||||
$PAGE->set_button($buttons);
|
||||
|
||||
// Find all areas in this collection and their items tagged with this tag.
|
||||
$tagareas = core_tag_collection::get_areas($tagcollid);
|
||||
if ($tagareaid) {
|
||||
$tagareas = array_intersect_key($tagareas, array($tagareaid => 1));
|
||||
}
|
||||
if (!$tagareaid && count($tagareas) == 1) {
|
||||
// Automatically set "exclusive" mode for tag collection with one tag area only.
|
||||
$exclusivemode = 1;
|
||||
}
|
||||
$entities = array();
|
||||
foreach ($tagareas as $ta) {
|
||||
$entities[] = $tag->get_tag_index($ta, $exclusivemode, $fromctx, $ctx, $rec, $page);
|
||||
}
|
||||
$entities = array_filter($entities);
|
||||
|
||||
$tagrenderer = $PAGE->get_renderer('core', 'tag');
|
||||
$pagecontents = $tagrenderer->tag_index_page($tag, array_filter($entities), $tagareaid,
|
||||
$exclusivemode, $fromctx, $ctx, $rec, $page);
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
// Manage all tags links
|
||||
if (has_capability('moodle/tag:manage', $systemcontext)) {
|
||||
echo '<div class="managelink"><a href="'. $CFG->wwwroot .'/tag/manage.php">'. get_string('managetags', 'tag') .'</a></div>' ;
|
||||
}
|
||||
|
||||
$tagname = tag_display_name($tag);
|
||||
|
||||
if ($tag->flag > 0 && has_capability('moodle/tag:manage', $systemcontext)) {
|
||||
$tagname = '<span class="flagged-tag">' . $tagname . '</span>';
|
||||
}
|
||||
|
||||
echo $OUTPUT->heading($tagname, 2);
|
||||
tag_print_management_box($tag);
|
||||
tag_print_description_box($tag);
|
||||
// Check what type of results are avaialable
|
||||
$courses = $courserenderer->tagged_courses($tag->id);
|
||||
|
||||
if (!empty($CFG->enableblogs) && has_capability('moodle/blog:view', $systemcontext)) {
|
||||
require_once($CFG->dirroot.'/blog/lib.php');
|
||||
require_once($CFG->dirroot.'/blog/locallib.php');
|
||||
|
||||
$bloglisting = new blog_listing(array('tag' => $tag->id));
|
||||
$limit = 10;
|
||||
$start = 0;
|
||||
$blogs = $bloglisting->get_entries($start, $limit);
|
||||
}
|
||||
$usercount = tag_record_count('user', $tag->id);
|
||||
|
||||
// Only include <a href />'s to those anchors that actually will be shown
|
||||
$relatedpageslink = "";
|
||||
$countanchors = 0;
|
||||
if (!empty($courses)) {
|
||||
$relatedpageslink = '<a href="#course">'.get_string('courses').'</a>';
|
||||
$countanchors++;
|
||||
}
|
||||
if (!empty($blogs)) {
|
||||
if ($countanchors > 0) {
|
||||
$relatedpageslink .= ' | ';
|
||||
}
|
||||
$relatedpageslink .= '<a href="#blog">'.get_string('relatedblogs', 'tag').'</a>';
|
||||
$countanchors++;
|
||||
}
|
||||
if ($usercount > 0) {
|
||||
if ($countanchors > 0) {
|
||||
$relatedpageslink .= ' | ';
|
||||
}
|
||||
$relatedpageslink .= '<a href="#user">'.get_string('users').'</a>';
|
||||
$countanchors++;
|
||||
}
|
||||
// If only one anchor is present, no <a href /> is needed
|
||||
if ($countanchors == 0) {
|
||||
echo '<div class="relatedpages"><p>'.get_string('noresultsfor', 'tag', $tagname).'</p></div>';
|
||||
} elseif ($countanchors > 1) {
|
||||
echo '<div class="relatedpages"><p>'.$relatedpageslink.'</p></div>';
|
||||
}
|
||||
|
||||
// Display courses tagged with the tag
|
||||
if (!empty($courses)) {
|
||||
|
||||
echo $OUTPUT->box_start('generalbox', 'tag-blogs'); //could use an id separate from tag-blogs, but would have to copy the css style to make it look the same
|
||||
|
||||
echo "<a name='course'></a>";
|
||||
echo $courses;
|
||||
|
||||
echo $OUTPUT->box_end();
|
||||
}
|
||||
|
||||
// Print up to 10 previous blogs entries
|
||||
|
||||
if (!empty($blogs)) {
|
||||
echo $OUTPUT->box_start('generalbox', 'tag-blogs');
|
||||
$heading = get_string('relatedblogs', 'tag', $tagname). ' ' . get_string('taggedwith', 'tag', $tagname);
|
||||
echo "<a name='blog'></a>";
|
||||
echo $OUTPUT->heading($heading, 3);
|
||||
|
||||
echo '<ul id="tagblogentries">';
|
||||
foreach ($blogs as $blog) {
|
||||
if ($blog->publishstate == 'draft') {
|
||||
$class = 'class="dimmed"';
|
||||
} else {
|
||||
$class = '';
|
||||
}
|
||||
echo '<li '.$class.'>';
|
||||
echo '<a '.$class.' href="'.$CFG->wwwroot.'/blog/index.php?entryid='.$blog->id.'">';
|
||||
echo format_string($blog->subject);
|
||||
echo '</a>';
|
||||
echo ' - ';
|
||||
echo '<a '.$class.' href="'.$CFG->wwwroot.'/user/view.php?id='.$blog->userid.'">';
|
||||
echo fullname($blog);
|
||||
echo '</a>';
|
||||
echo ', '. userdate($blog->lastmodified);
|
||||
echo '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
|
||||
$allblogsurl = new moodle_url('/blog/index.php', array('tagid' => $tag->id));
|
||||
echo '<p class="moreblogs"><a href="'.$allblogsurl->out().'">'.get_string('seeallblogs', 'tag', $tagname).'</a></p>';
|
||||
|
||||
echo $OUTPUT->box_end();
|
||||
}
|
||||
|
||||
if ($usercount > 0) {
|
||||
|
||||
//user table box
|
||||
echo $OUTPUT->box_start('generalbox', 'tag-user-table');
|
||||
|
||||
$heading = get_string('users'). ' ' . get_string('taggedwith', 'tag', $tagname) . ': ' . $usercount;
|
||||
echo "<a name='user'></a>";
|
||||
echo $OUTPUT->heading($heading, 3);
|
||||
|
||||
$baseurl = new moodle_url('/tag/index.php', array('id' => $tag->id));
|
||||
$pagingbar = new paging_bar($usercount, $userpage, $perpage, $baseurl);
|
||||
$pagingbar->pagevar = 'userpage';
|
||||
echo $OUTPUT->render($pagingbar);
|
||||
tag_print_tagged_users_table($tag, $userpage * $perpage, $perpage);
|
||||
echo $OUTPUT->box_end();
|
||||
}
|
||||
|
||||
echo $pagecontents;
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
+2
-1526
File diff suppressed because it is too large
Load Diff
+2
-418
@@ -23,421 +23,5 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
*/
|
||||
|
||||
require_once($CFG->dirroot.'/tag/lib.php');
|
||||
require_once($CFG->libdir.'/filelib.php');
|
||||
|
||||
/**
|
||||
* Prints or returns a HTML tag cloud with varying classes styles depending on the popularity and type of each tag.
|
||||
*
|
||||
* @package core_tag
|
||||
* @access public
|
||||
* @category tag
|
||||
* @param array $tagset Array of tags to display
|
||||
* @param int $nr_of_tags Limit for the number of tags to return/display, used if $tagset is null
|
||||
* @param bool $return if true the function will return the generated tag cloud instead of displaying it.
|
||||
* @param string $sort (optional) selected sorting, default is alpha sort (name) also timemodified or popularity
|
||||
* @return string|null a HTML string or null if this function does the output
|
||||
*/
|
||||
function tag_print_cloud($tagset=null, $nr_of_tags=150, $return=false, $sort='') {
|
||||
global $CFG, $DB;
|
||||
|
||||
$can_manage_tags = has_capability('moodle/tag:manage', context_system::instance());
|
||||
|
||||
if (is_null($tagset)) {
|
||||
// No tag set received, so fetch tags from database
|
||||
if ( !$tagsincloud = $DB->get_records_sql('SELECT tg.rawname, tg.id, tg.name, tg.tagtype, COUNT(ti.id) AS count, tg.flag
|
||||
FROM {tag_instance} ti JOIN {tag} tg ON tg.id = ti.tagid
|
||||
WHERE ti.itemtype <> \'tag\'
|
||||
GROUP BY tg.id, tg.rawname, tg.name, tg.flag, tg.tagtype
|
||||
ORDER BY count DESC, tg.name ASC', null, 0, $nr_of_tags) ) {
|
||||
$tagsincloud = array();
|
||||
}
|
||||
} else {
|
||||
$tagsincloud = $tagset;
|
||||
}
|
||||
|
||||
$tagkeys = array_keys($tagsincloud);
|
||||
if (!empty($tagkeys)) {
|
||||
$firsttagkey = $tagkeys[0];
|
||||
$maxcount = $tagsincloud[$firsttagkey]->count;
|
||||
}
|
||||
|
||||
$etags = array();
|
||||
|
||||
foreach ($tagsincloud as $tag) {
|
||||
$size = (int) (( $tag->count / $maxcount) * 20);
|
||||
$tag->class = "$tag->tagtype s$size";
|
||||
$etags[] = $tag;
|
||||
}
|
||||
|
||||
// Set up sort global - used to pass sort type into tag_cloud_sort through usort() avoiding multiple sort functions.
|
||||
// TODO make calling functions pass 'count' or 'timemodified' not 'popularity' or 'date'.
|
||||
$oldsort = empty($CFG->tagsort) ? null : $CFG->tagsort;
|
||||
if ($sort == 'popularity') {
|
||||
$CFG->tagsort = 'count';
|
||||
} else if ($sort == 'date') {
|
||||
$CFG->tagsort = 'timemodified';
|
||||
} else {
|
||||
$CFG->tagsort = 'name';
|
||||
}
|
||||
usort($etags, "tag_cloud_sort");
|
||||
$CFG->tagsort = $oldsort;
|
||||
|
||||
$output = '';
|
||||
$output .= "\n<ul class='tag_cloud inline-list'>\n";
|
||||
foreach ($etags as $tag) {
|
||||
if ($tag->flag > 0 && $can_manage_tags) {
|
||||
$tagname = '<span class="flagged-tag">'. tag_display_name($tag) .'</span>';
|
||||
} else {
|
||||
$tagname = tag_display_name($tag);
|
||||
}
|
||||
|
||||
$link = $CFG->wwwroot .'/tag/index.php?tag='. rawurlencode($tag->name);
|
||||
$output .= '<li><a href="'. $link .'" class="'. $tag->class .'" '.
|
||||
'title="'. get_string('numberofentries', 'blog', $tag->count) .'">'.
|
||||
$tagname .'</a></li> ';
|
||||
}
|
||||
$output .= "\n</ul>\n";
|
||||
|
||||
if ($return) {
|
||||
return $output;
|
||||
} else {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used by print_tag_cloud, to usort() the tags in the cloud. See php.net/usort for the parameters documentation.
|
||||
* This was originally in blocks/blog_tags/block_blog_tags.php, named blog_tags_sort().
|
||||
*
|
||||
* @package core_tag
|
||||
* @access private
|
||||
* @param string $a Tag name to compare against $b
|
||||
* @param string $b Tag name to compare against $a
|
||||
* @return int The result of the comparison/validation 1, 0 or -1
|
||||
*/
|
||||
function tag_cloud_sort($a, $b) {
|
||||
global $CFG;
|
||||
|
||||
if (empty($CFG->tagsort)) {
|
||||
$tagsort = 'name'; // by default, sort by name
|
||||
} else {
|
||||
$tagsort = $CFG->tagsort;
|
||||
}
|
||||
|
||||
if (is_numeric($a->$tagsort)) {
|
||||
return ($a->$tagsort == $b->$tagsort) ? 0 : ($a->$tagsort > $b->$tagsort) ? 1 : -1;
|
||||
} elseif (is_string($a->$tagsort)) {
|
||||
return strcmp($a->$tagsort, $b->$tagsort);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a box with the description of a tag and its related tags
|
||||
*
|
||||
* @package core_tag
|
||||
* @access public
|
||||
* @todo MDL-31149 create a system setting for $max_tags_displayed, instead of using an in code literal
|
||||
* @param stdClass $tag_object
|
||||
* @param bool $return if true the function will return the generated tag cloud instead of displaying it.
|
||||
* @return string/null a HTML box showing a description of the tag object and it's relationsips or null if output is done directly
|
||||
* in the function.
|
||||
*/
|
||||
function tag_print_description_box($tag_object, $return=false) {
|
||||
|
||||
global $USER, $CFG, $OUTPUT;
|
||||
|
||||
$max_tags_displayed = 10;
|
||||
|
||||
$tagname = tag_display_name($tag_object);
|
||||
$related_tags = tag_get_related_tags($tag_object->id, TAG_RELATED_ALL, $max_tags_displayed+1); // this gets one more than we want
|
||||
|
||||
$content = !empty($tag_object->description) || $related_tags;
|
||||
$output = '';
|
||||
|
||||
if ($content) {
|
||||
$output .= $OUTPUT->box_start('generalbox', 'tag-description');
|
||||
}
|
||||
|
||||
if (!empty($tag_object->description)) {
|
||||
$options = new stdClass();
|
||||
$options->para = false;
|
||||
$options->overflowdiv = true;
|
||||
$tag_object->description = file_rewrite_pluginfile_urls($tag_object->description, 'pluginfile.php', context_system::instance()->id, 'tag', 'description', $tag_object->id);
|
||||
$output .= format_text($tag_object->description, $tag_object->descriptionformat, $options);
|
||||
}
|
||||
|
||||
if ($related_tags) {
|
||||
$more_links = false;
|
||||
if (count($related_tags) > $max_tags_displayed) {
|
||||
array_pop($related_tags);
|
||||
$more_links = true;
|
||||
}
|
||||
$output .= '<br /><br /><strong>'. get_string('relatedtags', 'tag') .': </strong>'. tag_get_related_tags_csv($related_tags);
|
||||
if ($more_links) {
|
||||
$output .= ' ...';
|
||||
}
|
||||
}
|
||||
|
||||
if ($content) {
|
||||
$output .= $OUTPUT->box_end();
|
||||
}
|
||||
|
||||
if ($return) {
|
||||
return $output;
|
||||
} else {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a box that contains the management links of a tag
|
||||
*
|
||||
* @access public
|
||||
* @param stdClass $tag_object
|
||||
* @param bool $return if true the function will return the generated tag cloud instead of displaying it.
|
||||
* @return string|null a HTML string or null if this function does the output
|
||||
*/
|
||||
function tag_print_management_box($tag_object, $return=false) {
|
||||
|
||||
global $USER, $CFG, $OUTPUT;
|
||||
|
||||
$tagname = tag_display_name($tag_object);
|
||||
$output = '';
|
||||
|
||||
if (!isguestuser()) {
|
||||
$output .= $OUTPUT->box_start('box','tag-management-box');
|
||||
$systemcontext = context_system::instance();
|
||||
$links = array();
|
||||
|
||||
// Add a link for users to add/remove this from their interests
|
||||
if (tag_record_tagged_with('user', $USER->id, $tag_object->name)) {
|
||||
$links[] = '<a href="'. $CFG->wwwroot .'/tag/user.php?action=removeinterest&sesskey='. sesskey() .'&tag='. rawurlencode($tag_object->name) .'">'. get_string('removetagfrommyinterests', 'tag', $tagname) .'</a>';
|
||||
} else {
|
||||
$links[] = '<a href="'. $CFG->wwwroot .'/tag/user.php?action=addinterest&sesskey='. sesskey() .'&tag='. rawurlencode($tag_object->name) .'">'. get_string('addtagtomyinterests', 'tag', $tagname) .'</a>';
|
||||
}
|
||||
|
||||
// Flag as inappropriate link. Only people with moodle/tag:flag capability.
|
||||
if (has_capability('moodle/tag:flag', $systemcontext)) {
|
||||
$links[] = '<a href="'. $CFG->wwwroot .'/tag/user.php?action=flaginappropriate&sesskey='. sesskey() .'&tag='. rawurlencode($tag_object->name) .'">'. get_string('flagasinappropriate', 'tag', rawurlencode($tagname)) .'</a>';
|
||||
}
|
||||
|
||||
// Edit tag: Only people with moodle/tag:edit capability who either have it as an interest or can manage tags
|
||||
if (has_capability('moodle/tag:edit', $systemcontext) ||
|
||||
has_capability('moodle/tag:manage', $systemcontext)) {
|
||||
$links[] = '<a href="'. $CFG->wwwroot .'/tag/edit.php?tag='. rawurlencode($tag_object->name) .'">'. get_string('edittag', 'tag') .'</a>';
|
||||
}
|
||||
|
||||
$output .= implode(' | ', $links);
|
||||
$output .= $OUTPUT->box_end();
|
||||
}
|
||||
|
||||
if ($return) {
|
||||
return $output;
|
||||
} else {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the tag search box
|
||||
*
|
||||
* @access public
|
||||
* @param bool $return if true return html string
|
||||
* @return string|null a HTML string or null if this function does the output
|
||||
*/
|
||||
function tag_print_search_box($return=false) {
|
||||
global $CFG, $OUTPUT;
|
||||
|
||||
$output = $OUTPUT->box_start('','tag-search-box');
|
||||
$output .= '<form action="'.$CFG->wwwroot.'/tag/search.php" style="display:inline">';
|
||||
$output .= '<div>';
|
||||
$output .= '<label class="accesshide" for="searchform_search">'.get_string('searchtags', 'tag').'</label>';
|
||||
$output .= '<input id="searchform_search" name="query" type="text" size="40" />';
|
||||
$output .= '<button id="searchform_button" type="submit">'. get_string('search', 'tag') .'</button><br />';
|
||||
$output .= '</div>';
|
||||
$output .= '</form>';
|
||||
$output .= $OUTPUT->box_end();
|
||||
|
||||
if ($return) {
|
||||
return $output;
|
||||
}
|
||||
else {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the tag search results
|
||||
*
|
||||
* @access public
|
||||
* @param string $query text that tag names will be matched against
|
||||
* @param int $page current page
|
||||
* @param int $perpage nr of users displayed per page
|
||||
* @param bool $return if true return html string
|
||||
* @return string|null a HTML string or null if this function does the output
|
||||
*/
|
||||
function tag_print_search_results($query, $page, $perpage, $return=false) {
|
||||
|
||||
global $CFG, $USER, $OUTPUT;
|
||||
|
||||
$norm = tag_normalize($query, TAG_CASE_ORIGINAL);
|
||||
$query = array_shift($norm);
|
||||
|
||||
$count = sizeof(tag_find_tags($query, false));
|
||||
$tags = array();
|
||||
|
||||
if ( $found_tags = tag_find_tags($query, true, $page * $perpage, $perpage) ) {
|
||||
$tags = array_values($found_tags);
|
||||
}
|
||||
|
||||
$baseurl = $CFG->wwwroot.'/tag/search.php?query='. rawurlencode($query);
|
||||
$output = '';
|
||||
|
||||
// link "Add $query to my interests"
|
||||
$addtaglink = '';
|
||||
if( !tag_record_tagged_with('user', $USER->id, $query) ) {
|
||||
$addtaglink = '<a href="'. $CFG->wwwroot .'/tag/user.php?action=addinterest&sesskey='. sesskey() .'&tag='. rawurlencode($query) .'">';
|
||||
$addtaglink .= get_string('addtagtomyinterests', 'tag', htmlspecialchars($query)) .'</a>';
|
||||
}
|
||||
|
||||
if ( !empty($tags) ) { // there are results to display!!
|
||||
$output .= $OUTPUT->heading(get_string('searchresultsfor', 'tag', htmlspecialchars($query)) ." : {$count}", 3, 'main');
|
||||
|
||||
//print a link "Add $query to my interests"
|
||||
if (!empty($addtaglink)) {
|
||||
$output .= $OUTPUT->box($addtaglink, 'box', 'tag-management-box');
|
||||
}
|
||||
|
||||
$nr_of_lis_per_ul = 6;
|
||||
$nr_of_uls = ceil( sizeof($tags) / $nr_of_lis_per_ul );
|
||||
|
||||
$output .= '<ul id="tag-search-results">';
|
||||
for($i = 0; $i < $nr_of_uls; $i++) {
|
||||
$output .= '<li>';
|
||||
foreach (array_slice($tags, $i * $nr_of_lis_per_ul, $nr_of_lis_per_ul) as $tag) {
|
||||
$tag_link = ' <a href="'. $CFG->wwwroot .'/tag/index.php?id='. $tag->id .'">'. tag_display_name($tag) .'</a>';
|
||||
$output .= '•'. $tag_link .'<br/>';
|
||||
}
|
||||
$output .= '</li>';
|
||||
}
|
||||
$output .= '</ul>';
|
||||
$output .= '<div> </div>'; // <-- small layout hack in order to look good in Firefox
|
||||
|
||||
$output .= $OUTPUT->paging_bar($count, $page, $perpage, $baseurl);
|
||||
}
|
||||
else { //no results were found!!
|
||||
$output .= $OUTPUT->heading(get_string('noresultsfor', 'tag', htmlspecialchars($query)), 3, 'main');
|
||||
|
||||
//print a link "Add $query to my interests"
|
||||
if (!empty($addtaglink)) {
|
||||
$output .= $OUTPUT->box($addtaglink, 'box', 'tag-management-box');
|
||||
}
|
||||
}
|
||||
|
||||
if ($return) {
|
||||
return $output;
|
||||
}
|
||||
else {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a table of the users tagged with the tag passed as argument
|
||||
*
|
||||
* @param int $tag_object the tag we wish to return data for
|
||||
* @param int $limitfrom (optional, required if $limitnum is set) prints users starting at this point.
|
||||
* @param int $limitnum (optional, required if $limitfrom is set) prints this many users.
|
||||
* @param bool $return if true return html string
|
||||
* @return string|null a HTML string or null if this function does the output
|
||||
*/
|
||||
function tag_print_tagged_users_table($tag_object, $limitfrom='', $limitnum='', $return=false) {
|
||||
|
||||
//List of users with this tag
|
||||
$userlist = tag_find_records($tag_object->name, 'user', $limitfrom, $limitnum);
|
||||
|
||||
$output = tag_print_user_list($userlist, true);
|
||||
|
||||
if ($return) {
|
||||
return $output;
|
||||
}
|
||||
else {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an individual user box
|
||||
*
|
||||
* @param user_object $user (contains the following fields: id, firstname, lastname and picture)
|
||||
* @param bool $return if true return html string
|
||||
* @return string|null a HTML string or null if this function does the output
|
||||
*/
|
||||
function tag_print_user_box($user, $return=false) {
|
||||
global $CFG, $OUTPUT;
|
||||
|
||||
$usercontext = context_user::instance($user->id);
|
||||
$profilelink = '';
|
||||
|
||||
if ($usercontext and (has_capability('moodle/user:viewdetails', $usercontext) || has_coursecontact_role($user->id))) {
|
||||
$profilelink = $CFG->wwwroot .'/user/view.php?id='. $user->id;
|
||||
}
|
||||
|
||||
$output = $OUTPUT->box_start('user-box', 'user'. $user->id);
|
||||
$fullname = fullname($user);
|
||||
$alt = '';
|
||||
|
||||
if (!empty($profilelink)) {
|
||||
$output .= '<a href="'. $profilelink .'">';
|
||||
$alt = $fullname;
|
||||
}
|
||||
|
||||
$output .= $OUTPUT->user_picture($user, array('size'=>100));
|
||||
$output .= '<br />';
|
||||
|
||||
if (!empty($profilelink)) {
|
||||
$output .= '</a>';
|
||||
}
|
||||
|
||||
//truncate name if it's too big
|
||||
if (core_text::strlen($fullname) > 26) {
|
||||
$fullname = core_text::substr($fullname, 0, 26) .'...';
|
||||
}
|
||||
|
||||
$output .= '<strong>'. $fullname .'</strong>';
|
||||
$output .= $OUTPUT->box_end();
|
||||
|
||||
if ($return) {
|
||||
return $output;
|
||||
}
|
||||
else {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of users
|
||||
*
|
||||
* @param array $userlist an array of user objects
|
||||
* @param bool $return if true return html string, otherwise output the result
|
||||
* @return string|null a HTML string or null if this function does the output
|
||||
*/
|
||||
function tag_print_user_list($userlist, $return=false) {
|
||||
|
||||
$output = '<ul class="inline-list">';
|
||||
|
||||
foreach ($userlist as $user){
|
||||
$output .= '<li>'. tag_print_user_box($user, true) ."</li>\n";
|
||||
}
|
||||
$output .= "</ul>\n";
|
||||
|
||||
if ($return) {
|
||||
return $output;
|
||||
}
|
||||
else {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
debugging('All functions from /tags/locallib.php were deprecated and it will be removed soon, '.
|
||||
'do not include this file in your code', DEBUG_DEVELOPER);
|
||||
|
||||
+163
-36
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@@ -16,8 +15,9 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* @package core
|
||||
* @subpackage tag
|
||||
* Managing tags, tag areas and tags collections
|
||||
*
|
||||
* @package core_tag
|
||||
* @copyright 2007 Luiz Cruz <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -37,12 +37,8 @@ $action = optional_param('action', '', PARAM_ALPHA);
|
||||
$perpage = optional_param('perpage', DEFAULT_PAGE_SIZE, PARAM_INT);
|
||||
$page = optional_param('page', 0, PARAM_INT);
|
||||
$notice = optional_param('notice', '', PARAM_ALPHA);
|
||||
|
||||
require_login();
|
||||
|
||||
if (empty($CFG->usetags)) {
|
||||
print_error('tagsaredisabled', 'tag');
|
||||
}
|
||||
$tagcollid = optional_param('tc', 0, PARAM_INT);
|
||||
$tagareaid = optional_param('ta', null, PARAM_INT);
|
||||
|
||||
$params = array();
|
||||
if ($perpage != DEFAULT_PAGE_SIZE) {
|
||||
@@ -51,60 +47,168 @@ if ($perpage != DEFAULT_PAGE_SIZE) {
|
||||
if ($page > 0) {
|
||||
$params['page'] = $page;
|
||||
}
|
||||
|
||||
admin_externalpage_setup('managetags', '', $params, '', array('pagelayout' => 'report'));
|
||||
|
||||
if (empty($CFG->usetags)) {
|
||||
print_error('tagsaredisabled', 'tag');
|
||||
}
|
||||
|
||||
$tagobject = null;
|
||||
if ($tagid) {
|
||||
$tagobject = core_tag_tag::get($tagid, '*', MUST_EXIST);
|
||||
$tagcollid = $tagobject->tagcollid;
|
||||
}
|
||||
$tagcoll = core_tag_collection::get_by_id($tagcollid);
|
||||
$tagarea = core_tag_area::get_by_id($tagareaid);
|
||||
$manageurl = new moodle_url('/tag/manage.php');
|
||||
if ($tagcoll) {
|
||||
// We are inside a tag collection - add it to the page url and the breadcrumb.
|
||||
$PAGE->set_url(new moodle_url($PAGE->url, array('tc' => $tagcoll->id)));
|
||||
$PAGE->navbar->add(core_tag_collection::display_name($tagcoll),
|
||||
new moodle_url($manageurl, array('tc' => $tagcoll->id)));
|
||||
}
|
||||
|
||||
$PAGE->set_blocks_editing_capability('moodle/tag:editblocks');
|
||||
|
||||
switch($action) {
|
||||
|
||||
case 'colladd':
|
||||
case 'colledit':
|
||||
if ($action === 'colladd' || ($action === 'colledit' && $tagcoll && empty($tagcoll->component))) {
|
||||
$form = new core_tag_collection_form($manageurl, $tagcoll);
|
||||
if ($form->is_cancelled()) {
|
||||
redirect($manageurl);
|
||||
} else if ($data = $form->get_data()) {
|
||||
if ($action === 'colladd') {
|
||||
core_tag_collection::create($data);
|
||||
} else {
|
||||
core_tag_collection::update($tagcoll, $data);
|
||||
}
|
||||
redirect($manageurl);
|
||||
} else {
|
||||
$title = ($action === 'colladd') ?
|
||||
get_string('addtagcoll', 'tag') :
|
||||
get_string('edittagcoll', 'tag', core_tag_collection::display_name($tagcoll));
|
||||
$PAGE->navbar->add($title);
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($title, 2);
|
||||
$form->display();
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'colldelete':
|
||||
$confirm = optional_param('confirm', false, PARAM_BOOL);
|
||||
if (!$confirm) {
|
||||
echo $OUTPUT->header();
|
||||
$strconfirm = get_string('suredeletecoll', 'tag', core_tag_collection::display_name($tagcoll));
|
||||
$params = array('tc' => $tagcoll->id, 'confirm' => 1, 'sesskey' => sesskey(), 'action' => 'colldelete');
|
||||
$formcontinue = new single_button(new moodle_url($manageurl, $params), get_string('yes'));
|
||||
$formcancel = new single_button($manageurl, get_string('no'), 'get');
|
||||
echo $OUTPUT->confirm($strconfirm, $formcontinue, $formcancel);
|
||||
echo $OUTPUT->footer();
|
||||
die;
|
||||
}
|
||||
if ($tagcoll && !$tagcoll->component) {
|
||||
require_sesskey();
|
||||
core_tag_collection::delete($tagcoll);
|
||||
redirect(new moodle_url($manageurl, array('notice' => 'changessaved')));
|
||||
}
|
||||
redirect($manageurl);
|
||||
break;
|
||||
|
||||
case 'collmoveup':
|
||||
if ($tagcoll) {
|
||||
require_sesskey();
|
||||
core_tag_collection::change_sortorder($tagcoll, -1);
|
||||
redirect(new moodle_url($manageurl, array('notice' => 'changessaved')));
|
||||
}
|
||||
redirect($manageurl);
|
||||
break;
|
||||
|
||||
case 'collmovedown':
|
||||
if ($tagcoll) {
|
||||
require_sesskey();
|
||||
core_tag_collection::change_sortorder($tagcoll, 1);
|
||||
redirect(new moodle_url($manageurl, array('notice' => 'changessaved')));
|
||||
}
|
||||
redirect($manageurl);
|
||||
break;
|
||||
|
||||
case 'areaenable':
|
||||
case 'areadisable':
|
||||
if ($tagarea) {
|
||||
require_sesskey();
|
||||
$data = array('enabled' => ($action === 'areaenable') ? 1 : 0);
|
||||
core_tag_area::update($tagarea, $data);
|
||||
redirect(new moodle_url($manageurl, array('notice' => 'changessaved')));
|
||||
}
|
||||
redirect($manageurl);
|
||||
break;
|
||||
|
||||
case 'areasetcoll':
|
||||
if ($tagarea) {
|
||||
require_sesskey();
|
||||
if ($newtagcollid = optional_param('areacollid', null, PARAM_INT)) {
|
||||
core_tag_area::update($tagarea, array('tagcollid' => $newtagcollid));
|
||||
redirect(new moodle_url($manageurl, array('notice' => 'changessaved')));
|
||||
}
|
||||
}
|
||||
redirect($manageurl);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
require_sesskey();
|
||||
if (!$tagschecked && $tagid) {
|
||||
$tagschecked = array($tagid);
|
||||
}
|
||||
tag_delete($tagschecked);
|
||||
redirect(new moodle_url($PAGE->url, array('notice' => 'deleted')));
|
||||
core_tag_tag::delete_tags($tagschecked);
|
||||
redirect(new moodle_url($PAGE->url, $tagschecked ? array('notice' => 'deleted') : null));
|
||||
break;
|
||||
|
||||
case 'setflag':
|
||||
require_sesskey();
|
||||
tag_set_flag($tagid);
|
||||
redirect(new moodle_url($PAGE->url, array('notice' => 'flagged')));
|
||||
if ($tagid) {
|
||||
$tagobject->flag();
|
||||
redirect(new moodle_url($PAGE->url, array('notice' => 'flagged')));
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
break;
|
||||
|
||||
case 'resetflag':
|
||||
require_sesskey();
|
||||
tag_unset_flag($tagid);
|
||||
redirect(new moodle_url($PAGE->url, array('notice' => 'resetflag')));
|
||||
if ($tagid) {
|
||||
$tagobject->reset_flag();
|
||||
redirect(new moodle_url($PAGE->url, array('notice' => 'resetflag')));
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
break;
|
||||
|
||||
case 'changetype':
|
||||
require_sesskey();
|
||||
if ($tagtype === 'official' || $tagtype === 'default') {
|
||||
if (tag_type_set($tagid, $tagtype)) {
|
||||
redirect(new moodle_url($PAGE->url, array('notice' => 'typechanged')));
|
||||
}
|
||||
if ($tagid && $tagobject->update(array('tagtype' => $tagtype))) {
|
||||
redirect(new moodle_url($PAGE->url, array('notice' => 'typechanged')));
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
break;
|
||||
|
||||
case 'addofficialtag':
|
||||
require_sesskey();
|
||||
$otagsadd = optional_param('otagsadd', '', PARAM_RAW);
|
||||
$newtags = preg_split('/\s*,\s*/', trim($otagsadd), -1, PREG_SPLIT_NO_EMPTY);
|
||||
$newtags = array_filter(tag_normalize($newtags, TAG_CASE_ORIGINAL));
|
||||
if (!$newtags) {
|
||||
redirect($PAGE->url);
|
||||
$tagobjects = null;
|
||||
if ($tagcoll) {
|
||||
$otagsadd = optional_param('otagsadd', '', PARAM_RAW);
|
||||
$newtags = preg_split('/\s*,\s*/', trim($otagsadd), -1, PREG_SPLIT_NO_EMPTY);
|
||||
$tagobjects = core_tag_tag::create_if_missing($tagcoll->id, $newtags, true);
|
||||
}
|
||||
foreach ($newtags as $newotag) {
|
||||
if ($newotagid = tag_get_id($newotag) ) {
|
||||
// Tag exists, change the type.
|
||||
tag_type_set($newotagid, 'official');
|
||||
} else {
|
||||
tag_add($newotag, 'official');
|
||||
foreach ($tagobjects as $tagobject) {
|
||||
if ($tagobject->tagtype !== 'official') {
|
||||
$tagobject->update(array('tagtype' => 'official'));
|
||||
}
|
||||
}
|
||||
redirect(new moodle_url($PAGE->url, array('notice' => 'added')));
|
||||
redirect(new moodle_url($PAGE->url, $tagobjects ? array('notice' => 'added') : null));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -114,8 +218,28 @@ if ($notice && get_string_manager()->string_exists($notice, 'tag')) {
|
||||
echo $OUTPUT->notification(get_string($notice, 'tag'), 'notifysuccess');
|
||||
}
|
||||
|
||||
if (!$tagcoll) {
|
||||
// Tag collection is not specified. Display the overview of tag collections and tag areas.
|
||||
$tagareastable = new core_tag_areas_table($manageurl);
|
||||
$colltable = new core_tag_collections_table($manageurl);
|
||||
|
||||
echo $OUTPUT->heading(get_string('tagcollections', 'core_tag'), 3);
|
||||
echo html_writer::table($colltable);
|
||||
$url = new moodle_url($manageurl, array('action' => 'colladd'));
|
||||
echo html_writer::div(html_writer::link($url, get_string('addtagcoll', 'tag')), 'mdl-right addtagcoll');
|
||||
|
||||
echo $OUTPUT->heading(get_string('tagareas', 'core_tag'), 3);
|
||||
echo html_writer::table($tagareastable);
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
}
|
||||
|
||||
// Tag collection is specified. Manage tags in this collection.
|
||||
|
||||
// Small form to add an official tag.
|
||||
print('<form class="tag-addtags-form" method="post" action="'.$CFG->wwwroot.'/tag/manage.php">');
|
||||
print('<input type="hidden" name="tc" value="'.$tagcollid.'" />');
|
||||
print('<input type="hidden" name="action" value="addofficialtag" />');
|
||||
print('<input type="hidden" name="perpage" value="'.$perpage.'" />');
|
||||
print('<input type="hidden" name="page" value="'.$page.'" />');
|
||||
@@ -128,18 +252,21 @@ print('<div class="tag-management-form generalbox"><label class="accesshide" for
|
||||
'</div>');
|
||||
print('</form>');
|
||||
|
||||
$table = new core_tag_manage_table();
|
||||
$table = new core_tag_manage_table($tagcollid);
|
||||
echo '<form class="tag-management-form" method="post" action="'.$CFG->wwwroot.'/tag/manage.php">';
|
||||
echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'tc', 'value' => $tagcollid));
|
||||
echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
|
||||
echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'action', 'value' => 'delete'));
|
||||
echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'perpage', 'value' => $perpage));
|
||||
echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'page', 'value' => $page));
|
||||
echo $table->out($perpage, true);
|
||||
|
||||
echo html_writer::start_tag('p');
|
||||
echo html_writer::tag('button', get_string('deleteselected', 'tag'),
|
||||
array('id' => 'tag-management-delete', 'type' => 'submit', 'class' => 'tagdeleteselected'));
|
||||
echo html_writer::end_tag('p');
|
||||
if ($table->rawdata) {
|
||||
echo html_writer::start_tag('p');
|
||||
echo html_writer::tag('button', get_string('deleteselected', 'tag'),
|
||||
array('id' => 'tag-management-delete', 'type' => 'submit', 'class' => 'tagdeleteselected'));
|
||||
echo html_writer::end_tag('p');
|
||||
}
|
||||
echo '</form>';
|
||||
|
||||
$totalcount = $table->totalcount;
|
||||
|
||||
+24
-33
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@@ -23,8 +22,6 @@
|
||||
*/
|
||||
|
||||
require_once('../config.php');
|
||||
require_once('lib.php');
|
||||
require_once('locallib.php');
|
||||
|
||||
require_login();
|
||||
|
||||
@@ -32,47 +29,41 @@ if (empty($CFG->usetags)) {
|
||||
print_error('tagsaredisabled', 'tag');
|
||||
}
|
||||
|
||||
$query = optional_param('query', '', PARAM_RAW);
|
||||
$page = optional_param('page', 0, PARAM_INT); // which page to show
|
||||
$perpage = optional_param('perpage', 18, PARAM_INT);
|
||||
$query = optional_param('query', '', PARAM_RAW);
|
||||
$tagcollid = optional_param('tc', 0, PARAM_INT);
|
||||
$edit = optional_param('edit', -1, PARAM_BOOL);
|
||||
|
||||
$params = array();
|
||||
if ($query !== '') {
|
||||
$params['query'] = $query;
|
||||
}
|
||||
if ($page !== 0) {
|
||||
$params['page'] = $page;
|
||||
}
|
||||
if ($perpage !== 18) {
|
||||
$params['perpage'] = $perpage;
|
||||
if ($tagcollid) {
|
||||
$params['tc'] = $tagcollid;
|
||||
}
|
||||
|
||||
$PAGE->set_url(new moodle_url('/tag/search.php', $params));
|
||||
$PAGE->set_context(context_system::instance());
|
||||
$PAGE->set_pagelayout('standard');
|
||||
|
||||
$systemcontext = context_system::instance();
|
||||
$manage_link = ' ';
|
||||
|
||||
$PAGE->set_title(get_string('tags', 'tag'));
|
||||
$PAGE->set_heading($SITE->fullname);
|
||||
|
||||
$buttons = '';
|
||||
if (has_capability('moodle/tag:manage', context_system::instance())) {
|
||||
$buttons .= $OUTPUT->single_button(new moodle_url('/tag/manage.php'),
|
||||
get_string('managetags', 'tag'), 'GET');
|
||||
}
|
||||
if ($PAGE->user_allowed_editing()) {
|
||||
if ($edit != -1) {
|
||||
$USER->editing = $edit;
|
||||
}
|
||||
$buttons .= $OUTPUT->edit_button(clone($PAGE->url));
|
||||
}
|
||||
$PAGE->set_button($buttons);
|
||||
|
||||
$tagrenderer = $PAGE->get_renderer('core', 'tag');
|
||||
$pagecontents = $tagrenderer->tag_search_page($query, $tagcollid);
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
echo $OUTPUT->heading(get_string('searchtags', 'tag'), 2);
|
||||
|
||||
if ( has_capability('moodle/tag:manage',$systemcontext) ) {
|
||||
echo '<div class="managelink"><a href="'. $CFG->wwwroot .'/tag/manage.php">' . get_string('managetags', 'tag') . '</a></div>' ;
|
||||
}
|
||||
|
||||
tag_print_search_box();
|
||||
|
||||
if(!empty($query)) {
|
||||
tag_print_search_results($query, $page, $perpage);
|
||||
}
|
||||
|
||||
echo '<br/><br/>';
|
||||
|
||||
echo $OUTPUT->box_start('generalbox', 'big-tag-cloud-box');
|
||||
tag_print_cloud(null, 150);
|
||||
echo $OUTPUT->box_end();
|
||||
|
||||
echo $pagecontents;
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_tag/index
|
||||
|
||||
Displays items in one tag area tagged with the specific tag
|
||||
|
||||
Classes required for JS:
|
||||
* tagarea
|
||||
|
||||
Data attributes required for JS:
|
||||
* data-quickload
|
||||
* data-ta
|
||||
|
||||
Context variables required for this template:
|
||||
* none
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"component" : "core",
|
||||
"itemtype" : "course",
|
||||
"nextpageurl" : "http://moodle.org",
|
||||
"prevpageurl" : "http://moodle.org",
|
||||
"exclusiveurl" : "http://moodle.org",
|
||||
"exclusivetext" : "Show all Courses tagged with \"Sometag\"",
|
||||
"title" : "Courses tagged with \"Sometag\"",
|
||||
"content": "Course1, Course2",
|
||||
"hascontent": 1,
|
||||
"anchor": "core_course",
|
||||
"ta": 2,
|
||||
"tagid": 15
|
||||
}
|
||||
|
||||
}}
|
||||
{{#hascontent}}
|
||||
<div class="box generalbox tagarea" id="tagarea-{{component}}-{{itemtype}}" data-ta="{{ta}}">
|
||||
<a name="{{anchor}}"></a>
|
||||
<h3>{{{title}}}</h3>
|
||||
<div class="taggeditems">
|
||||
{{{content}}}
|
||||
</div>
|
||||
<div class="controls controls-bottom">
|
||||
{{#prevpageurl}}
|
||||
<a class="gotopage prevpage" href="{{prevpageurl}}" data-quickload="1">{{#str}}prevpage,tag{{/str}}</a>
|
||||
{{/prevpageurl}}
|
||||
{{#nextpageurl}}
|
||||
<a class="gotopage nextpage" href="{{nextpageurl}}" data-quickload="1">{{#str}}nextpage,tag{{/str}}</a>
|
||||
{{/nextpageurl}}
|
||||
{{#exclusiveurl}}
|
||||
<a class="exclusivemode" href="{{exclusiveurl}}">{{exclusivetext}}</a>
|
||||
{{/exclusiveurl}}
|
||||
</div>
|
||||
</div>
|
||||
{{/hascontent}}
|
||||
@@ -0,0 +1,67 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_tag/tagcloud
|
||||
|
||||
Displays a tag cloud
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* none
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"tags": [
|
||||
{"id":1,"name":"Cats","viewurl":"http://moodle.org/tag/index.php?tag=Cats","tagtype":"official","count":12,"size":20,"flag":0},
|
||||
{"id":1,"name":"Dogs","viewurl":"http://moodle.org/tag/index.php?tag=Dogs","tagtype":"default","size":2,"flag":1},
|
||||
{"id":1,"name":"Mice","viewurl":"http://moodle.org/tag/index.php?tag=Mice","tagtype":"default","count":4,"size":8,"flag":0}
|
||||
],
|
||||
"tagscount": 3,
|
||||
"totalcount": 5,
|
||||
"overflow": 1
|
||||
}
|
||||
|
||||
}}
|
||||
{{#tagscount}}
|
||||
<div class="tag_cloud">
|
||||
<ul class="inline-list">
|
||||
{{#tags}}
|
||||
<li>
|
||||
<a href="{{viewurl}}" class="{{tagtype}} s{{size}}"
|
||||
{{#count}}title="{{#str}}numberofentries, blog, {{count}}{{/str}}{{/count}}">
|
||||
{{#flag}}
|
||||
<span class="flagged-tag">{{name}}</span></a>
|
||||
{{/flag}}
|
||||
{{^flag}}
|
||||
{{name}}</a>
|
||||
{{/flag}}
|
||||
|
||||
</li>
|
||||
{{/tags}}
|
||||
</ul>
|
||||
{{#overflow}}
|
||||
<p class="tag_overflow">
|
||||
{{#str}} showingfirsttags, core_tag, {{tagscount}} {{/str}}
|
||||
</p>
|
||||
{{/overflow}}
|
||||
</div>
|
||||
{{/tagscount}}
|
||||
@@ -0,0 +1,63 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_tag/tagfeed
|
||||
|
||||
Displays list of items tagged with a tag
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* none
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"items": [
|
||||
{"img":"<img class=\"smallicon\" src=\"https://moodle.org/pix/i/siteevent.png\">","heading":"<a href=\"#\">First post</a>","details":"Posted by user 1"},
|
||||
{"img":"<img class=\"smallicon\" src=\"https://moodle.org/pix/i/siteevent.png\">","heading":"<a href=\"#\">Second post without details</a>"},
|
||||
{"heading":"<a href=\"#\">Third post without image</a>","details":"Posted by <a href=\"#\">user 3</a>"}
|
||||
]
|
||||
}
|
||||
|
||||
}}
|
||||
<ul class="tag_feed media-list">
|
||||
{{#items}}
|
||||
<li class="media">
|
||||
{{#img}}
|
||||
<div class="itemimage">
|
||||
{{{img}}}
|
||||
</div>
|
||||
{{/img}}
|
||||
<div class="media-body">
|
||||
{{#heading}}
|
||||
<div class="media-heading">
|
||||
{{{heading}}}
|
||||
</div>
|
||||
{{/heading}}
|
||||
{{#details}}
|
||||
<div class="muted">
|
||||
{{{details}}}
|
||||
</div>
|
||||
{{/details}}
|
||||
</div>
|
||||
</li>
|
||||
{{/items}}
|
||||
</ul>
|
||||
@@ -0,0 +1,85 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_tag/taglist
|
||||
|
||||
Displays a tag list, usually the list of tags some entry is tagged with
|
||||
|
||||
Classes required for JS:
|
||||
* hideoverlimit
|
||||
* tagmorelink
|
||||
* taglesslink
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* none
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"tags": [
|
||||
{"id":1,"name":"Cats","viewurl":"http://moodle.org/tag/index.php?tag=Cats","tagtype":"official","flag":0},
|
||||
{"id":1,"name":"Dogs","viewurl":"http://moodle.org/tag/index.php?tag=Dogs","tagtype":"default","flag":1},
|
||||
{"id":1,"name":"Mice","viewurl":"http://moodle.org/tag/index.php?tag=Mice","tagtype":"default","flag":0}
|
||||
],
|
||||
"label": "Tags",
|
||||
"tagscount": 3,
|
||||
"overflow": 1,
|
||||
"classes": "someadditionalclass"
|
||||
}
|
||||
|
||||
}}
|
||||
{{#tagscount}}
|
||||
<div class="tag_list hideoverlimit {{classes}}">
|
||||
{{#label}}
|
||||
<b>{{label}}:</b>
|
||||
{{/label}}
|
||||
<ul class="inline-list">
|
||||
{{#tags}}
|
||||
<li {{#overlimit}}class="overlimit"{{/overlimit}}>
|
||||
<a href="{{viewurl}}" class="label label-info {{tagtype}}" >
|
||||
{{#flag}}
|
||||
<span class="flagged-tag">{{name}}</span></a>
|
||||
{{/flag}}
|
||||
{{^flag}}
|
||||
{{name}}</a>
|
||||
{{/flag}}
|
||||
</li>
|
||||
{{/tags}}
|
||||
{{#overflow}}
|
||||
<li class="visibleifjs">
|
||||
<a href="#" class="tagmorelink">{{#str}}moretags, core_tag{{/str}}</a>
|
||||
</li>
|
||||
<li class="visibleifjs">
|
||||
<a href="#" class="taglesslink">{{#str}}lesstags, core_tag{{/str}}</a>
|
||||
</li>
|
||||
{{/overflow}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/tagscount}}
|
||||
|
||||
{{#js}}
|
||||
require(['jquery'], function($) {
|
||||
$('.tag_list .tagmorelink').click(function() {
|
||||
$(this).closest('.tag_list').removeClass('hideoverlimit');
|
||||
});
|
||||
$('.tag_list .taglesslink').click(function() {
|
||||
$(this).closest('.tag_list').addClass('hideoverlimit');
|
||||
});
|
||||
});
|
||||
{{/js}}
|
||||
@@ -0,0 +1,135 @@
|
||||
@core @core_tag
|
||||
Feature: Managers can create and manage tag collections
|
||||
In order to use tags effectively
|
||||
As a manager
|
||||
I need to be able to manage tag collections
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| manager1 | Manager | 1 | manager1@example.com |
|
||||
| user1 | User | 1 | user1@example.com |
|
||||
And the following "system role assigns" exist:
|
||||
| user | course | role |
|
||||
| manager1 | Acceptance test site | manager |
|
||||
And the following "tags" exist:
|
||||
| name | tagtype |
|
||||
| Tag0 | official |
|
||||
| Tag1 | official |
|
||||
| Tag2 | official |
|
||||
| Tag3 | official |
|
||||
And I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Add tag collection"
|
||||
And I set the following fields to these values:
|
||||
| Name | Hobbies |
|
||||
And I press "Save changes"
|
||||
|
||||
Scenario: Adding tag collections
|
||||
When I follow "Hobbies"
|
||||
Then I should see "Nothing to display"
|
||||
And I log out
|
||||
|
||||
Scenario: Editing tag collections
|
||||
When I follow "Edit tag collection Hobbies"
|
||||
And I set the following fields to these values:
|
||||
| Name | Newname |
|
||||
And I press "Save changes"
|
||||
Then I should not see "Hobbies"
|
||||
And I should see "Newname"
|
||||
And I log out
|
||||
|
||||
Scenario: Resorting tag collections
|
||||
When I follow "Add tag collection"
|
||||
And I set the following fields to these values:
|
||||
| Name | Blogging |
|
||||
And I press "Save changes"
|
||||
Then "Blogging" "link" should appear after "Hobbies" "link"
|
||||
And I click on "Move up" "link" in the "Blogging" "table_row"
|
||||
And "Blogging" "link" should appear before "Hobbies" "link"
|
||||
And I click on "Move down" "link" in the "Blogging" "table_row"
|
||||
And "Blogging" "link" should appear after "Hobbies" "link"
|
||||
And I log out
|
||||
|
||||
Scenario: Deleting tag collections
|
||||
When I click on "Delete" "link" in the "Hobbies" "table_row"
|
||||
Then I should see "Are you sure you want to delete tag collection \"Hobbies\"?"
|
||||
And I press "Yes"
|
||||
And I should not see "Hobbies"
|
||||
And I log out
|
||||
|
||||
@javascript
|
||||
Scenario: Assigning tag area to tag collection
|
||||
And "User interests" "text" should exist in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Default collection')]" "xpath_element"
|
||||
And "User interests" "text" should not exist in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Hobbies')]" "xpath_element"
|
||||
When I set the field "Change tag collection of area User interests" to "Hobbies"
|
||||
Then "User interests" "text" should not exist in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Default collection')]" "xpath_element"
|
||||
And "User interests" "text" should exist in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Hobbies')]" "xpath_element"
|
||||
And the field "Change tag collection of area User interests" matches value "Hobbies"
|
||||
And I log out
|
||||
|
||||
Scenario: Disabling tag areas
|
||||
And "Change tag collection of area User interests" "select" should exist
|
||||
When I click on "Disable" "link" in the "//table[contains(@class,'tag-areas-table')]//tr[contains(.,'User interests')]" "xpath_element"
|
||||
Then "Change tag collection of area User interests" "select" should not exist
|
||||
And "User interests" "text" should not exist in the "table.tag-collections-table" "css_element"
|
||||
And I click on "Enable" "link" in the "//table[contains(@class,'tag-areas-table')]//tr[contains(.,'User interests')]" "xpath_element"
|
||||
And "User interests" "text" should exist in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Default collection')]" "xpath_element"
|
||||
And "Change tag collection of area User interests" "select" should exist
|
||||
And I log out
|
||||
|
||||
Scenario: Deleting non-empty tag collections
|
||||
When I set the field "Change tag collection of area User interests" to "Hobbies"
|
||||
And I click on "Delete" "link" in the "Hobbies" "table_row"
|
||||
Then I should see "Are you sure you want to delete tag collection \"Hobbies\"?"
|
||||
And I press "Yes"
|
||||
And I should not see "Hobbies"
|
||||
And "User interests" "text" should exist in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Default collection')]" "xpath_element"
|
||||
And I log out
|
||||
|
||||
@javascript
|
||||
Scenario: Moving tags when changing tag collections
|
||||
And I follow "Preferences" in the user menu
|
||||
And I follow "Edit profile"
|
||||
And I expand all fieldsets
|
||||
And I set the field "List of interests" to "Swimming, Tag0, Tag3"
|
||||
And I press "Update profile"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
When I set the field "Change tag collection of area User interests" to "Hobbies"
|
||||
And I follow "Hobbies"
|
||||
Then I should see "Swimming"
|
||||
And I should see "Tag0"
|
||||
And I should see "Tag3"
|
||||
And I should not see "Tag1"
|
||||
And I should not see "Tag2"
|
||||
And I follow "Manage tags"
|
||||
And I follow "Default collection"
|
||||
# Tag "Swimming" was not official and was moved completely.
|
||||
And I should not see "Swimming"
|
||||
# Official tag was not removed.
|
||||
And I should see "Tag0"
|
||||
And I should see "Tag3"
|
||||
And I should see "Tag1"
|
||||
And I should see "Tag2"
|
||||
And I log out
|
||||
|
||||
@javascript
|
||||
Scenario: Creating searchable and non-searchable tag collections
|
||||
And I follow "Add tag collection"
|
||||
And I set the following fields to these values:
|
||||
| Name | Hiddencoll |
|
||||
| Searchable | 0 |
|
||||
And I press "Save changes"
|
||||
And "Yes" "text" should not exist in the "//table[contains(@class,'tag-collections-table')]//tr[contains(.,'Hiddencoll')]" "xpath_element"
|
||||
And I navigate to "Tags" node in "Site pages"
|
||||
Then the "Select tag collection" select box should contain "Default collection"
|
||||
And the "Select tag collection" select box should contain "Hobbies"
|
||||
And the "Select tag collection" select box should not contain "Hiddencoll"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I click on "Edit" "link" in the "Hobbies" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Searchable | 0 |
|
||||
And I press "Save changes"
|
||||
And I navigate to "Tags" node in "Site pages"
|
||||
And "Select tag collection" "select" should not exist
|
||||
And I log out
|
||||
@@ -19,6 +19,7 @@ Feature: Manager is able to delete tags
|
||||
Scenario: Deleting a tag with javascript disabled
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
And I click on "Delete" "link" in the "Dog" "table_row"
|
||||
And I should see "Tag(s) deleted"
|
||||
Then I should not see "Dog"
|
||||
@@ -31,6 +32,7 @@ Feature: Manager is able to delete tags
|
||||
Scenario: Deleting multiple tags with javascript disabled
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
And I set the following fields to these values:
|
||||
| Select tag Dog | 1 |
|
||||
| Select tag Neverusedtag | 1 |
|
||||
@@ -48,6 +50,7 @@ Feature: Manager is able to delete tags
|
||||
Scenario: Deleting a tag with javascript enabled
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
And I click on "Delete" "link" in the "Turtle" "table_row"
|
||||
Then I should see "Are you sure you want to delete this tag?"
|
||||
And I press "No"
|
||||
@@ -58,7 +61,7 @@ Feature: Manager is able to delete tags
|
||||
And I press "Yes"
|
||||
And I should see "Tag(s) deleted"
|
||||
And I should not see "Dog"
|
||||
And I follow "Manage tags"
|
||||
And I follow "Default collection"
|
||||
And I should not see "Dog"
|
||||
And I navigate to "Participants" node in "Site pages"
|
||||
And I follow "User 1"
|
||||
@@ -70,6 +73,7 @@ Feature: Manager is able to delete tags
|
||||
Scenario: Deleting multiple tags with javascript enabled
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
And I press "Delete selected"
|
||||
And I should not see "Are you sure"
|
||||
And I should not see "Tag(s) deleted"
|
||||
@@ -90,7 +94,7 @@ Feature: Manager is able to delete tags
|
||||
And I should see "Tag(s) deleted"
|
||||
And I should not see "Dog"
|
||||
And I should not see "Neverusedtag"
|
||||
And I follow "Manage tags"
|
||||
And I follow "Default collection"
|
||||
And I should not see "Dog"
|
||||
And I should not see "Neverusedtag"
|
||||
And I navigate to "Participants" node in "Site pages"
|
||||
|
||||
@@ -42,8 +42,11 @@ Feature: Users can edit tags to add description or rename
|
||||
| Related tags | Dog, Turtle,Fish |
|
||||
And I press "Update"
|
||||
Then "Cat" "text" should exist in the ".breadcrumb-nav" "css_element"
|
||||
And "Description of tag 1" "text" should exist in the "#tag-description" "css_element"
|
||||
And I should see "Related tags: Dog, Turtle, Fish"
|
||||
And "Description of tag 1" "text" should exist in the ".tag-description" "css_element"
|
||||
And I should see "Related tags:" in the ".tag_list" "css_element"
|
||||
And I should see "Dog" in the ".tag_list" "css_element"
|
||||
And I should see "Turtle" in the ".tag_list" "css_element"
|
||||
And I should see "Fish" in the ".tag_list" "css_element"
|
||||
And I log out
|
||||
|
||||
@javascript
|
||||
@@ -60,14 +63,19 @@ Feature: Users can edit tags to add description or rename
|
||||
| Official | 0 |
|
||||
And I press "Update"
|
||||
Then "Kitten" "text" should exist in the ".breadcrumb-nav" "css_element"
|
||||
And "Description of tag 1" "text" should exist in the "#tag-description" "css_element"
|
||||
And I should see "Related tags: Dog, Turtle, Fish"
|
||||
And "Description of tag 1" "text" should exist in the ".tag-description" "css_element"
|
||||
And I should see "Related tags:" in the ".tag_list" "css_element"
|
||||
And I should see "Dog" in the ".tag_list" "css_element"
|
||||
And I should see "Turtle" in the ".tag_list" "css_element"
|
||||
And I should see "Fish" in the ".tag_list" "css_element"
|
||||
And I follow "Edit this tag"
|
||||
And I click on "× Dog" "text"
|
||||
And I press "Update"
|
||||
Then "Kitten" "text" should exist in the ".breadcrumb-nav" "css_element"
|
||||
And "Description of tag 1" "text" should exist in the "#tag-description" "css_element"
|
||||
And I should see "Related tags: Turtle, Fish"
|
||||
And "Description of tag 1" "text" should exist in the ".tag-description" "css_element"
|
||||
And I should see "Related tags:" in the ".tag_list" "css_element"
|
||||
And I should see "Turtle" in the ".tag_list" "css_element"
|
||||
And I should see "Fish" in the ".tag_list" "css_element"
|
||||
And I should not see "Dog"
|
||||
And I log out
|
||||
|
||||
@@ -80,7 +88,7 @@ Feature: Users can edit tags to add description or rename
|
||||
And I set the following fields to these values:
|
||||
| Tag name | DOG |
|
||||
And I press "Update"
|
||||
And I should see "DOG: Tag names already being used"
|
||||
And I should see "Tag names already being used"
|
||||
And I set the following fields to these values:
|
||||
| Tag name | Kitten |
|
||||
And I press "Update"
|
||||
@@ -96,6 +104,7 @@ Feature: Users can edit tags to add description or rename
|
||||
Scenario: Manager can change tag description and rename the tag from tag manage page
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
And I click on "Edit this tag" "link" in the "Cat" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Tag name | Kitten |
|
||||
@@ -103,29 +112,33 @@ Feature: Users can edit tags to add description or rename
|
||||
| Related tags | Dog, Turtle,Fish |
|
||||
| Official | 0 |
|
||||
And I press "Update"
|
||||
Then "Manage tags" "link" should exist in the ".breadcrumb-nav" "css_element"
|
||||
Then "Default collection" "link" should exist in the ".breadcrumb-nav" "css_element"
|
||||
And I follow "Kitten"
|
||||
And "Description of tag 1" "text" should exist in the "#tag-description" "css_element"
|
||||
And I should see "Related tags: Dog, Turtle, Fish"
|
||||
And "Description of tag 1" "text" should exist in the ".tag-description" "css_element"
|
||||
And I should see "Related tags:" in the ".tag_list" "css_element"
|
||||
And I should see "Dog" in the ".tag_list" "css_element"
|
||||
And I should see "Turtle" in the ".tag_list" "css_element"
|
||||
And I should see "Fish" in the ".tag_list" "css_element"
|
||||
And I log out
|
||||
|
||||
Scenario: Renaming the tag in edit tag form from tag manage page
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
And I click on "Edit this tag" "link" in the "Cat" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Tag name | DOG |
|
||||
And I press "Update"
|
||||
And I should see "DOG: Tag names already being used"
|
||||
And I should see "Tag names already being used"
|
||||
And I set the following fields to these values:
|
||||
| Tag name | Kitten |
|
||||
And I press "Update"
|
||||
Then "Manage tags" "text" should exist in the ".breadcrumb-nav" "css_element"
|
||||
Then "Default collection" "text" should exist in the ".breadcrumb-nav" "css_element"
|
||||
And I click on "Edit this tag" "link" in the "Kitten" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Tag name | KITTEN |
|
||||
And I press "Update"
|
||||
And "Manage tags" "text" should exist in the ".breadcrumb-nav" "css_element"
|
||||
And "Default collection" "text" should exist in the ".breadcrumb-nav" "css_element"
|
||||
And I should see "KITTEN"
|
||||
And I should not see "Kitten"
|
||||
And I log out
|
||||
@@ -134,6 +147,7 @@ Feature: Users can edit tags to add description or rename
|
||||
Scenario: Renaming the tag using quick edit field on tag manage page
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
# Renaming tag to a valid name
|
||||
And I click on "Edit tag name" "link" in the "Cat" "table_row"
|
||||
And I set the field "New name for tag Cat" to "Kitten"
|
||||
@@ -141,7 +155,7 @@ Feature: Users can edit tags to add description or rename
|
||||
Then I should not see "Cat"
|
||||
And "New name for tag" "field" should not be visible
|
||||
And I wait until "Kitten" "link" exists
|
||||
And I follow "Manage tags"
|
||||
And I follow "Default collection"
|
||||
And I should see "Kitten"
|
||||
And I should not see "Cat"
|
||||
# Renaming tag to an invalid name
|
||||
@@ -154,7 +168,7 @@ Feature: Users can edit tags to add description or rename
|
||||
And I should see "Turtle"
|
||||
And I should see "Dog"
|
||||
And I should not see "DOG"
|
||||
And I follow "Manage tags"
|
||||
And I follow "Default collection"
|
||||
And I should see "Turtle"
|
||||
And I should see "Dog"
|
||||
And I should not see "DOG"
|
||||
@@ -165,7 +179,7 @@ Feature: Users can edit tags to add description or rename
|
||||
And "New name for tag" "field" should not be visible
|
||||
And I should see "Turtle"
|
||||
And I should not see "Penguin"
|
||||
And I follow "Manage tags"
|
||||
And I follow "Default collection"
|
||||
And I should see "Turtle"
|
||||
And I should not see "Penguin"
|
||||
And I log out
|
||||
|
||||
@@ -49,6 +49,7 @@ Feature: Users can flag tags and manager can reset flags
|
||||
Scenario: Managing tag flags with javascript disabled
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
Then "Sweartag" "link" should appear before "Badtag" "link"
|
||||
And "Badtag" "link" should appear before "Nicetag" "link"
|
||||
And "(2)" "text" should exist in the "//tr[contains(.,'Sweartag')]//td[contains(@class,'col-flag')]" "xpath_element"
|
||||
@@ -69,6 +70,7 @@ Feature: Users can flag tags and manager can reset flags
|
||||
Scenario: Managing tag flags with javascript enabled
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
Then "Sweartag" "link" should appear before "Badtag" "link"
|
||||
And "Badtag" "link" should appear before "Nicetag" "link"
|
||||
And "(2)" "text" should exist in the "//tr[contains(.,'Sweartag')]//td[contains(@class,'col-flag')]" "xpath_element"
|
||||
@@ -84,7 +86,7 @@ Feature: Users can flag tags and manager can reset flags
|
||||
And "(1)" "text" should exist in the "//tr[contains(.,'Nicetag')]//td[contains(@class,'col-flag')]" "xpath_element"
|
||||
And "(" "text" should not exist in the "//tr[contains(.,'Badtag')]//td[contains(@class,'col-flag')]" "xpath_element"
|
||||
And "(" "text" should not exist in the "//tr[contains(.,'Neverusedtag')]//td[contains(@class,'col-flag')]" "xpath_element"
|
||||
And I follow "Manage tags"
|
||||
And I follow "Default collection"
|
||||
And "Nicetag" "link" should appear before "Sweartag" "link"
|
||||
And "Sweartag" "link" should appear before "Badtag" "link"
|
||||
And "(1)" "text" should exist in the "//tr[contains(.,'Sweartag')]//td[contains(@class,'col-flag')]" "xpath_element"
|
||||
|
||||
@@ -22,6 +22,7 @@ Feature: Manager can add official tags and change the tag type of existing tags
|
||||
Scenario: Adding official tags
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
Then "Make official" "link" should exist in the "Tag0" "table_row"
|
||||
And "Make official" "link" should exist in the "Tag1" "table_row"
|
||||
And "Make official" "link" should exist in the "Tag2" "table_row"
|
||||
@@ -47,6 +48,7 @@ Feature: Manager can add official tags and change the tag type of existing tags
|
||||
Scenario: Changing tag type with javascript disabled
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
And I click on "Make official" "link" in the "Tag0" "table_row"
|
||||
And I should see "Tag type changed"
|
||||
And I click on "Make official" "link" in the "Tag1" "table_row"
|
||||
@@ -65,6 +67,7 @@ Feature: Manager can add official tags and change the tag type of existing tags
|
||||
Scenario: Changing tag type with javascript enabled
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
And I click on "Make official" "link" in the "Tag0" "table_row"
|
||||
And I click on "Make official" "link" in the "Tag1" "table_row"
|
||||
And I wait until "//tr[contains(.,'Tag0')]//a[contains(@title,'Remove from official tags')]" "xpath_element" exists
|
||||
@@ -77,7 +80,7 @@ Feature: Manager can add official tags and change the tag type of existing tags
|
||||
And "Remove from official tags" "link" should exist in the "Tag1" "table_row"
|
||||
And "Make official" "link" should exist in the "Tag2" "table_row"
|
||||
And "Make official" "link" should exist in the "Tag3" "table_row"
|
||||
And I follow "Manage tags"
|
||||
And I follow "Default collection"
|
||||
And "Make official" "link" should exist in the "Tag0" "table_row"
|
||||
And "Remove from official tags" "link" should exist in the "Tag1" "table_row"
|
||||
And "Make official" "link" should exist in the "Tag2" "table_row"
|
||||
@@ -87,6 +90,7 @@ Feature: Manager can add official tags and change the tag type of existing tags
|
||||
Scenario: Changing tag type in edit form
|
||||
When I log in as "manager1"
|
||||
And I navigate to "Manage tags" node in "Site administration > Appearance"
|
||||
And I follow "Default collection"
|
||||
And I click on "Edit this tag" "link" in the "Tag1" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Official | 1 |
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
@core @core_tag
|
||||
Feature: Browsing tagged items
|
||||
In order to search by tag
|
||||
As a user
|
||||
I need to be able to browse tagged items
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email | interests |
|
||||
| user1 | User | 1 | user1@example.com | Cat |
|
||||
| user2 | User | 2 | user1@example.com | Cat, Dog |
|
||||
| user3 | User | 3 | user1@example.com | Dog |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | tags |
|
||||
| Course 1 | c1 | Cat, Dog |
|
||||
| Course 2 | c2 | Cat |
|
||||
| Course 3 | c3 | Cat |
|
||||
| Course 4 | c4 | Cat |
|
||||
| Course 5 | c5 | Cat |
|
||||
| Course 6 | c6 | Cat |
|
||||
| Course 7 | c7 | Cat |
|
||||
|
||||
Scenario: Browse tag index with javascript disabled
|
||||
When I log in as "user1"
|
||||
And I navigate to "Tags" node in "Site pages"
|
||||
And I follow "Cat"
|
||||
Then I should see "Courses" in the ".tag-index-items h3" "css_element"
|
||||
And I should see "User 1" in the "#tagarea-core-user" "css_element"
|
||||
And I should see "Course 7"
|
||||
And I should see "Course 3"
|
||||
And I should not see "Course 2"
|
||||
And I should not see "Course 1"
|
||||
And I should see "User 1" in the "#tagarea-core-user" "css_element"
|
||||
And I should see "User 2"
|
||||
And I should not see "User 3"
|
||||
And I click on "More" "link" in the "#tagarea-core-course" "css_element"
|
||||
And I should see "Courses" in the "#tagarea-core-course" "css_element"
|
||||
And "#tagarea-core-user" "css_element" should not exist
|
||||
And I should not see "Course 7"
|
||||
And I should not see "Course 3"
|
||||
And I should see "Course 2"
|
||||
And I should see "Course 1"
|
||||
And I click on "Back" "link" in the "#tagarea-core-course" "css_element"
|
||||
And I should see "Courses" in the ".tag-index-items h3" "css_element"
|
||||
And "#tagarea-core-user" "css_element" should not exist
|
||||
And I should see "Course 7"
|
||||
And I should see "Course 3"
|
||||
And I should not see "Course 2"
|
||||
And I should not see "Course 1"
|
||||
And I follow "Show only tagged Courses"
|
||||
And I should see "Courses tagged with \"Cat\""
|
||||
And "#tagarea-core-user" "css_element" should not exist
|
||||
And I should see "Course 7"
|
||||
And I should see "Course 3"
|
||||
And I should see "Course 2"
|
||||
And I should see "Course 1"
|
||||
And I follow "Back to all items tagged with \"Cat\""
|
||||
And I should see "Courses" in the "#tagarea-core-course" "css_element"
|
||||
And I should see "User interests" in the "#tagarea-core-user" "css_element"
|
||||
And I should see "Course 7"
|
||||
And I should see "Course 3"
|
||||
And I should not see "Course2"
|
||||
And I should not see "Course1"
|
||||
And I log out
|
||||
|
||||
@javascript
|
||||
Scenario: Browse tag index with javascript enabled
|
||||
When I log in as "user1"
|
||||
And I navigate to "Tags" node in "Site pages"
|
||||
And I follow "Cat"
|
||||
Then I should see "Courses" in the "#tagarea-core-course" "css_element"
|
||||
And I should see "User interests" in the "#tagarea-core-user" "css_element"
|
||||
And I should see "Course 7"
|
||||
And I should see "Course 3"
|
||||
And I should not see "Course 2"
|
||||
And I should not see "Course 1"
|
||||
And I click on "More" "link" in the "#tagarea-core-course" "css_element"
|
||||
And I should see "Courses" in the "#tagarea-core-course" "css_element"
|
||||
And I should see "User interests" in the "#tagarea-core-user" "css_element"
|
||||
And I should not see "Course 7"
|
||||
And I should not see "Course 3"
|
||||
And I should see "Course 2"
|
||||
And I should see "Course 1"
|
||||
And I click on "Back" "link" in the "#tagarea-core-course" "css_element"
|
||||
And I should see "Courses" in the "#tagarea-core-course" "css_element"
|
||||
And I should see "User interests" in the "#tagarea-core-user" "css_element"
|
||||
And I should see "Course 7"
|
||||
And I should see "Course 3"
|
||||
And I should not see "Course 2"
|
||||
And I should not see "Course 1"
|
||||
And I follow "Show only tagged Courses"
|
||||
And I should see "Courses" in the "#tagarea-core-course" "css_element"
|
||||
And "#tagarea-core-user" "css_element" should not exist
|
||||
And I should see "Course 7"
|
||||
And I should see "Course 3"
|
||||
And I should see "Course 2"
|
||||
And I should see "Course 1"
|
||||
And I follow "Back to all items tagged with \"Cat\""
|
||||
And I should see "Courses" in the "#tagarea-core-course" "css_element"
|
||||
And I should see "User interests" in the "#tagarea-core-user" "css_element"
|
||||
And I should see "Course 7"
|
||||
And I should see "Course 3"
|
||||
And I should not see "Course2"
|
||||
And I should not see "Course1"
|
||||
And I log out
|
||||
+96
-12
@@ -27,7 +27,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/tag/lib.php');
|
||||
|
||||
// Used to create a wiki page to tag.
|
||||
require_once($CFG->dirroot . '/mod/wiki/locallib.php');
|
||||
@@ -61,6 +60,7 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
// Trigger and capture the event when renaming a tag.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_rename($tag->id, 'newname');
|
||||
$this->assertDebuggingCalled();
|
||||
// Update the tag's name since we have renamed it.
|
||||
$tag->name = 'newname';
|
||||
$events = $sink->get_events();
|
||||
@@ -75,6 +75,7 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
// Trigger and capture the event when setting the type of a tag.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_type_set($tag->id, 'official');
|
||||
$this->assertDebuggingCalled();
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
@@ -87,6 +88,7 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
// Trigger and capture the event for setting the description of a tag.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_description_set($tag->id, 'description', FORMAT_MOODLE);
|
||||
$this->assertDebuggingCalled();
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
@@ -108,7 +110,7 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
|
||||
// Trigger and capture the event for tagging a course.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_set('course', $course->id, array('A tag'), 'core', context_course::instance($course->id)->id);
|
||||
core_tag_tag::set_item_tags('core', 'course', $course->id, context_course::instance($course->id), array('A tag'));
|
||||
$events = $sink->get_events();
|
||||
$event = $events[1];
|
||||
|
||||
@@ -127,7 +129,8 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
// Trigger and capture the event for tagging a question.
|
||||
$this->assertEquals(1, $DB->count_records('tag_instance'));
|
||||
$sink = $this->redirectEvents();
|
||||
tag_set('question', $question->id, array('A tag'), 'core_question', $cat->contextid);
|
||||
core_tag_tag::set_item_tags('core_question', 'question', $question->id,
|
||||
context::instance_by_id($cat->contextid), array('A tag'));
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
@@ -161,6 +164,7 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
|
||||
// Assign a tag to a course.
|
||||
tag_assign('course', $course->id, $tag->id, 1, 2, 'core', context_course::instance($course->id)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Trigger and capture the event for untagging a course.
|
||||
$sink = $this->redirectEvents();
|
||||
@@ -179,10 +183,12 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
|
||||
// Assign a tag to a wiki this time.
|
||||
tag_assign('wiki_pages', $wikipageid, $tag->id, 1, 2, 'mod_wiki', context_module::instance($wiki->cmid)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Trigger and capture the event for deleting this tag instance.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_delete_instance('wiki_pages', $wikipageid, $tag->id);
|
||||
$this->assertDebuggingCalled();
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
@@ -196,11 +202,13 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
|
||||
// Assign a tag to the wiki again.
|
||||
tag_assign('wiki_pages', $wikipageid, $tag->id, 1, 2, 'mod_wiki', context_module::instance($wiki->cmid)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Now we want to delete this tag, and because there is only one tag instance
|
||||
// associated with it, it should get deleted as well.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_delete($tag->id);
|
||||
$this->assertDebuggingCalled();
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
@@ -214,10 +222,11 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
|
||||
// Assign a tag to the wiki again.
|
||||
tag_assign('wiki_pages', $wikipageid, $tag->id, 1, 2, 'mod_wiki', context_module::instance($wiki->cmid)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Delete all tag instances for this wiki instance.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_delete_instances('mod_wiki', context_module::instance($wiki->cmid)->id);
|
||||
core_tag_tag::delete_instances('mod_wiki', 'wiki_pages', context_module::instance($wiki->cmid)->id);
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
@@ -233,11 +242,13 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
|
||||
// Assign a tag to both wiki pages.
|
||||
tag_assign('wiki_pages', $wikipageid, $tag->id, 1, 2, 'mod_wiki', context_module::instance($wiki->cmid)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
tag_assign('wiki_pages', $wikipageid2, $tag->id, 1, 2, 'mod_wiki', context_module::instance($wiki2->cmid)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Now remove all tag_instances associated with all wikis.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_delete_instances('mod_wiki');
|
||||
core_tag_tag::delete_instances('mod_wiki');
|
||||
$events = $sink->get_events();
|
||||
|
||||
// There will be two events - one for each wiki instance removed.
|
||||
@@ -271,6 +282,7 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
// Trigger and capture the event for setting the flag of a tag.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_set_flag($tag->id);
|
||||
$this->assertDebuggingCalled();
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
@@ -286,10 +298,12 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
|
||||
// Unset the flag for both (though by default tag2 should have been created with 0 already).
|
||||
tag_unset_flag(array($tag->id, $tag2->id));
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Trigger and capture the event for setting the flag for multiple tags.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_set_flag(array($tag->id, $tag2->id));
|
||||
$this->assertDebuggingCalled();
|
||||
$events = $sink->get_events();
|
||||
|
||||
// Check that the flags were updated.
|
||||
@@ -326,10 +340,12 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
|
||||
// Flag it.
|
||||
tag_set_flag($tag->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Trigger and capture the event for unsetting the flag of a tag.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_unset_flag($tag->id);
|
||||
$this->assertDebuggingCalled();
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
@@ -343,10 +359,12 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
|
||||
// Set the flag back for both.
|
||||
tag_set_flag(array($tag->id, $tag2->id));
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Trigger and capture the event for unsetting the flag for multiple tags.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_unset_flag(array($tag->id, $tag2->id));
|
||||
$this->assertDebuggingCalled();
|
||||
$events = $sink->get_events();
|
||||
|
||||
// Check that the flags were updated.
|
||||
@@ -366,15 +384,16 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the tag deleted event.
|
||||
* Test the tag deleted event
|
||||
*/
|
||||
public function test_tag_deleted() {
|
||||
global $DB;
|
||||
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a course.
|
||||
// Create a course and a user.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Create tag we are going to delete.
|
||||
$tag = $this->getDataGenerator()->create_tag();
|
||||
@@ -382,6 +401,7 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
// Trigger and capture the event for deleting a tag.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_delete($tag->id);
|
||||
$this->assertDebuggingCalled();
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
@@ -397,6 +417,7 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
// Trigger and capture the events for deleting multiple tags.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_delete(array($tag->id, $tag2->id));
|
||||
$this->assertDebuggingCalled();
|
||||
$events = $sink->get_events();
|
||||
|
||||
// Check that the tags were deleted and the events data is valid.
|
||||
@@ -406,11 +427,70 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
$this->assertEquals(context_system::instance(), $event->get_context());
|
||||
}
|
||||
|
||||
// Add a tag instance to a course.
|
||||
core_tag_tag::add_item_tag('core', 'course', $course->id, context_course::instance($course->id), 'cat', $user->id);
|
||||
|
||||
// Trigger and capture the event for deleting a personal tag for a user for a course.
|
||||
$sink = $this->redirectEvents();
|
||||
core_tag_tag::remove_item_tag('core', 'course', $course->id, 'cat', $user->id);
|
||||
$events = $sink->get_events();
|
||||
$event = $events[1];
|
||||
|
||||
// Check that the tag was deleted and the event data is valid.
|
||||
$this->assertEquals(0, $DB->count_records('tag'));
|
||||
$this->assertInstanceOf('\core\event\tag_deleted', $event);
|
||||
$this->assertEquals(context_system::instance(), $event->get_context());
|
||||
|
||||
// Add the tag instance to the course again as it was deleted.
|
||||
core_tag_tag::add_item_tag('core', 'course', $course->id, context_course::instance($course->id), 'dog', $user->id);
|
||||
|
||||
// Trigger and capture the event for deleting all tags in a course.
|
||||
$sink = $this->redirectEvents();
|
||||
core_tag_tag::remove_all_item_tags('core', 'course', $course->id);
|
||||
$events = $sink->get_events();
|
||||
$event = $events[1];
|
||||
|
||||
// Check that the tag was deleted and the event data is valid.
|
||||
$this->assertEquals(0, $DB->count_records('tag'));
|
||||
$this->assertInstanceOf('\core\event\tag_deleted', $event);
|
||||
$this->assertEquals(context_system::instance(), $event->get_context());
|
||||
|
||||
// Add multiple tag instances now and check that it still works.
|
||||
core_tag_tag::set_item_tags('core', 'course', $course->id, context_course::instance($course->id),
|
||||
array('fish', 'hamster'), $user->id);
|
||||
|
||||
// Trigger and capture the event for deleting all tags in a course.
|
||||
$sink = $this->redirectEvents();
|
||||
core_tag_tag::remove_all_item_tags('core', 'course', $course->id);
|
||||
$events = $sink->get_events();
|
||||
$events = array($events[1], $events[3]);
|
||||
|
||||
// Check that the tags were deleted and the events data is valid.
|
||||
$this->assertEquals(0, $DB->count_records('tag'));
|
||||
foreach ($events as $event) {
|
||||
$this->assertInstanceOf('\core\event\tag_deleted', $event);
|
||||
$this->assertEquals(context_system::instance(), $event->get_context());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the tag deleted event while calling deprecated functions.
|
||||
* Remove the test when the functions are removed.
|
||||
*/
|
||||
public function test_tag_deleted_deprecated() {
|
||||
global $DB;
|
||||
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a course.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
// Create another tag to delete.
|
||||
$tag = $this->getDataGenerator()->create_tag();
|
||||
|
||||
// Add a tag instance to a course.
|
||||
tag_assign('course', $course->id, $tag->id, 0, 2, 'course', context_course::instance($course->id)->id);
|
||||
tag_assign('course', $course->id, $tag->id, 0, 2, 'core', context_course::instance($course->id)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Trigger and capture the event for deleting a personal tag for a user for a course.
|
||||
$sink = $this->redirectEvents();
|
||||
@@ -428,7 +508,8 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
$tag = $this->getDataGenerator()->create_tag();
|
||||
|
||||
// Add the tag instance to the course again as it was deleted.
|
||||
tag_assign('course', $course->id, $tag->id, 0, 2, 'course', context_course::instance($course->id)->id);
|
||||
tag_assign('course', $course->id, $tag->id, 0, 2, 'core', context_course::instance($course->id)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Trigger and capture the event for deleting all tags in a course.
|
||||
$sink = $this->redirectEvents();
|
||||
@@ -447,8 +528,10 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
$tag2 = $this->getDataGenerator()->create_tag();
|
||||
|
||||
// Add multiple tag instances now and check that it still works.
|
||||
tag_assign('course', $course->id, $tag->id, 0, 2, 'course', context_course::instance($course->id)->id);
|
||||
tag_assign('course', $course->id, $tag2->id, 0, 2, 'course', context_course::instance($course->id)->id);
|
||||
tag_assign('course', $course->id, $tag->id, 0, 2, 'core', context_course::instance($course->id)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
tag_assign('course', $course->id, $tag2->id, 0, 2, 'core', context_course::instance($course->id)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Trigger and capture the event for deleting all tags in a course.
|
||||
$sink = $this->redirectEvents();
|
||||
@@ -473,7 +556,8 @@ class core_tag_events_testcase extends advanced_testcase {
|
||||
|
||||
// Trigger and capture the event for creating a tag.
|
||||
$sink = $this->redirectEvents();
|
||||
tag_add('A really awesome tag!');
|
||||
core_tag_tag::create_if_missing(core_tag_area::get_collection('core', 'course'),
|
||||
array('A really awesome tag!'));
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/tag/lib.php');
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
|
||||
|
||||
+510
-76
@@ -27,8 +27,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/tag/lib.php');
|
||||
|
||||
class core_tag_taglib_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
@@ -42,8 +40,9 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test the tag_set function.
|
||||
* This function was deprecated in 3.1
|
||||
*/
|
||||
public function test_tag_set() {
|
||||
public function test_tag_set_get() {
|
||||
global $DB;
|
||||
|
||||
// Create a course to tag.
|
||||
@@ -51,20 +50,45 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
|
||||
// Create the tag and tag instance.
|
||||
tag_set('course', $course->id, array('A random tag'), 'core', context_course::instance($course->id)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Get the tag instance that should have been created.
|
||||
$taginstance = $DB->get_record('tag_instance', array('itemtype' => 'course', 'itemid' => $course->id), '*', MUST_EXIST);
|
||||
$this->assertEquals('core', $taginstance->component);
|
||||
$this->assertEquals(context_course::instance($course->id)->id, $taginstance->contextid);
|
||||
|
||||
// Now call the tag_set function without specifying the component or contextid and
|
||||
// ensure the function debugging is called.
|
||||
tag_set('course', $course->id, array('Another tag'));
|
||||
$tagbyname = tag_get('name', 'A random tag');
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertEquals('A random tag', $tagbyname->rawname);
|
||||
|
||||
$this->assertEmpty(tag_get('name', 'Non existing tag'));
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
$tagbyid = tag_get('id', $tagbyname->id);
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertEquals('A random tag', $tagbyid->rawname);
|
||||
$tagid = $tagbyname->id;
|
||||
|
||||
$this->assertEmpty(tag_get('id', $tagid + 1));
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
tag_set('tag', $tagid, array('Some related tag'));
|
||||
$this->assertDebuggingCalled();
|
||||
$relatedtags = tag_get_related_tags($tagid);
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertCount(1, $relatedtags);
|
||||
$this->assertEquals('Some related tag', $relatedtags[0]->rawname);
|
||||
|
||||
$tagids = tag_get_id(array('A random tag', 'Some related tag'));
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertCount(2, $tagids);
|
||||
$this->assertEquals($tagid, $tagids['a random tag']);
|
||||
$this->assertEquals($relatedtags[0]->id, $tagids['some related tag']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the tag_set_add function.
|
||||
* This function was deprecated in 3.1
|
||||
*/
|
||||
public function test_tag_set_add() {
|
||||
global $DB;
|
||||
@@ -74,24 +98,25 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
|
||||
// Create the tag and tag instance.
|
||||
tag_set_add('course', $course->id, 'A random tag', 'core', context_course::instance($course->id)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Get the tag instance that should have been created.
|
||||
$taginstance = $DB->get_record('tag_instance', array('itemtype' => 'course', 'itemid' => $course->id), '*', MUST_EXIST);
|
||||
$this->assertEquals('core', $taginstance->component);
|
||||
$this->assertEquals(context_course::instance($course->id)->id, $taginstance->contextid);
|
||||
$tagid = $taginstance->tagid;
|
||||
|
||||
// Remove the tag we just created.
|
||||
$tag = $DB->get_record('tag', array('rawname' => 'A random tag'));
|
||||
tag_delete($tag->id);
|
||||
|
||||
// Now call the tag_set_add function without specifying the component or
|
||||
// contextid and ensure the function debugging is called.
|
||||
tag_set_add('course', $course->id, 'Another tag');
|
||||
tag_set_add('tag', $tagid, 'Some related tag');
|
||||
$this->assertDebuggingCalled();
|
||||
$relatedtags = tag_get_related_tags($tagid);
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertCount(1, $relatedtags);
|
||||
$this->assertEquals('Some related tag', $relatedtags[0]->rawname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the tag_set_delete function.
|
||||
* This function was deprecated in 3.1
|
||||
*/
|
||||
public function test_tag_set_delete() {
|
||||
global $DB;
|
||||
@@ -101,25 +126,56 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
|
||||
// Create the tag and tag instance we are going to delete.
|
||||
tag_set_add('course', $course->id, 'A random tag', 'core', context_course::instance($course->id)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Call the tag_set_delete function.
|
||||
tag_set_delete('course', $course->id, 'a random tag', 'core', context_course::instance($course->id)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Now check that there are no tags or tag instances.
|
||||
$this->assertEquals(0, $DB->count_records('tag'));
|
||||
$this->assertEquals(0, $DB->count_records('tag_instance'));
|
||||
|
||||
// Recreate the tag and tag instance.
|
||||
// Add tag again, add and remove related tag.
|
||||
tag_set_add('course', $course->id, 'A random tag', 'core', context_course::instance($course->id)->id);
|
||||
|
||||
// Now call the tag_set_delete function without specifying the component or
|
||||
// contextid and ensure the function debugging is called.
|
||||
tag_set_delete('course', $course->id, 'A random tag');
|
||||
$this->assertDebuggingCalled();
|
||||
$taginstance = $DB->get_record('tag_instance', array('itemtype' => 'course', 'itemid' => $course->id), '*', MUST_EXIST);
|
||||
$tagid = $taginstance->tagid;
|
||||
tag_set_add('tag', $tagid, 'Some related tag');
|
||||
$this->assertDebuggingCalled();
|
||||
tag_set_delete('tag', $tagid, 'Some related tag');
|
||||
$this->assertDebuggingCalled();
|
||||
$relatedtags = tag_get_related_tags($tagid);
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertCount(0, $relatedtags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the core_tag_tag::add_item_tag() and core_tag_tag::remove_item_tag() functions.
|
||||
*/
|
||||
public function test_add_remove_item_tag() {
|
||||
global $DB;
|
||||
|
||||
// Create a course to tag.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
// Create the tag and tag instance we are going to delete.
|
||||
core_tag_tag::add_item_tag('core', 'course', $course->id, context_course::instance($course->id), 'A random tag');
|
||||
|
||||
$this->assertEquals(1, $DB->count_records('tag'));
|
||||
$this->assertEquals(1, $DB->count_records('tag_instance'));
|
||||
|
||||
// Call the tag_set_delete function.
|
||||
core_tag_tag::remove_item_tag('core', 'course', $course->id, 'A random tag');
|
||||
|
||||
// Now check that there are no tags or tag instances.
|
||||
$this->assertEquals(0, $DB->count_records('tag'));
|
||||
$this->assertEquals(0, $DB->count_records('tag_instance'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the tag_assign function.
|
||||
* This function was deprecated in 3.1
|
||||
*/
|
||||
public function test_tag_assign() {
|
||||
global $DB;
|
||||
@@ -129,9 +185,11 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
|
||||
// Create the tag.
|
||||
$tag = $this->getDataGenerator()->create_tag();
|
||||
$tag2 = $this->getDataGenerator()->create_tag();
|
||||
|
||||
// Tag the course with the tag we created.
|
||||
tag_assign('course', $course->id, $tag->id, 0, 0, 'core', context_course::instance($course->id)->id);
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Get the tag instance that should have been created.
|
||||
$taginstance = $DB->get_record('tag_instance', array('itemtype' => 'course', 'itemid' => $course->id), '*', MUST_EXIST);
|
||||
@@ -139,8 +197,8 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
$this->assertEquals(context_course::instance($course->id)->id, $taginstance->contextid);
|
||||
|
||||
// Now call the tag_assign function without specifying the component or
|
||||
// contextid and ensure the function debugging is called.
|
||||
tag_assign('course', $course->id, $tag->id, 0, 0);
|
||||
// contextid and ensure the function debugging is called twice.
|
||||
tag_assign('course', $course->id, $tag2->id, 0, 0);
|
||||
$this->assertDebuggingCalled();
|
||||
}
|
||||
|
||||
@@ -150,6 +208,8 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
public function test_tag_cleanup() {
|
||||
global $DB;
|
||||
|
||||
$task = new \core\task\tag_cron_task();
|
||||
|
||||
// Create some users.
|
||||
$users = array();
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
@@ -162,24 +222,26 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
|
||||
// Test clean up instances with tags that no longer exist.
|
||||
$tags = array();
|
||||
$tagnames = array();
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$tags[] = $this->getDataGenerator()->create_tag(array('userid' => $users[0]->id));
|
||||
$tags[] = $tag = $this->getDataGenerator()->create_tag(array('userid' => $users[0]->id));
|
||||
$tagnames[] = $tag->rawname;
|
||||
}
|
||||
// Create instances with the tags.
|
||||
foreach ($tags as $tag) {
|
||||
tag_assign('course', $course->id, $tag->id, 0, 0, 'core', $context->id);
|
||||
}
|
||||
core_tag_tag::set_item_tags('core', 'course', $course->id, $context, $tagnames);
|
||||
// We should now have ten tag instances.
|
||||
$coursetaginstances = $DB->count_records('tag_instance', array('itemtype' => 'course'));
|
||||
$this->assertEquals(10, $coursetaginstances);
|
||||
|
||||
// Delete four tags
|
||||
// Manual delete of tags is done as the function will remove the instances as well.
|
||||
$DB->delete_records('tag', array('id' => $tags[6]->id));
|
||||
$DB->delete_records('tag', array('id' => $tags[7]->id));
|
||||
$DB->delete_records('tag', array('id' => $tags[8]->id));
|
||||
$DB->delete_records('tag', array('id' => $tags[9]->id));
|
||||
|
||||
// Clean up the tags.
|
||||
tag_cleanup();
|
||||
$task->cleanup();
|
||||
// Check that we now only have six tag_instance records left.
|
||||
$coursetaginstances = $DB->count_records('tag_instance', array('itemtype' => 'course'));
|
||||
$this->assertEquals(6, $coursetaginstances);
|
||||
@@ -187,7 +249,8 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
// Test clean up with users that have been deleted.
|
||||
// Create a tag for this course.
|
||||
foreach ($users as $user) {
|
||||
tag_assign('user', $user->id, $tags[0]->id, 0, 0, 'core', $context->id);
|
||||
$context = context_user::instance($user->id);
|
||||
core_tag_tag::set_item_tags('core', 'user', $user->id, $context, array($tags[0]->rawname));
|
||||
}
|
||||
$usertags = $DB->count_records('tag_instance', array('itemtype' => 'user'));
|
||||
$this->assertCount($usertags, $users);
|
||||
@@ -196,8 +259,9 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
$DB->update_record('user', array('id' => $users[4]->id, 'deleted' => 1));
|
||||
$DB->update_record('user', array('id' => $users[5]->id, 'deleted' => 1));
|
||||
$DB->update_record('user', array('id' => $users[6]->id, 'deleted' => 1));
|
||||
|
||||
// Clean up the tags.
|
||||
tag_cleanup();
|
||||
$task->cleanup();
|
||||
$usertags = $DB->count_records('tag_instance', array('itemtype' => 'user'));
|
||||
$usercount = $DB->count_records('user', array('deleted' => 0));
|
||||
// Remove admin and guest from the count.
|
||||
@@ -206,7 +270,7 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
// Test clean up where a course has been removed.
|
||||
// Delete the course. This also needs to be this way otherwise the tags are removed by using the proper function.
|
||||
$DB->delete_records('course', array('id' => $course->id));
|
||||
tag_cleanup();
|
||||
$task->cleanup();
|
||||
$coursetags = $DB->count_records('tag_instance', array('itemtype' => 'course'));
|
||||
$this->assertEquals(0, $coursetags);
|
||||
|
||||
@@ -216,14 +280,16 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
$post->userid = $users[1]->id;
|
||||
$post->content = 'test post content text';
|
||||
$post->id = $DB->insert_record('post', $post);
|
||||
tag_assign('post', $post->id, $tags[0]->id, 0, 0, 'core', $context->id);
|
||||
$context = context_system::instance();
|
||||
core_tag_tag::set_item_tags('core', 'post', $post->id, $context, array($tags[0]->rawname));
|
||||
|
||||
// Add another one with a fake post id to be removed.
|
||||
tag_assign('post', 15, $tags[0]->id, 0, 0, 'core', $context->id);
|
||||
core_tag_tag::set_item_tags('core', 'post', 15, $context, array($tags[0]->rawname));
|
||||
// Check that there are two tag instances.
|
||||
$posttags = $DB->count_records('tag_instance', array('itemtype' => 'post'));
|
||||
$this->assertEquals(2, $posttags);
|
||||
// Clean up the tags.
|
||||
tag_cleanup();
|
||||
$task->cleanup();
|
||||
// We should only have one entry left now.
|
||||
$posttags = $DB->count_records('tag_instance', array('itemtype' => 'post'));
|
||||
$this->assertEquals(1, $posttags);
|
||||
@@ -234,6 +300,8 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
*/
|
||||
public function test_tag_bulk_delete_instances() {
|
||||
global $DB;
|
||||
$task = new \core\task\tag_cron_task();
|
||||
|
||||
// Setup.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
@@ -242,7 +310,7 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
// Create some tag instances.
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$tag = $this->getDataGenerator()->create_tag(array('userid' => $user->id));
|
||||
tag_assign('course', $course->id, $tag->id, 0, 0, 'core', $context->id);
|
||||
core_tag_tag::add_item_tag('core', 'course', $course->id, $context, $tag->rawname);
|
||||
}
|
||||
// Get tag instances. tag name and rawname are required for the event fired in this function.
|
||||
$sql = "SELECT ti.*, t.name, t.rawname
|
||||
@@ -251,7 +319,7 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
$taginstances = $DB->get_records_sql($sql);
|
||||
$this->assertCount(10, $taginstances);
|
||||
// Run the function.
|
||||
tag_bulk_delete_instances($taginstances);
|
||||
$task->bulk_delete_instances($taginstances);
|
||||
// Make sure they are gone.
|
||||
$instancecount = $DB->count_records('tag_instance');
|
||||
$this->assertEquals(0, $instancecount);
|
||||
@@ -262,6 +330,8 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
*/
|
||||
public function test_correlations() {
|
||||
global $DB;
|
||||
$task = new \core\task\tag_cron_task();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
|
||||
@@ -275,80 +345,118 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
// Several records have both 'cat' and 'cats' tags attached to them.
|
||||
// This will make those tags automatically correlated.
|
||||
// Same with 'dog', 'dogs' and 'puppy.
|
||||
tag_set('user', $user1->id, array('cat', 'cats'),
|
||||
'core', context_user::instance($user1->id)->id);
|
||||
tag_set('user', $user2->id, array('cat', 'cats', 'kitten'),
|
||||
'core', context_user::instance($user2->id)->id);
|
||||
tag_set('user', $user3->id, array('cat', 'cats'),
|
||||
'core', context_user::instance($user3->id)->id);
|
||||
tag_set('user', $user4->id, array('dog', 'dogs', 'puppy'),
|
||||
'core', context_user::instance($user4->id)->id);
|
||||
tag_set('user', $user5->id, array('dog', 'dogs', 'puppy'),
|
||||
'core', context_user::instance($user5->id)->id);
|
||||
tag_set('user', $user6->id, array('dog', 'dogs', 'puppy'),
|
||||
'core', context_user::instance($user6->id)->id);
|
||||
core_tag_tag::set_item_tags('core', 'user', $user1->id, context_user::instance($user1->id), array('cat', 'cats'));
|
||||
core_tag_tag::set_item_tags('core', 'user', $user2->id, context_user::instance($user2->id), array('cat', 'cats', 'kitten'));
|
||||
core_tag_tag::set_item_tags('core', 'user', $user3->id, context_user::instance($user3->id), array('cat', 'cats'));
|
||||
core_tag_tag::set_item_tags('core', 'user', $user4->id, context_user::instance($user4->id), array('dog', 'dogs', 'puppy'));
|
||||
core_tag_tag::set_item_tags('core', 'user', $user5->id, context_user::instance($user5->id), array('dog', 'dogs', 'puppy'));
|
||||
core_tag_tag::set_item_tags('core', 'user', $user6->id, context_user::instance($user6->id), array('dog', 'dogs', 'puppy'));
|
||||
|
||||
$tags = tag_get_id(array('cat', 'cats', 'dog', 'dogs', 'kitten', 'puppy'));
|
||||
$tags = core_tag_tag::get_by_name_bulk(core_tag_collection::get_default(),
|
||||
array('cat', 'cats', 'dog', 'dogs', 'kitten', 'puppy'));
|
||||
$tags = array_map(function ($t) {
|
||||
return $t->id;
|
||||
}, $tags);
|
||||
|
||||
// Add manual relation between tags 'cat' and 'kitten'.
|
||||
tag_set('tag', $tags['cat'], array('kitten'), 'core', context_system::instance()->id);
|
||||
core_tag_tag::get($tags['cat'])->set_related_tags(array('kitten'));
|
||||
|
||||
tag_compute_correlations();
|
||||
$task->compute_correlations();
|
||||
|
||||
$this->assertEquals($tags['cats'],
|
||||
$DB->get_field_select('tag_correlation', 'correlatedtags',
|
||||
$DB->get_field_select('tag_correlation', 'correlatedtags',
|
||||
'tagid = ?', array($tags['cat'])));
|
||||
$this->assertEquals($tags['cat'],
|
||||
$DB->get_field_select('tag_correlation', 'correlatedtags',
|
||||
$DB->get_field_select('tag_correlation', 'correlatedtags',
|
||||
'tagid = ?', array($tags['cats'])));
|
||||
$this->assertEquals($tags['dogs'] . ',' . $tags['puppy'],
|
||||
$DB->get_field_select('tag_correlation', 'correlatedtags',
|
||||
$DB->get_field_select('tag_correlation', 'correlatedtags',
|
||||
'tagid = ?', array($tags['dog'])));
|
||||
$this->assertEquals($tags['dog'] . ',' . $tags['puppy'],
|
||||
$DB->get_field_select('tag_correlation', 'correlatedtags',
|
||||
$DB->get_field_select('tag_correlation', 'correlatedtags',
|
||||
'tagid = ?', array($tags['dogs'])));
|
||||
$this->assertEquals($tags['dog'] . ',' . $tags['dogs'],
|
||||
$DB->get_field_select('tag_correlation', 'correlatedtags',
|
||||
$DB->get_field_select('tag_correlation', 'correlatedtags',
|
||||
'tagid = ?', array($tags['puppy'])));
|
||||
|
||||
// Make sure tag_get_correlated() returns 'cats' as the only correlated tag to the 'cat'.
|
||||
// Make sure get_correlated_tags() returns 'cats' as the only correlated tag to the 'cat'.
|
||||
$correlatedtags = array_values(core_tag_tag::get($tags['cat'])->get_correlated_tags(true));
|
||||
$this->assertCount(3, $correlatedtags); // This will return all existing instances but they all point to the same tag.
|
||||
$this->assertEquals('cats', $correlatedtags[0]->rawname);
|
||||
$this->assertEquals('cats', $correlatedtags[1]->rawname);
|
||||
$this->assertEquals('cats', $correlatedtags[2]->rawname);
|
||||
|
||||
$correlatedtags = array_values(core_tag_tag::get($tags['cat'])->get_correlated_tags());
|
||||
$this->assertCount(1, $correlatedtags); // Duplicates are filtered out here.
|
||||
$this->assertEquals('cats', $correlatedtags[0]->rawname);
|
||||
|
||||
// Make sure tag_get_correlated() returns 'dogs' and 'puppy' as the correlated tags to the 'dog'.
|
||||
$correlatedtags = core_tag_tag::get($tags['dog'])->get_correlated_tags(true);
|
||||
$this->assertCount(6, $correlatedtags); // 2 tags times 3 instances.
|
||||
|
||||
$correlatedtags = array_values(core_tag_tag::get($tags['dog'])->get_correlated_tags());
|
||||
$this->assertCount(2, $correlatedtags);
|
||||
$this->assertEquals('dogs', $correlatedtags[0]->rawname);
|
||||
$this->assertEquals('puppy', $correlatedtags[1]->rawname);
|
||||
|
||||
// Function get_related_tags() will return both related and correlated tags.
|
||||
$relatedtags = array_values(core_tag_tag::get($tags['cat'])->get_related_tags());
|
||||
$this->assertCount(2, $relatedtags);
|
||||
$this->assertEquals('kitten', $relatedtags[0]->rawname);
|
||||
$this->assertEquals('cats', $relatedtags[1]->rawname);
|
||||
|
||||
// Also test deprecated method tag_get_related_tags() and tag_get_correlated().
|
||||
$correlatedtags = array_values(tag_get_correlated($tags['cat']));
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertCount(3, $correlatedtags); // This will return all existing instances but they all point to the same tag.
|
||||
$this->assertEquals('cats', $correlatedtags[0]->rawname);
|
||||
$this->assertEquals('cats', $correlatedtags[1]->rawname);
|
||||
$this->assertEquals('cats', $correlatedtags[2]->rawname);
|
||||
|
||||
$correlatedtags = array_values(tag_get_related_tags($tags['cat'], TAG_RELATED_CORRELATED));
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertCount(1, $correlatedtags); // Duplicates are filtered out here.
|
||||
$this->assertEquals('cats', $correlatedtags[0]->rawname);
|
||||
|
||||
// Make sure tag_get_correlated() returns 'dogs' and 'puppy' as the correlated tags to the 'dog'.
|
||||
$correlatedtags = array_values(tag_get_correlated($tags['dog']));
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertCount(6, $correlatedtags); // 2 tags times 3 instances.
|
||||
|
||||
$correlatedtags = array_values(tag_get_related_tags($tags['dog'], TAG_RELATED_CORRELATED));
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertCount(2, $correlatedtags);
|
||||
$this->assertEquals('dogs', $correlatedtags[0]->rawname);
|
||||
$this->assertEquals('puppy', $correlatedtags[1]->rawname);
|
||||
|
||||
// Function tag_get_related_tags() with default argument will return both related and correlated tags.
|
||||
$relatedtags = array_values(tag_get_related_tags($tags['cat']));
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertCount(2, $relatedtags);
|
||||
$this->assertEquals('kitten', $relatedtags[0]->rawname);
|
||||
$this->assertEquals('cats', $relatedtags[1]->rawname);
|
||||
// End of testing deprecated methods.
|
||||
|
||||
// If we then manually set 'cat' and 'cats' as related, get_related_tags() will filter out duplicates.
|
||||
core_tag_tag::get($tags['cat'])->set_related_tags(array('kitten', 'cats'));
|
||||
|
||||
$relatedtags = array_values(core_tag_tag::get($tags['cat'])->get_related_tags());
|
||||
$this->assertCount(2, $relatedtags);
|
||||
$this->assertEquals('kitten', $relatedtags[0]->rawname);
|
||||
$this->assertEquals('cats', $relatedtags[1]->rawname);
|
||||
|
||||
// If we then manually set 'cat' and 'cats' as related, tag_get_related_tags() will filter out duplicates.
|
||||
tag_set('tag', $tags['cat'], array('kitten', 'cats'), 'core', context_system::instance()->id);
|
||||
|
||||
$relatedtags = array_values(tag_get_related_tags($tags['cat']));
|
||||
$this->assertCount(2, $relatedtags);
|
||||
$this->assertEquals('kitten', $relatedtags[0]->rawname);
|
||||
$this->assertEquals('cats', $relatedtags[1]->rawname);
|
||||
// Make sure core_tag_tag::get_item_tags(), core_tag_tag::get_correlated_tags() return the same set of fields.
|
||||
$relatedtags = core_tag_tag::get_item_tags('core', 'tag', $tags['cat']);
|
||||
$relatedtag = reset($relatedtags);
|
||||
$correlatedtags = core_tag_tag::get($tags['cat'])->get_correlated_tags();
|
||||
$correlatedtag = reset($correlatedtags);
|
||||
$this->assertEquals(array_keys((array)$relatedtag->to_object()), array_keys((array)$correlatedtag->to_object()));
|
||||
|
||||
// Make sure tag_get_correlated() and tag_get_tags() return the same set of fields.
|
||||
// Both functions were deprecated in 3.1.
|
||||
$relatedtags = tag_get_tags('tag', $tags['cat']);
|
||||
$this->assertDebuggingCalled();
|
||||
$relatedtag = reset($relatedtags);
|
||||
$correlatedtags = tag_get_correlated($tags['cat']);
|
||||
$this->assertDebuggingCalled();
|
||||
$correlatedtag = reset($correlatedtags);
|
||||
$this->assertEquals(array_keys((array)$relatedtag), array_keys((array)$correlatedtag));
|
||||
}
|
||||
@@ -358,26 +466,28 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
*/
|
||||
public function test_cleanup() {
|
||||
global $DB;
|
||||
$task = new \core\task\tag_cron_task();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$defaultcoll = core_tag_collection::get_default();
|
||||
|
||||
// Setting tags will create non-official tags 'cat', 'dog' and 'fish'.
|
||||
tag_set('user', $user->id, array('cat', 'dog', 'fish'),
|
||||
'core', context_user::instance($user->id)->id);
|
||||
core_tag_tag::set_item_tags('core', 'user', $user->id, context_user::instance($user->id), array('cat', 'dog', 'fish'));
|
||||
|
||||
$this->assertTrue($DB->record_exists('tag', array('name' => 'cat')));
|
||||
$this->assertTrue($DB->record_exists('tag', array('name' => 'dog')));
|
||||
$this->assertTrue($DB->record_exists('tag', array('name' => 'fish')));
|
||||
|
||||
// Make tag 'dog' official.
|
||||
$dogtag = tag_get('name', 'dog');
|
||||
$fishtag = tag_get('name', 'fish');
|
||||
tag_type_set($dogtag->id, 'official');
|
||||
$dogtag = core_tag_tag::get_by_name($defaultcoll, 'dog', '*');
|
||||
$fishtag = core_tag_tag::get_by_name($defaultcoll, 'fish');
|
||||
$dogtag->update(array('tagtype' => 'official'));
|
||||
|
||||
// Manually remove the instances pointing on tags 'dog' and 'fish'.
|
||||
$DB->execute('DELETE FROM {tag_instance} WHERE tagid in (?,?)', array($dogtag->id, $fishtag->id));
|
||||
|
||||
// Call tag_cleanup().
|
||||
tag_cleanup();
|
||||
$task->cleanup();
|
||||
|
||||
// Tag 'cat' is still present because it's used. Tag 'dog' is present because it's official.
|
||||
// Tag 'fish' was removed because it is not official and it is no longer used by anybody.
|
||||
@@ -389,26 +499,25 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
$DB->update_record('user', array('id' => $user->id, 'deleted' => 1));
|
||||
|
||||
// Call tag_cleanup().
|
||||
tag_cleanup();
|
||||
$task->cleanup();
|
||||
|
||||
// Tag 'cat' was now deleted too.
|
||||
$this->assertFalse($DB->record_exists('tag', array('name' => 'cat')));
|
||||
|
||||
// Assign tag to non-existing record. Make sure tag was created in the DB.
|
||||
tag_set('course', 1231231, array('bird'), 'core', context_system::instance()->id);
|
||||
core_tag_tag::set_item_tags('core', 'course', 1231231, context_system::instance(), array('bird'));
|
||||
$this->assertTrue($DB->record_exists('tag', array('name' => 'bird')));
|
||||
|
||||
// Call tag_cleanup().
|
||||
tag_cleanup();
|
||||
$task->cleanup();
|
||||
|
||||
// Tag 'bird' was now deleted because the related record does not exist in the DB.
|
||||
$this->assertFalse($DB->record_exists('tag', array('name' => 'bird')));
|
||||
|
||||
// Now we have a tag instance pointing on 'sometag' tag.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
tag_set('user', $user->id, array('sometag'),
|
||||
'core', context_user::instance($user->id)->id);
|
||||
$sometag = tag_get('name', 'sometag');
|
||||
core_tag_tag::set_item_tags('core', 'user', $user->id, context_user::instance($user->id), array('sometag'));
|
||||
$sometag = core_tag_tag::get_by_name($defaultcoll, 'sometag');
|
||||
|
||||
$this->assertTrue($DB->record_exists('tag_instance', array('tagid' => $sometag->id)));
|
||||
|
||||
@@ -416,9 +525,334 @@ class core_tag_taglib_testcase extends advanced_testcase {
|
||||
$DB->delete_records('tag', array('id' => $sometag->id));
|
||||
|
||||
// Call tag_cleanup().
|
||||
tag_cleanup();
|
||||
$task->cleanup();
|
||||
|
||||
// The tag instances were also removed.
|
||||
$this->assertFalse($DB->record_exists('tag_instance', array('tagid' => $sometag->id)));
|
||||
}
|
||||
|
||||
public function test_guess_tag() {
|
||||
global $DB;
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$tag1 = $this->getDataGenerator()->create_tag(array('name' => 'Cat'));
|
||||
$tc = core_tag_collection::create((object)array('name' => 'tagcoll'));
|
||||
$tag2 = $this->getDataGenerator()->create_tag(array('name' => 'Cat', 'tagcollid' => $tc->id));
|
||||
$this->assertEquals(2, count($DB->get_records('tag')));
|
||||
$this->assertEquals(2, count(core_tag_tag::guess_by_name('Cat')));
|
||||
$this->assertEquals(core_tag_collection::get_default(), core_tag_tag::get_by_name(0, 'Cat')->tagcollid);
|
||||
}
|
||||
|
||||
public function test_instances() {
|
||||
global $DB;
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
|
||||
// Create a course to tag.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
$initialtagscount = $DB->count_records('tag');
|
||||
|
||||
core_tag_tag::set_item_tags('core', 'course', $course->id, $context, array('Tag 1', 'Tag 2'));
|
||||
$tags = core_tag_tag::get_item_tags('core', 'course', $course->id);
|
||||
$tagssimple = array_values($tags);
|
||||
$this->assertEquals(2, count($tags));
|
||||
$this->assertEquals('Tag 1', $tagssimple[0]->rawname);
|
||||
$this->assertEquals('Tag 2', $tagssimple[1]->rawname);
|
||||
$this->assertEquals($initialtagscount + 2, $DB->count_records('tag'));
|
||||
|
||||
core_tag_tag::set_item_tags('core', 'course', $course->id, $context, array('Tag 3', 'Tag 2', 'Tag 1'));
|
||||
$tags = core_tag_tag::get_item_tags('core', 'course', $course->id);
|
||||
$tagssimple = array_values($tags);
|
||||
$this->assertEquals(3, count($tags));
|
||||
$this->assertEquals('Tag 3', $tagssimple[0]->rawname);
|
||||
$this->assertEquals('Tag 2', $tagssimple[1]->rawname);
|
||||
$this->assertEquals('Tag 1', $tagssimple[2]->rawname);
|
||||
$this->assertEquals($initialtagscount + 3, $DB->count_records('tag'));
|
||||
|
||||
core_tag_tag::set_item_tags('core', 'course', $course->id, $context, array('Tag 3'));
|
||||
$tags = core_tag_tag::get_item_tags('core', 'course', $course->id);
|
||||
$tagssimple = array_values($tags);
|
||||
$this->assertEquals(1, count($tags));
|
||||
$this->assertEquals('Tag 3', $tagssimple[0]->rawname);
|
||||
|
||||
// Make sure the unused tags were removed from tag table.
|
||||
$this->assertEquals($initialtagscount + 1, $DB->count_records('tag'));
|
||||
}
|
||||
|
||||
public function test_related_tags() {
|
||||
global $DB;
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$tagcollid = core_tag_collection::get_default();
|
||||
$tag = $this->getDataGenerator()->create_tag(array('$tagcollid' => $tagcollid, 'rawname' => 'My tag'));
|
||||
$tag = core_tag_tag::get($tag->id, '*');
|
||||
|
||||
$tag->set_related_tags(array('Synonym 1', 'Synonym 2'));
|
||||
$relatedtags = array_values(core_tag_tag::get_item_tags('core', 'tag', $tag->id));
|
||||
$this->assertEquals(2, count($relatedtags));
|
||||
$this->assertEquals('Synonym 1', $relatedtags[0]->rawname);
|
||||
$this->assertEquals('Synonym 2', $relatedtags[1]->rawname);
|
||||
|
||||
$t1 = core_tag_tag::get_by_name($tagcollid, 'Synonym 1', '*');
|
||||
$relatedtags = array_values(core_tag_tag::get_item_tags('core', 'tag', $t1->id));
|
||||
$this->assertEquals(1, count($relatedtags));
|
||||
$this->assertEquals('My tag', $relatedtags[0]->rawname);
|
||||
|
||||
$t2 = core_tag_tag::get_by_name($tagcollid, 'Synonym 2', '*');
|
||||
$relatedtags = array_values(core_tag_tag::get_item_tags('core', 'tag', $t2->id));
|
||||
$this->assertEquals(1, count($relatedtags));
|
||||
$this->assertEquals('My tag', $relatedtags[0]->rawname);
|
||||
|
||||
$tag->set_related_tags(array('Synonym 3', 'Synonym 2', 'Synonym 1'));
|
||||
$relatedtags = array_values(core_tag_tag::get_item_tags('core', 'tag', $tag->id));
|
||||
$this->assertEquals(3, count($relatedtags));
|
||||
$this->assertEquals('Synonym 1', $relatedtags[0]->rawname);
|
||||
$this->assertEquals('Synonym 2', $relatedtags[1]->rawname);
|
||||
$this->assertEquals('Synonym 3', $relatedtags[2]->rawname);
|
||||
|
||||
$t3 = core_tag_tag::get_by_name($tagcollid, 'Synonym 3', '*');
|
||||
$relatedtags = array_values(core_tag_tag::get_item_tags('core', 'tag', $t3->id));
|
||||
$this->assertEquals(1, count($relatedtags));
|
||||
$this->assertEquals('My tag', $relatedtags[0]->rawname);
|
||||
|
||||
$tag->set_related_tags(array('Synonym 3', 'Synonym 2'));
|
||||
$relatedtags = array_values(core_tag_tag::get_item_tags('core', 'tag', $tag->id));
|
||||
$this->assertEquals(2, count($relatedtags));
|
||||
$this->assertEquals('Synonym 2', $relatedtags[0]->rawname);
|
||||
$this->assertEquals('Synonym 3', $relatedtags[1]->rawname);
|
||||
|
||||
// Assert "Synonym 1" no longer links but is still present (will be removed by cron).
|
||||
$relatedtags = array_values(core_tag_tag::get_item_tags('core', 'tag', $t1->id));
|
||||
$this->assertEquals(0, count($relatedtags));
|
||||
}
|
||||
|
||||
/**
|
||||
* Very basic test for create/move/update/delete actions, without any itemtype movements.
|
||||
*/
|
||||
public function test_tag_coll_basic() {
|
||||
global $DB;
|
||||
|
||||
// Make sure there is one and only one tag coll that is marked as default.
|
||||
$tagcolls = core_tag_collection::get_collections();
|
||||
$this->assertEquals(1, count($DB->get_records('tag_coll', array('isdefault' => 1))));
|
||||
$defaulttagcoll = core_tag_collection::get_default();
|
||||
|
||||
// Create a new tag coll to store user tags and something else.
|
||||
$data = (object)array('name' => 'new tag coll');
|
||||
$tagcollid1 = core_tag_collection::create($data)->id;
|
||||
$tagcolls = core_tag_collection::get_collections();
|
||||
$this->assertEquals('new tag coll', $tagcolls[$tagcollid1]->name);
|
||||
|
||||
// Create a new tag coll to store post tags.
|
||||
$data = (object)array('name' => 'posts');
|
||||
$tagcollid2 = core_tag_collection::create($data)->id;
|
||||
$tagcolls = core_tag_collection::get_collections();
|
||||
$this->assertEquals('posts', $tagcolls[$tagcollid2]->name);
|
||||
$this->assertEquals($tagcolls[$tagcollid1]->sortorder + 1,
|
||||
$tagcolls[$tagcollid2]->sortorder);
|
||||
|
||||
// Illegal tag colls sortorder changing.
|
||||
$this->assertFalse(core_tag_collection::change_sortorder($tagcolls[$defaulttagcoll], 1));
|
||||
$this->assertFalse(core_tag_collection::change_sortorder($tagcolls[$defaulttagcoll], -1));
|
||||
$this->assertFalse(core_tag_collection::change_sortorder($tagcolls[$tagcollid2], 1));
|
||||
|
||||
// Move the very last tag coll one position up.
|
||||
$this->assertTrue(core_tag_collection::change_sortorder($tagcolls[$tagcollid2], -1));
|
||||
$tagcolls = core_tag_collection::get_collections();
|
||||
$this->assertEquals($tagcolls[$tagcollid2]->sortorder + 1,
|
||||
$tagcolls[$tagcollid1]->sortorder);
|
||||
|
||||
// Move the second last tag coll one position down.
|
||||
$this->assertTrue(core_tag_collection::change_sortorder($tagcolls[$tagcollid2], 1));
|
||||
$tagcolls = core_tag_collection::get_collections();
|
||||
$this->assertEquals($tagcolls[$tagcollid1]->sortorder + 1,
|
||||
$tagcolls[$tagcollid2]->sortorder);
|
||||
|
||||
// Edit tag coll.
|
||||
$this->assertTrue(core_tag_collection::update($tagcolls[$tagcollid2],
|
||||
(object)array('name' => 'posts2')));
|
||||
$tagcolls = core_tag_collection::get_collections();
|
||||
$this->assertEquals('posts2', $tagcolls[$tagcollid2]->name);
|
||||
|
||||
// Delete tag coll.
|
||||
$count = $DB->count_records('tag_coll');
|
||||
$this->assertFalse(core_tag_collection::delete($tagcolls[$defaulttagcoll]));
|
||||
$this->assertTrue(core_tag_collection::delete($tagcolls[$tagcollid1]));
|
||||
$this->assertEquals($count - 1, $DB->count_records('tag_coll'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares environment for test_move_tags_* tests
|
||||
*/
|
||||
protected function prepare_move_tags() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot.'/blog/locallib.php');
|
||||
$this->setUser($this->getDataGenerator()->create_user());
|
||||
|
||||
$collid1 = core_tag_collection::get_default();
|
||||
$collid2 = core_tag_collection::create(array('name' => 'newcoll'))->id;
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$blogpost = new blog_entry(null, array('subject' => 'test'), null);
|
||||
$states = blog_entry::get_applicable_publish_states();
|
||||
$blogpost->publishstate = reset($states);
|
||||
$blogpost->add();
|
||||
|
||||
core_tag_tag::set_item_tags('core', 'user', $user1->id, context_user::instance($user1->id),
|
||||
array('Tag1', 'Tag2'));
|
||||
core_tag_tag::set_item_tags('core', 'user', $user2->id, context_user::instance($user2->id),
|
||||
array('Tag2', 'Tag3'));
|
||||
$this->getDataGenerator()->create_tag(array('rawname' => 'Tag4',
|
||||
'tagcollid' => $collid1, 'tagtype' => 'official'));
|
||||
$this->getDataGenerator()->create_tag(array('rawname' => 'Tag5',
|
||||
'tagcollid' => $collid2, 'tagtype' => 'official'));
|
||||
|
||||
return array($collid1, $collid2, $user1, $user2, $blogpost);
|
||||
}
|
||||
|
||||
public function test_move_tags_simple() {
|
||||
global $DB;
|
||||
list($collid1, $collid2, $user1, $user2, $blogpost) = $this->prepare_move_tags();
|
||||
|
||||
// Move 'user' area from collection 1 to collection 2, make sure tags were moved completely.
|
||||
$tagarea = $DB->get_record('tag_area', array('itemtype' => 'user', 'component' => 'core'));
|
||||
core_tag_area::update($tagarea, array('tagcollid' => $collid2));
|
||||
|
||||
$this->assertEquals(array('Tag4'),
|
||||
$DB->get_fieldset_select('tag', 'rawname', 'tagcollid = ? ORDER BY name', array($collid1)));
|
||||
$this->assertEquals(array('Tag1', 'Tag2', 'Tag3', 'Tag5'),
|
||||
$DB->get_fieldset_select('tag', 'rawname', 'tagcollid = ? ORDER BY name', array($collid2)));
|
||||
$this->assertEquals(array('Tag1', 'Tag2'), array_values(core_tag_tag::get_item_tags_array('core', 'user', $user1->id)));
|
||||
$this->assertEquals(array('Tag2', 'Tag3'), array_values(core_tag_tag::get_item_tags_array('core', 'user', $user2->id)));
|
||||
}
|
||||
|
||||
public function test_move_tags_split_tag() {
|
||||
global $DB;
|
||||
list($collid1, $collid2, $user1, $user2, $blogpost) = $this->prepare_move_tags();
|
||||
|
||||
core_tag_tag::set_item_tags('core', 'post', $blogpost->id, context_system::instance(),
|
||||
array('Tag1', 'Tag3'));
|
||||
|
||||
// Move 'user' area from collection 1 to collection 2, make sure tag Tag2 was moved and tags Tag1 and Tag3 were duplicated.
|
||||
$tagareauser = $DB->get_record('tag_area', array('itemtype' => 'user', 'component' => 'core'));
|
||||
core_tag_area::update($tagareauser, array('tagcollid' => $collid2));
|
||||
|
||||
$this->assertEquals(array('Tag1', 'Tag3', 'Tag4'),
|
||||
$DB->get_fieldset_select('tag', 'rawname', 'tagcollid = ? ORDER BY name', array($collid1)));
|
||||
$this->assertEquals(array('Tag1', 'Tag2', 'Tag3', 'Tag5'),
|
||||
$DB->get_fieldset_select('tag', 'rawname', 'tagcollid = ? ORDER BY name', array($collid2)));
|
||||
$this->assertEquals(array('Tag1', 'Tag2'), array_values(core_tag_tag::get_item_tags_array('core', 'user', $user1->id)));
|
||||
$this->assertEquals(array('Tag2', 'Tag3'), array_values(core_tag_tag::get_item_tags_array('core', 'user', $user2->id)));
|
||||
$this->assertEquals(array('Tag1', 'Tag3'), array_values(core_tag_tag::get_item_tags_array('core', 'post', $blogpost->id)));
|
||||
}
|
||||
|
||||
public function test_move_tags_merge_tag() {
|
||||
global $DB;
|
||||
list($collid1, $collid2, $user1, $user2, $blogpost) = $this->prepare_move_tags();
|
||||
|
||||
// Set collection for 'post' tag area to be collection 2 and add some tags there.
|
||||
$tagareablog = $DB->get_record('tag_area', array('itemtype' => 'post', 'component' => 'core'));
|
||||
core_tag_area::update($tagareablog, array('tagcollid' => $collid2));
|
||||
|
||||
core_tag_tag::set_item_tags('core', 'post', $blogpost->id, context_system::instance(),
|
||||
array('TAG1', 'Tag3'));
|
||||
|
||||
// Move 'user' area from collection 1 to collection 2,
|
||||
// make sure tag Tag2 was moved and tags Tag1 and Tag3 were merged into existing.
|
||||
$tagareauser = $DB->get_record('tag_area', array('itemtype' => 'user', 'component' => 'core'));
|
||||
core_tag_area::update($tagareauser, array('tagcollid' => $collid2));
|
||||
|
||||
$this->assertEquals(array('Tag4'),
|
||||
$DB->get_fieldset_select('tag', 'rawname', 'tagcollid = ? ORDER BY name', array($collid1)));
|
||||
$this->assertEquals(array('TAG1', 'Tag2', 'Tag3', 'Tag5'),
|
||||
$DB->get_fieldset_select('tag', 'rawname', 'tagcollid = ? ORDER BY name', array($collid2)));
|
||||
$this->assertEquals(array('TAG1', 'Tag2'), array_values(core_tag_tag::get_item_tags_array('core', 'user', $user1->id)));
|
||||
$this->assertEquals(array('Tag2', 'Tag3'), array_values(core_tag_tag::get_item_tags_array('core', 'user', $user2->id)));
|
||||
$this->assertEquals(array('TAG1', 'Tag3'), array_values(core_tag_tag::get_item_tags_array('core', 'post', $blogpost->id)));
|
||||
}
|
||||
|
||||
public function test_move_tags_with_related() {
|
||||
global $DB;
|
||||
list($collid1, $collid2, $user1, $user2, $blogpost) = $this->prepare_move_tags();
|
||||
|
||||
// Set Tag1 to be related to Tag2 and Tag4 (in collection 1).
|
||||
core_tag_tag::get_by_name($collid1, 'Tag1')->set_related_tags(array('Tag2', 'Tag4'));
|
||||
|
||||
// Set collection for 'post' tag area to be collection 2 and add some tags there.
|
||||
$tagareablog = $DB->get_record('tag_area', array('itemtype' => 'post', 'component' => 'core'));
|
||||
core_tag_area::update($tagareablog, array('tagcollid' => $collid2));
|
||||
|
||||
core_tag_tag::set_item_tags('core', 'post', $blogpost->id, context_system::instance(),
|
||||
array('TAG1', 'Tag3'));
|
||||
|
||||
// Move 'user' area from collection 1 to collection 2, make sure tags were moved completely.
|
||||
$tagarea = $DB->get_record('tag_area', array('itemtype' => 'user', 'component' => 'core'));
|
||||
core_tag_area::update($tagarea, array('tagcollid' => $collid2));
|
||||
|
||||
$this->assertEquals(array('Tag1', 'Tag2', 'Tag4'),
|
||||
$DB->get_fieldset_select('tag', 'rawname', 'tagcollid = ? ORDER BY name', array($collid1)));
|
||||
$this->assertEquals(array('TAG1', 'Tag2', 'Tag3', 'Tag4', 'Tag5'),
|
||||
$DB->get_fieldset_select('tag', 'rawname', 'tagcollid = ? ORDER BY name', array($collid2)));
|
||||
$this->assertEquals(array('TAG1', 'Tag2'), array_values(core_tag_tag::get_item_tags_array('core', 'user', $user1->id)));
|
||||
$this->assertEquals(array('Tag2', 'Tag3'), array_values(core_tag_tag::get_item_tags_array('core', 'user', $user2->id)));
|
||||
|
||||
$tag11 = core_tag_tag::get_by_name($collid1, 'Tag1');
|
||||
$related11 = tag_get_related_tags($tag11->id, TAG_RELATED_MANUAL);
|
||||
$this->assertDebuggingCalled();
|
||||
$related11 = array_map('core_tag_tag::make_display_name', $related11);
|
||||
sort($related11); // Order of related tags may be random.
|
||||
$this->assertEquals('Tag2, Tag4', join(', ', $related11));
|
||||
|
||||
$tag21 = core_tag_tag::get_by_name($collid2, 'TAG1');
|
||||
$related21 = tag_get_related_tags($tag21->id, TAG_RELATED_MANUAL);
|
||||
$this->assertDebuggingCalled();
|
||||
$related21 = array_map('core_tag_tag::make_display_name', $related21);
|
||||
sort($related21); // Order of related tags may be random.
|
||||
$this->assertEquals('Tag2, Tag4', join(', ', $related21));
|
||||
}
|
||||
|
||||
public function test_normalize() {
|
||||
$tagset = array('Cat', ' Dog ', '<Mouse', '<>', 'mouse', 'Dog');
|
||||
|
||||
// Test function tag_normalize() that was deprecated in 3.1.
|
||||
$this->assertEquals(array('Cat' => 'Cat', 'Dog' => 'Dog', '<Mouse' => 'Mouse', '<>' => '', 'mouse' => 'mouse'),
|
||||
tag_normalize($tagset, TAG_CASE_ORIGINAL));
|
||||
$this->assertDebuggingCalled();
|
||||
$this->assertEquals(array('Cat' => 'cat', 'Dog' => 'dog', '<Mouse' => 'mouse', '<>' => '', 'mouse' => 'mouse'),
|
||||
tag_normalize($tagset, TAG_CASE_LOWER));
|
||||
$this->assertDebuggingCalled();
|
||||
|
||||
// Test replacement function core_tag_tag::normalize().
|
||||
$this->assertEquals(array('Cat' => 'Cat', 'Dog' => 'Dog', '<Mouse' => 'Mouse', '<>' => '', 'mouse' => 'mouse'),
|
||||
core_tag_tag::normalize($tagset, false));
|
||||
$this->assertEquals(array('Cat' => 'cat', 'Dog' => 'dog', '<Mouse' => 'mouse', '<>' => '', 'mouse' => 'mouse'),
|
||||
core_tag_tag::normalize($tagset, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test functions core_tag_tag::create_if_missing() and core_tag_tag::get_by_name_bulk().
|
||||
*/
|
||||
public function test_create_get() {
|
||||
$tagset = array('Cat', ' Dog ', '<Mouse', '<>', 'mouse', 'Dog');
|
||||
|
||||
$collid = core_tag_collection::get_default();
|
||||
$tags = core_tag_tag::create_if_missing($collid, $tagset);
|
||||
$this->assertEquals(array('cat', 'dog', 'mouse'), array_keys($tags));
|
||||
$this->assertEquals('Dog', $tags['dog']->rawname);
|
||||
$this->assertEquals('mouse', $tags['mouse']->rawname); // Case of the last tag wins.
|
||||
|
||||
$tags2 = core_tag_tag::create_if_missing($collid, array('CAT', 'Elephant'));
|
||||
$this->assertEquals(array('cat', 'elephant'), array_keys($tags2));
|
||||
$this->assertEquals('Cat', $tags2['cat']->rawname);
|
||||
$this->assertEquals('Elephant', $tags2['elephant']->rawname);
|
||||
$this->assertEquals($tags['cat']->id, $tags2['cat']->id); // Tag 'cat' already existed and was not created again.
|
||||
|
||||
$tags3 = core_tag_tag::get_by_name_bulk($collid, $tagset);
|
||||
$this->assertEquals(array('cat', 'dog', 'mouse'), array_keys($tags3));
|
||||
$this->assertEquals('Dog', $tags3['dog']->rawname);
|
||||
$this->assertEquals('mouse', $tags3['mouse']->rawname);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,66 @@
|
||||
This files describes API changes in tagging, information provided
|
||||
here is intended especially for developers.
|
||||
|
||||
=== 3.1 ===
|
||||
|
||||
* Each plugin must define the tag areas it uses in plugindir/db/tag.php
|
||||
See https://docs.moodle.org/dev/Tag_API for more details.
|
||||
* When adding a 'tags' form element the component and itemtype must be specified
|
||||
in the element attributes.
|
||||
* Tag name is no longer a unique field in 'tag' table, tags may be separated in
|
||||
tag collections
|
||||
* Specifying component is also required when retrieving item tags. This will allow
|
||||
different components to add tags to the same table (for example blog and forum
|
||||
posts) and even suggest alternative tagging for the same items.
|
||||
* All tag_* functions were deprecated because they now require either tag
|
||||
collection id or component, some were moved to the new class core_tag_tag and
|
||||
some - to the renderers or templates. As usual, the deprecated function code
|
||||
and debugging message will provide you with the suggestions of the alternative.
|
||||
Please note that all deprecated functions will be removed after several major releases.
|
||||
- tag_type_set
|
||||
- tag_description_set
|
||||
- tag_get_tags
|
||||
- tag_get_tags_array
|
||||
- tag_get_tags_csv
|
||||
- tag_get_tags_ids
|
||||
- tag_get_id
|
||||
- tag_rename
|
||||
- tag_delete_instance
|
||||
- tag_find_records
|
||||
- tag_add
|
||||
- tag_assign
|
||||
- tag_record_count
|
||||
- tag_record_tagged_with
|
||||
- tag_set_flag
|
||||
- tag_unset_flag
|
||||
- tag_print_cloud
|
||||
- tag_print_description_box
|
||||
- tag_print_management_box
|
||||
- tag_print_search_box
|
||||
- tag_print_search_results
|
||||
- tag_print_tagged_users_table
|
||||
- tag_print_user_box
|
||||
- tag_print_user_list
|
||||
- tag_display_name
|
||||
- tag_normalize
|
||||
- tag_get_related_tags_csv
|
||||
- tag_set
|
||||
- tag_set_add
|
||||
- tag_set_delete
|
||||
- tag_get
|
||||
- tag_get_related_tags
|
||||
- tag_delete
|
||||
- tag_delete_instances
|
||||
- tag_cleanup
|
||||
- tag_bulk_delete_instances
|
||||
- tag_compute_correlations
|
||||
- tag_process_computed_correlation
|
||||
- tag_cron
|
||||
- tag_find_tags
|
||||
- tag_get_name
|
||||
- tag_get_correlated
|
||||
- tag_cloud_sort
|
||||
|
||||
=== 3.0 ===
|
||||
|
||||
* Tagging courses is now implemented without user id - through course edit form or
|
||||
|
||||
+37
-44
@@ -734,28 +734,17 @@ table.mod_index {width:100%;}
|
||||
/**
|
||||
* Tags
|
||||
*/
|
||||
#page-tag-coursetags_edit .coursetag_edit_centered {position: relative;width: 600px;margin: 20px auto;}
|
||||
#page-tag-coursetags_edit .coursetag_edit_row {clear:both;}
|
||||
#page-tag-coursetags_edit .coursetag_edit_row .coursetag_edit_left {float:left;width:50%;text-align:right;}
|
||||
#page-tag-coursetags_edit .coursetag_edit_row .coursetag_edit_right {margin-left:50%;}
|
||||
#page-tag-coursetags_edit .coursetag_edit_input3 {display: none;}
|
||||
#page-tag-coursetags_more .coursetag_more_large {font-size: 120%;}
|
||||
#page-tag-coursetags_more .coursetag_more_small {font-size: 80%;}
|
||||
#page-tag-coursetags_more .coursetag_more_link {font-size: 80%;}
|
||||
#tag-description,
|
||||
#tag-blogs {width:100%;}
|
||||
#tag-management-box {margin-bottom:10px;line-height:20px;}
|
||||
#tag-user-table {padding:3px;clear: both;width:100%;}
|
||||
#tag-user-table:after {content:".";display:block;clear:both;visibility:hidden;height:0;overflow:hidden;}
|
||||
img.user-image {height:100px;width:100px;}
|
||||
#small-tag-cloud-box {width:300px;margin:0 auto;}
|
||||
#big-tag-cloud-box {width:600px;margin:0 auto;float:none;}
|
||||
ul#tag-cloud-list {list-style:none;padding:5px;margin:0;}
|
||||
ul#tag-cloud-list li {margin:0;display:inline;list-style-type:none;}
|
||||
#tag-search-box {text-align:center;margin:10px auto;}
|
||||
#tag-search-results-container {padding:0;width:100%;}
|
||||
#tag-search-results {padding:0;margin: 15px 20% 0 20%;float:left;width:60%;display:block;}
|
||||
#tag-search-results li {width:30%;float:left;padding-left:1%;text-align:left;line-height:20px;padding-right:1%;list-style:none;}
|
||||
.tagarea {clear:both;overflow:hidden;}
|
||||
.tagarea .controls {text-align: center;}
|
||||
.tagarea .controls .gotopage.nextpage {float: right;}
|
||||
.tagarea .controls .gotopage.prevpage {float: left;}
|
||||
.tagarea .controls .exclusivemode {display: inline-block;}
|
||||
.dir-rtl .tagarea .controls .gotopage.nextpage {float: left;}
|
||||
.dir-rtl .tagarea .controls .gotopage.prevpage {float: right;}
|
||||
.tagarea .controls.controls-bottom {margin-top: 5px;}
|
||||
span.flagged-tag,
|
||||
tr.flagged-tag,
|
||||
span.flagged-tag a,
|
||||
@@ -770,31 +759,35 @@ tr.flagged-tag a {color:#FF0000;}
|
||||
.tag-management-table tr:hover td .tagnameedit img,
|
||||
.tag-management-table tr td .tagnameedit:focus img {opacity: 1;}
|
||||
.tag-management-table tr:hover td.tageditingon .tagnameedit img {opacity: 0.2;}
|
||||
h2.tag-heading,
|
||||
div#tag-description,
|
||||
div#tag-blogs,
|
||||
body.tag .managelink {padding: 5px;}
|
||||
.tag_cloud .s20 {font-size: 1.5em;font-weight: bold;}
|
||||
.tag_cloud .s19 {font-size: 1.5em;}
|
||||
.tag_cloud .s18 {font-size: 1.4em;font-weight: bold;}
|
||||
.tag_cloud .s17 {font-size: 1.4em;}
|
||||
.tag_cloud .s16 {font-size: 1.3em;font-weight: bold;}
|
||||
.tag_cloud .s15 {font-size: 1.3em;}
|
||||
.tag_cloud .s14 {font-size: 1.2em;font-weight: bold;}
|
||||
.tag_cloud .s13 {font-size: 1.2em;}
|
||||
.tag_cloud .s12,
|
||||
.tag_cloud .s11 {font-size: 1.1em;font-weight: bold;}
|
||||
.tag_cloud .s10,
|
||||
.tag_cloud .s9 {font-size: 1.1em;}
|
||||
.tag_cloud .s8,
|
||||
.tag_cloud .s7 {font-size: 1em;font-weight: bold;}
|
||||
.tag_cloud .s6,
|
||||
.tag_cloud .s5 {font-size: 1em;}
|
||||
.tag_cloud .s4,
|
||||
.tag_cloud .s3 {font-size: 0.9em;font-weight: bold;}
|
||||
.tag_cloud .s2,
|
||||
.tag_cloud .s1 {font-size: 0.9em;}
|
||||
.tag_cloud .s0 {font-size: 0.8em;}
|
||||
.tag_feed .media, .tag_feed .media-body {overflow: hidden;}
|
||||
.tag_feed.media-list .media .itemimage {float: left;}
|
||||
.dir-rtl .tag_feed.media-list .media .itemimage {float: right;}
|
||||
.tag_feed.media-list .media .itemimage img {height: 35px;width: 35px;}
|
||||
.tag_feed.media-list .media .media-body {padding-right: 10px;padding-left: 10px;}
|
||||
.tag_cloud {text-align: center;}
|
||||
.tag_cloud .inline-list li {padding: 0px 0.2em;}
|
||||
.tag_cloud .tag_overflow {margin-top: 1em;font-style: italic;}
|
||||
.tag_cloud .s20 {font-size: 2.7em;}
|
||||
.tag_cloud .s19 {font-size: 2.6em;}
|
||||
.tag_cloud .s18 {font-size: 2.5em;}
|
||||
.tag_cloud .s17 {font-size: 2.4em;}
|
||||
.tag_cloud .s16 {font-size: 2.3em;}
|
||||
.tag_cloud .s15 {font-size: 2.2em;}
|
||||
.tag_cloud .s14 {font-size: 2.1em;}
|
||||
.tag_cloud .s13 {font-size: 2em;}
|
||||
.tag_cloud .s12 {font-size: 1.9em;}
|
||||
.tag_cloud .s11 {font-size: 1.8em;}
|
||||
.tag_cloud .s10 {font-size: 1.7em;}
|
||||
.tag_cloud .s9 {font-size: 1.6em;}
|
||||
.tag_cloud .s8 {font-size: 1.5em;}
|
||||
.tag_cloud .s7 {font-size: 1.4em;}
|
||||
.tag_cloud .s6 {font-size: 1.3em;}
|
||||
.tag_cloud .s5 {font-size: 1.2em;}
|
||||
.tag_cloud .s4 {font-size: 1.1em;}
|
||||
.tag_cloud .s3 {font-size: 1em;}
|
||||
.tag_cloud .s2 {font-size: 0.9em;}
|
||||
.tag_cloud .s1 {font-size: 0.8em;}
|
||||
.tag_cloud .s0 {font-size: 0.7em;}
|
||||
|
||||
/*
|
||||
* Backup and Restore CSS
|
||||
|
||||
@@ -747,97 +747,74 @@ table.mod_index {
|
||||
/**
|
||||
* Tags
|
||||
*/
|
||||
#page-tag-coursetags_edit .coursetag_edit_centered {
|
||||
position: relative;
|
||||
width: 600px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
#page-tag-coursetags_edit .coursetag_edit_row {
|
||||
clear: both;
|
||||
}
|
||||
#page-tag-coursetags_edit .coursetag_edit_row .coursetag_edit_left {
|
||||
float: left;
|
||||
width: 50%;
|
||||
text-align: right;
|
||||
}
|
||||
#page-tag-coursetags_edit .coursetag_edit_row .coursetag_edit_right {
|
||||
margin-left: 50%;
|
||||
}
|
||||
#page-tag-coursetags_edit .coursetag_edit_input3 {
|
||||
display: none;
|
||||
}
|
||||
#page-tag-coursetags_more .coursetag_more_large {
|
||||
font-size: 120%;
|
||||
}
|
||||
#page-tag-coursetags_more .coursetag_more_small {
|
||||
font-size: 80%;
|
||||
}
|
||||
#page-tag-coursetags_more .coursetag_more_link {
|
||||
font-size: 80%;
|
||||
}
|
||||
#tag-description,
|
||||
#tag-blogs {
|
||||
width: 100%;
|
||||
}
|
||||
#tag-management-box {
|
||||
margin-bottom: 10px;
|
||||
line-height: 20px;
|
||||
}
|
||||
#tag-user-table {
|
||||
padding: 3px;
|
||||
clear: both;
|
||||
width: 100%;
|
||||
}
|
||||
#tag-user-table {
|
||||
.clearfix
|
||||
}
|
||||
img.user-image {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
#small-tag-cloud-box {
|
||||
width: 300px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#big-tag-cloud-box {
|
||||
width: 600px;
|
||||
margin: 0 auto;
|
||||
float: none;
|
||||
}
|
||||
ul#tag-cloud-list {
|
||||
list-style: none;
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
}
|
||||
ul#tag-cloud-list li {
|
||||
margin: 0;
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
}
|
||||
#tag-search-box {
|
||||
text-align: center;
|
||||
margin: 10px auto;
|
||||
}
|
||||
#tag-search-results-container {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
|
||||
.path-tag .tag-index-items .tagarea {
|
||||
border: 1px solid #E3E3E3;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
#tag-search-results {
|
||||
padding: 0;
|
||||
margin: 15px 20% 0 20%;
|
||||
float: left;
|
||||
width: 60%;
|
||||
|
||||
.path-tag .tag-index-items .tagarea h3 {
|
||||
display: block;
|
||||
}
|
||||
#tag-search-results li {
|
||||
width: 30%;
|
||||
float: left;
|
||||
padding-left: 1%;
|
||||
text-align: left;
|
||||
padding: 3px 0 10px 0;
|
||||
margin: 0px;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
padding-right: 1%;
|
||||
list-style: none;
|
||||
color: #999;
|
||||
text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.5);
|
||||
text-transform: uppercase;
|
||||
word-wrap: break-word;
|
||||
border-bottom: solid 1px #E3E3E3;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.path-tag .tagarea .controls,
|
||||
.path-tag .tagarea .taggeditems {
|
||||
.clearfix();
|
||||
}
|
||||
.path-tag .tagarea .controls,
|
||||
.path-tag .tag-backtoallitems {
|
||||
text-align: center;
|
||||
}
|
||||
.path-tag .tagarea .controls .gotopage.nextpage {
|
||||
float: right;
|
||||
}
|
||||
.path-tag .tagarea .controls .gotopage.prevpage {
|
||||
float: left;
|
||||
}
|
||||
.path-tag .tagarea .controls .exclusivemode {
|
||||
display: inline-block;
|
||||
}
|
||||
.dir-rtl.path-tag .tagarea .controls .gotopage.nextpage {
|
||||
float: left;
|
||||
}
|
||||
.dir-rtl.path-tag .tagarea .controls .gotopage.prevpage {
|
||||
float: right;
|
||||
}
|
||||
.path-tag .tagarea .controls.controls-bottom {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.path-tag .tagarea .controls .gotopage.nextpage::after {
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
content: "»";
|
||||
}
|
||||
.path-tag .tagarea .controls .gotopage.prevpage::before {
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
content: "«";
|
||||
}
|
||||
|
||||
span.flagged-tag,
|
||||
tr.flagged-tag,
|
||||
span.flagged-tag a,
|
||||
@@ -871,70 +848,144 @@ tr.flagged-tag a {
|
||||
.tag-management-table tr:hover td.tageditingon .tagnameedit img {
|
||||
opacity: 0.2;
|
||||
}
|
||||
h2.tag-heading,
|
||||
div#tag-description,
|
||||
div#tag-blogs,
|
||||
body.tag .managelink {
|
||||
padding: 5px;
|
||||
.path-tag .tag-relatedtags {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.path-tag .tag-management-box {
|
||||
text-align: right;
|
||||
}
|
||||
.path-tag .tag-index-toc {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.path-tag .tag-index-toc li,
|
||||
.path-tag .tag-management-box li {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.path-tag .tag-management-box li a.edittag {
|
||||
background-image: url([[pix:moodle|i/settings]]);
|
||||
}
|
||||
.path-tag .tag-management-box li a.flagasinappropriate {
|
||||
background-image: url([[pix:moodle|i/flagged]]);
|
||||
}
|
||||
.path-tag .tag-management-box li a.removefrommyinterests {
|
||||
background-image: url([[pix:moodle|t/delete]]);
|
||||
}
|
||||
.path-tag .tag-management-box li a.addtomyinterests {
|
||||
background-image: url([[pix:moodle|t/add]]);
|
||||
}
|
||||
.path-tag .tag-management-box li a {
|
||||
background-repeat: no-repeat;
|
||||
background-position: left;
|
||||
padding-left: 17px;
|
||||
}
|
||||
.tag_feed.media-list .media .itemimage {
|
||||
float: left;
|
||||
}
|
||||
.dir-rtl .tag_feed.media-list .media .itemimage {
|
||||
float: right;
|
||||
}
|
||||
.tag_feed.media-list .media .itemimage img {
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
}
|
||||
.tag_feed.media-list .media .media-body {
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.tag_feed .media .muted a {
|
||||
.muted();
|
||||
}
|
||||
.tag_cloud {
|
||||
text-align: center;
|
||||
}
|
||||
.tag_cloud .inline-list li {
|
||||
padding: 0px 0.2em;
|
||||
}
|
||||
.tag_cloud .tag_overflow {
|
||||
margin-top: 1em;
|
||||
font-style: italic;
|
||||
}
|
||||
.tag_cloud .s20 {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
font-size: 2.7em;
|
||||
}
|
||||
.tag_cloud .s19 {
|
||||
font-size: 1.5em;
|
||||
font-size: 2.6em;
|
||||
}
|
||||
.tag_cloud .s18 {
|
||||
font-size: 1.4em;
|
||||
font-weight: bold;
|
||||
font-size: 2.5em;
|
||||
}
|
||||
.tag_cloud .s17 {
|
||||
font-size: 1.4em;
|
||||
font-size: 2.4em;
|
||||
}
|
||||
.tag_cloud .s16 {
|
||||
font-size: 1.3em;
|
||||
font-weight: bold;
|
||||
font-size: 2.3em;
|
||||
}
|
||||
.tag_cloud .s15 {
|
||||
font-size: 1.3em;
|
||||
font-size: 2.2em;
|
||||
}
|
||||
.tag_cloud .s14 {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
font-size: 2.1em;
|
||||
}
|
||||
.tag_cloud .s13 {
|
||||
font-size: 2em;
|
||||
}
|
||||
.tag_cloud .s12 {
|
||||
font-size: 1.9em;
|
||||
}
|
||||
.tag_cloud .s11 {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
.tag_cloud .s10 {
|
||||
font-size: 1.7em;
|
||||
}
|
||||
.tag_cloud .s9 {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
.tag_cloud .s8 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.tag_cloud .s7 {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
.tag_cloud .s6 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.tag_cloud .s5 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.tag_cloud .s12,
|
||||
.tag_cloud .s11 {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tag_cloud .s10,
|
||||
.tag_cloud .s9 {
|
||||
.tag_cloud .s4 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.tag_cloud .s8,
|
||||
.tag_cloud .s7 {
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tag_cloud .s6,
|
||||
.tag_cloud .s5 {
|
||||
font-size: 1em;
|
||||
}
|
||||
.tag_cloud .s4,
|
||||
.tag_cloud .s3 {
|
||||
font-size: 1em;
|
||||
}
|
||||
.tag_cloud .s2 {
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tag_cloud .s2,
|
||||
.tag_cloud .s1 {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.tag_cloud .s0 {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.tag_cloud .s0 {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
.tag_list ul {
|
||||
display: inline;
|
||||
}
|
||||
.tag_list.hideoverlimit .overlimit {
|
||||
display:none;
|
||||
}
|
||||
.tag_list .tagmorelink {
|
||||
display:none;
|
||||
}
|
||||
.tag_list.hideoverlimit .tagmorelink {
|
||||
display:inline;
|
||||
}
|
||||
.tag_list.hideoverlimit .taglesslink {
|
||||
display:none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Web Service
|
||||
*/
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -394,14 +394,6 @@ body {
|
||||
/* Tags
|
||||
------------------------*/
|
||||
|
||||
#big-tag-cloud-box {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#tag-management-box {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tag-management-form {
|
||||
border: none;
|
||||
padding: 0 0 1em;
|
||||
@@ -411,12 +403,6 @@ body {
|
||||
margin: 1em 0
|
||||
}
|
||||
|
||||
#page-tag-index #tag-blogs {
|
||||
border-width: 0 0 1px;
|
||||
width: 65%;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
#tagblogentries {
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
@@ -427,43 +413,6 @@ body {
|
||||
margin: 0
|
||||
}
|
||||
|
||||
#big-tag-cloud-box {
|
||||
border: none;
|
||||
}
|
||||
|
||||
#tag-user-table {
|
||||
width: 95%;
|
||||
margin: 0 auto;
|
||||
border-width: 0 0 1px;
|
||||
}
|
||||
|
||||
.managelink {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#page-tag-index h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#page-tag-index #tag-management-box {
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
#page-tag-index .relatedpages {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#page-tag-manage .lastinitial {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#page-tag-manage .green {
|
||||
text-align: center;
|
||||
padding: 0 0 1em;
|
||||
}
|
||||
|
||||
/* Embedded Pages
|
||||
------------------------*/
|
||||
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2016010700.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2016011100.15; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user