From db32b054d114bdee5a7ffbc26ba0eb0b362689b5 Mon Sep 17 00:00:00 2001 From: scyrma Date: Fri, 22 Feb 2008 10:25:36 +0000 Subject: [PATCH] MDL-13404 - Merge from 1.9 --- backup/backuplib.php | 2 +- blocks/blog_tags/block_blog_tags.php | 2 +- blocks/tag_flickr/block_tag_flickr.php | 17 +- blocks/tag_youtube/block_tag_youtube.php | 59 +- blocks/tags/block_tags.php | 4 +- blog/edit.php | 57 +- blog/edit_form.php | 28 +- blog/header.php | 7 +- blog/index.php | 4 +- blog/lib.php | 17 +- lang/en_utf8/blog.php | 4 - lang/en_utf8/tag.php | 4 + lib/moodlelib.php | 43 +- tag/edit.php | 20 +- tag/edit_form.php | 2 +- tag/index.php | 37 +- tag/lib.php | 1916 +++++++--------------- tag/manage.php | 118 +- tag/search.php | 8 +- tag/tag_autocomplete.php | 2 +- theme/standard/styles_layout.css | 2 +- user/editadvanced.php | 2 +- user/editlib.php | 4 +- user/tag.php | 27 +- user/view.php | 8 +- 25 files changed, 811 insertions(+), 1583 deletions(-) diff --git a/backup/backuplib.php b/backup/backuplib.php index 3d6297e698866..9cf508fcece10 100644 --- a/backup/backuplib.php +++ b/backup/backuplib.php @@ -1280,7 +1280,7 @@ function backup_user_info ($bf,$preferences) { //Check if we have user tags to backup if (!empty($CFG->usetags)) { - if ($tags = get_item_tags('user', $user->id)) { //This return them ordered by default + if ($tags = tag_get_tags(array('type'=>'user', 'id'=>$user->id))) { //This return them ordered by default //Start USER_TAGS tag fwrite ($bf,start_tag("USER_TAGS",4,true)); //Write user tags fields diff --git a/blocks/blog_tags/block_blog_tags.php b/blocks/blog_tags/block_blog_tags.php index c92a863e6b2e9..693b4efc5df78 100644 --- a/blocks/blog_tags/block_blog_tags.php +++ b/blocks/blog_tags/block_blog_tags.php @@ -161,7 +161,7 @@ function get_content() { $this->content->text .= '
  • '. - $tag->name.'
  • '; + tag_display_name($tag) .' '; } $this->content->text .= "\n\n"; diff --git a/blocks/tag_flickr/block_tag_flickr.php b/blocks/tag_flickr/block_tag_flickr.php index d5be5b9c17e81..faf952a8c4bcf 100644 --- a/blocks/tag_flickr/block_tag_flickr.php +++ b/blocks/tag_flickr/block_tag_flickr.php @@ -39,12 +39,23 @@ function get_content() { return $this->content; } - $tagid = optional_param('id', 0, PARAM_INT); // tag id + $tagid = optional_param('id', 0, PARAM_INT); // tag id - for backware compatibility + $tag = optional_param('tag', '', PARAM_TAG); // tag + + if ($tag) { + $tag_object = tag_get_id($tag, TAG_RETURN_OBJECT); + } elseif (!$tag && $tagid) { + $tag_object = tag_get_tag_by_id($tagid); + } else { + // can't display the block if no tag was submitted! + // todo: something useful here. + error('Missing tag parameter'); + } //include related tags in the photo query ? - $tags_csv = tag_display_name(tag_by_id($tagid)); + $tags_csv = html_entity_decode(tag_display_name($tag_object)); if (!empty($this->config->includerelatedtags)) { - $tags_csv .= ',' . tag_names_csv( get_item_tags('tag',$tagid)); + $tags_csv .= ',' . tag_get_related_tags_csv(tag_get_related_tags(array('type'=>'tag','id'=>$tag_object->id)), TAG_RETURN_TEXT); } $tags_csv = urlencode($tags_csv); diff --git a/blocks/tag_youtube/block_tag_youtube.php b/blocks/tag_youtube/block_tag_youtube.php index 9593202476239..1f439e5a25e63 100644 --- a/blocks/tag_youtube/block_tag_youtube.php +++ b/blocks/tag_youtube/block_tag_youtube.php @@ -78,18 +78,29 @@ function get_videos_by_playlist(){ function get_videos_by_tag(){ - $tagid = optional_param('id', 0, PARAM_INT); // tag id + $tagid = optional_param('id', 0, PARAM_INT); // tag id - for backware compatibility + $tag = optional_param('tag', '', PARAM_TAG); // tag + + if ($tag) { + $tag_object = tag_get_id($tag, TAG_RETURN_OBJECT); + } elseif (!$tag && $tagid) { + $tag_object = tag_get_tag_by_id($tagid); + } else { + // can't display the block if no tag was submitted! + // todo: something useful here. + error('Missing tag parameter'); + } - $query_tag = tag_display_name(tag_by_id($tagid)); + $query_tag = html_entity_decode(tag_display_name($tag_object)); $query_tag = urlencode($query_tag); $numberofvideos = DEFAULT_NUMBER_OF_VIDEOS; - if( !empty($this->config->numberofvideos)) { + if ( !empty($this->config->numberofvideos) ) { $numberofvideos = $this->config->numberofvideos; } $request = 'http://www.youtube.com/api2_rest?method=youtube.videos.list_by_tag'; - $request .= '&dev_id=' . YOUTUBE_DEV_KEY; + $request .= '&dev_id='. YOUTUBE_DEV_KEY; $request .= "&tag={$query_tag}"; $request .= "&page=1"; $request .= "&per_page={$numberofvideos}"; @@ -99,9 +110,20 @@ function get_videos_by_tag(){ function get_videos_by_tag_and_category(){ - $tagid = optional_param('id', 0, PARAM_INT); // tag id + $tagid = optional_param('id', 0, PARAM_INT); // tag id - for backware compatibility + $tag = optional_param('tag', '', PARAM_TAG); // tag + + if ($tag) { + $tag_object = tag_get_id($tag, TAG_RETURN_OBJECT); + } elseif (!$tag && $tagid) { + $tag_object = tag_get_tag_by_id($tagid); + } else { + // can't display the block if no tag was submitted! + // todo: something useful here. + error('Missing tag parameter'); + } - $query_tag = tag_display_name(tag_by_id($tagid)); + $query_tag = html_entity_decode(tag_display_name($tag_object)); $query_tag = urlencode($query_tag); $numberofvideos = DEFAULT_NUMBER_OF_VIDEOS; @@ -158,18 +180,23 @@ function render_video_list($xmlobj){ $text .= '
    \n"; - return $text; } } diff --git a/blocks/tags/block_tags.php b/blocks/tags/block_tags.php index 28211a73d7083..210ebee3d5859 100644 --- a/blocks/tags/block_tags.php +++ b/blocks/tags/block_tags.php @@ -59,9 +59,9 @@ function get_content() { /// Get a list of tags - require_once($CFG->dirroot.'/tag/lib.php'); + require_once($CFG->dirroot.'/tag/taglib.php'); - $this->content->text = print_tag_cloud(popular_tags_count($this->config->numberoftags), false, 170, 70, true); + $this->content->text = tag_print_cloud($this->config->numberoftags, false, 170, 70, true); return $this->content; } diff --git a/blog/edit.php b/blog/edit.php index 1262f5437715e..1664c06aa0426 100755 --- a/blog/edit.php +++ b/blog/edit.php @@ -119,18 +119,12 @@ $post->action = $action; $strformheading = get_string('updateentrywithid', 'blog'); - if ($itemptags = get_item_tags('post', $post->id, 'ti.ordering ASC', 'id,rawname', '', '', 'default')) { - if ($ptags = records_to_menu($itemptags, 'id','rawname')) { - $post->ptags = implode(', ', $ptags); - } else { - $post->ptags = ''; - } + if ($itemptags = html_entity_decode(tag_get_tags_csv(array('type'=>'post', 'id'=>$post->id), TAG_RETURN_TEXT, 'default'))) { + $post->ptags = $itemptags; } - if ($itemotags = get_item_tags('post', $post->id, 'ti.ordering ASC', 'id,rawname', '', '', 'official')) { - if ($otags = records_to_menu($itemotags, 'id','rawname')) { - $post->otags = array_keys($otags); - } + if ($itemotags = tag_get_tags_array(array('type'=>'post', 'id'=>$post->id), 'official')) { + $post->otags = array_keys($itemotags); } break; default : @@ -231,7 +225,7 @@ function do_delete($post) { $status = delete_records('post', 'id', $post->id); //$status = delete_records('blog_tag_instance', 'entryid', $post->id) and $status; - untag_an_item('post', $post->id); + tag_set('post', $post->id, array()); blog_delete_old_attachments($post); @@ -293,7 +287,7 @@ function do_edit($post, $blogeditform) { //delete_records('blog_tag_instance', 'entryid', $post->id); //delete_records('tag_instance', 'itemid', $post->id, 'itemtype', 'blog'); - untag_an_item('post', $post->id); + //untag_an_item('post', $post->id); // add them back add_tags_info($post->id); @@ -309,38 +303,19 @@ function do_edit($post, $blogeditform) { * @param int postid - id of the blog */ function add_tags_info($postid) { - - global $USER; - - $post = get_record('post', 'id', $postid); - - /// Attach official tags + + $tags = array(); if ($otags = optional_param('otags', '', PARAM_INT)) { - foreach ($otags as $otag) { - $tag->tagid = $otag; - //insert_record('blog_tag_instance', $tag); - tag_an_item('post', $postid, $otag, 'official'); + foreach ($otags as $tagid) { + // TODO : make this use the tag name in the form + $tag = tag_get_tag_by_id($tagid); + $tags[] = $tag->name; } } - /// Attach Personal Tags - if ($ptags = optional_param('ptags', '', PARAM_NOTAGS)) { - $ptags = explode(',', $ptags); - foreach ($ptags as $ptag) { - $ptag = trim($ptag); - // check for existence - // it does not matter whether it is an offical tag or personal tag - // we do not want to have 1 copy of offical tag and 1 copy of personal tag (for the same tag) - if ($ctag = tag_by_name($ptag)) { - tag_an_item('post', $postid, $ctag->id); - } else { // create a personal tag - if ($tagid = tag_create($ptag)) { - if ($tagid = array_shift($tagid)) { - tag_an_item('post', $postid, $tagid); - } - } - } - } - } + $manual_tags = optional_param('ptags', '', PARAM_NOTAGS); + $tags = array_merge($tags, explode(',', $manual_tags)); + + tag_set('post', $postid, $tags); } ?> diff --git a/blog/edit_form.php b/blog/edit_form.php index 510edc5da43c6..f6426fb745cd2 100644 --- a/blog/edit_form.php +++ b/blog/edit_form.php @@ -34,8 +34,8 @@ function definition() { if (!empty($CFG->usetags)) { - $mform->addElement('header', 'tagshdr', get_string('tags', 'blog')); - $mform->createElement('select', 'otags', get_string('otags','blog')); + $mform->addElement('header', 'tagshdr', get_string('tags', 'tag')); + $mform->createElement('select', 'otags', get_string('otags','tag')); $js_escape = array( "\r" => '\r', @@ -46,28 +46,11 @@ function definition() { '\\' => '\\\\' ); - $otagsselEl =& $mform->addElement('select', 'otags', get_string('otags', 'blog'), array(), 'size="5"'); + $otagsselEl =& $mform->addElement('select', 'otags', get_string('otags', 'tag'), array(), 'size="5"'); $otagsselEl->setMultiple(true); $this->otags_select_setup(); - if (has_capability('moodle/blog:manageofficialtags', $sitecontext)){ - $deleteotagsmsg = strtr(get_string('deleteotagswarn', 'blog'), $js_escape); - $mform->registerNoSubmitButton('deleteotags'); - $mform->addElement('submit', 'deleteotags', get_string('delete'), - array('onclick'=>"return confirm('$deleteotagsmsg');")); - $mform->disabledIf('deleteotags', 'otags[]', 'noitemselected'); - $mform->setAdvanced('deleteotags'); - - $mform->registerNoSubmitButton('addotags'); - $otagsgrp = array(); - $otagsgrp[] =& $mform->createElement('text', 'otagsadd', get_string('addotags', 'blog')); - $otagsgrp[] =& $mform->createElement('submit', 'addotags', get_string('add')); - $mform->addGroup($otagsgrp, 'otagsgrp', get_string('addotags','blog'), array(' '), false); - $mform->setType('otagsadd', PARAM_NOTAGS); - $mform->setAdvanced('otagsgrp'); - } - - $mform->addElement('textarea', 'ptags', get_string('ptags', 'blog'), array('cols'=>'40', 'rows'=>'5')); + $mform->addElement('textarea', 'ptags', get_string('ptags', 'tag'), array('cols'=>'40', 'rows'=>'5')); $mform->setType('ptagsadd', PARAM_NOTAGS); } @@ -99,9 +82,6 @@ function otags_select_setup(){ } if ($otags = get_records_sql_menu('SELECT id, name from '.$CFG->prefix.'tag WHERE tagtype=\'official\' ORDER by name ASC')){ $otagsselect->loadArray($otags); - } else { - // removing this causes errors - //$mform->removeElement('otags'); } } diff --git a/blog/header.php b/blog/header.php index 0200e9b0128be..deb58421fc68f 100755 --- a/blog/header.php +++ b/blog/header.php @@ -84,11 +84,8 @@ if (!empty($tagid)) { $taginstance = get_record('tag', 'id', $tagid); -} else { - $tagid = ''; - if (!empty($tag)) { - $taginstance = tag_id($tag); - } +} elseif (!empty($tag)) { + $taginstance = tag_id($tag); } /// navigations diff --git a/blog/index.php b/blog/index.php index f472923abc2da..f0b21b2a3a040 100755 --- a/blog/index.php +++ b/blog/index.php @@ -12,10 +12,10 @@ $id = optional_param('id', 0, PARAM_INT); $start = optional_param('formstart', 0, PARAM_INT); -$userid = optional_param('userid',0,PARAM_INT); +$userid = optional_param('userid', 0, PARAM_INT); $tag = optional_param('tag', '', PARAM_NOTAGS); $tagid = optional_param('tagid', 0, PARAM_INT); -$postid = optional_param('postid',0,PARAM_INT); +$postid = optional_param('postid', 0, PARAM_INT); $filtertype = optional_param('filtertype', '', PARAM_ALPHA); $filterselect = optional_param('filterselect', 0, PARAM_INT); diff --git a/blog/lib.php b/blog/lib.php index 2e1e9f3d57c51..76c43eb5d5c72 100755 --- a/blog/lib.php +++ b/blog/lib.php @@ -222,22 +222,11 @@ function blog_print_entry($blogEntry, $viewtype='full', $filtertype='', $filters echo $attachedimages; /// Links to tags - /* - if ($blogtags = get_records_sql('SELECT t.* FROM '.$CFG->prefix.'tags t, '.$CFG->prefix.'blog_tag_instance ti - WHERE t.id = ti.tagid - AND ti.entryid = '.$blogEntry->id)) { - */ - if (!empty($CFG->usetags) && ($blogtags = get_item_tags('post', $blogEntry->id))) { + if ( !empty($CFG->usetags) && ($blogtags = tag_get_tags_csv(array('type'=>'post', 'id'=>$blogEntry->id))) ) { echo '
    '; if ($blogtags) { - print_string('tags'); - echo ': '; - foreach ($blogtags as $key => $blogtag) { - //$taglist[] = ''.tag_display_name($blogtag).''; // Blog tag only - $taglist[] = ''.tag_display_name($blogtag).''; // General tag link - } - echo implode(', ', $taglist); - } + print(get_string('tags', 'tag') .': '. $blogtags); + } echo '
    '; } diff --git a/lang/en_utf8/blog.php b/lang/en_utf8/blog.php index 4dd4b349d56d5..51a2c1afc533e 100755 --- a/lang/en_utf8/blog.php +++ b/lang/en_utf8/blog.php @@ -3,8 +3,6 @@ $string['addnewentry'] = 'Add a new entry'; -$string['addotags'] = 'Add official tags'; -$string['addptags'] = 'Add user-defined tags'; $string['backupblogshelp'] = 'If enabled then blogs will be included in SITE automated backups'; $string['blockmenutitle'] = 'Blog Menu'; $string['blocktagstitle'] = 'Blog Tags'; @@ -35,10 +33,8 @@ $string['notallowedtoedit'] = 'You are not allowed to edit this entry'; $string['numberofentries'] = 'Entries: $a'; $string['numberoftags'] = 'Number of tags to display'; -$string['otags'] = 'Official tags'; $string['pagesize'] = 'Number of blog entries per Page'; $string['personalblogs'] = 'Users can only see their own blog'; -$string['ptags'] = 'User defined tags (Comma separated)'; $string['publishto'] = 'Publish to'; $string['publishtonoone'] = 'Yourself (draft)'; $string['publishtosite'] = 'Anyone on this site'; diff --git a/lang/en_utf8/tag.php b/lang/en_utf8/tag.php index 1daf7472cb915..046e304060c79 100644 --- a/lang/en_utf8/tag.php +++ b/lang/en_utf8/tag.php @@ -1,5 +1,7 @@ substr($param, 0, TAG_MAX_LENGTH); - //numeric tags not allowed - return is_numeric($param) ? '' : $param; - - case PARAM_TAGLIST: - $tags = explode(',', $param); - $result = array(); - foreach ($tags as $tag) { - $res = clean_param($tag, PARAM_TAG); - if ($res != '') { - $result[] = $res; - } - } - if ($result) { - return implode(',', $result); - } else { - return ''; - } + $param = $textlib->substr(trim($param), 0, TAG_MAX_LENGTH); + return $param; default: // throw error, switched parameters in optional_param or another serious problem error("Unknown parameter type: $type"); diff --git a/tag/edit.php b/tag/edit.php index b91975787543f..33294df4e2388 100644 --- a/tag/edit.php +++ b/tag/edit.php @@ -12,9 +12,15 @@ error(get_string('tagsaredisabled', 'tag')); } -$tagid = required_param('id', PARAM_INT); // user id - -$tag = tag_by_id($tagid); +$tag_id = optional_param('id', 0, PARAM_INT); +$tag_name = optional_param('tag', '', PARAM_TAG); +if ($tag_name) { + $tag = array_shift(tag_get_id($tag_name, TAG_RETURN_OBJECT)); +} elseif ( $tag_id ) { + $tag = tag_get_tag_by_id($tag_id); +} else { + error('A required parameter was missing'); +} $tagname = tag_display_name($tag); //Editing a tag requires moodle/tag:edit capability @@ -22,7 +28,8 @@ require_capability('moodle/tag:edit', $systemcontext); // set the relatedtags field of the $tag object that will be passed to the form -$tag->relatedtags = tag_names_csv(get_item_tags('tag',$tagid)); +//$tag->relatedtags = tag_names_csv(get_item_tags('tag',$tagid)); +$tag->relatedtags = tag_get_related_tags_csv(tag_get_related_tags($tag->id), TAG_RETURN_TEXT); if (can_use_html_editor()) { $options = new object(); @@ -47,9 +54,10 @@ } //updated related tags - update_item_tags('tag', $tagnew->id, $tagnew->relatedtags); + tag_set('tag', $tagnew->id, explode(',', trim($tagnew->relatedtags))); + //var_dump($tagnew); die(); - redirect($CFG->wwwroot.'/tag/index.php?id='.$tagnew->id); + redirect($CFG->wwwroot.'/tag/index.php?tag='.rawurlencode($tag->name)); // must use $tag here, as the name isn't in the edit form } diff --git a/tag/edit_form.php b/tag/edit_form.php index ec954fdbbabef..e96429439425f 100644 --- a/tag/edit_form.php +++ b/tag/edit_form.php @@ -18,7 +18,7 @@ function definition () { $mform->addElement('html', '
    '); $mform->addElement('textarea', 'relatedtags', get_string('relatedtags','tag'), 'cols="50" rows="3"'); - $mform->setType('relatedtags', PARAM_TAGLIST); + $mform->setType('relatedtags', PARAM_TAG); $mform->addElement('html', '
    '); $mform->addElement('html', '
    '); diff --git a/tag/index.php b/tag/index.php index 921caf8e1f890..f538442f0929e 100644 --- a/tag/index.php +++ b/tag/index.php @@ -12,14 +12,18 @@ error(get_string('tagsaredisabled', 'tag')); } -$tagid = optional_param('id', 0, PARAM_INT); // tag id +$tagid = optional_param('id', 0, PARAM_INT); // tag id +$tag_name = optional_param('tag', '', PARAM_TAG); // tag +if ($tag_name) { + $tag = tag_get_id($tag_name, TAG_RETURN_OBJECT); +} elseif ($tagid) { + $tag = tag_get_tag_by_id($tagid); // for backward compatibility +} $edit = optional_param('edit', -1, PARAM_BOOL); -$userpage = optional_param('userpage', 0, PARAM_INT); // which page to show +$userpage = optional_param('userpage', 0, PARAM_INT); // which page to show $perpage = optional_param('perpage', 24, PARAM_INT); -$tag = tag_by_id($tagid); - -if (!$tag) { +if (!isset($tag) || !$tag->id) { redirect($CFG->wwwroot.'/tag/search.php'); } @@ -32,10 +36,8 @@ $USER->editing = $edit; } - $PAGE->print_header(); - echo ''; echo ''; @@ -49,8 +51,6 @@ echo ''; } - - //----------------- middle column ----------------- echo '
    '; @@ -59,17 +59,14 @@ $systemcontext = get_context_instance(CONTEXT_SYSTEM); if ($tag->flag > 0 && has_capability('moodle/tag:manage', $systemcontext)) { - $tagname = '' . $tagname . ''; + $tagname = '' . rawurlencode($tagname) . ''; } print_heading($tagname, '', 2, 'headingblock header tag-heading'); +tag_print_management_box($tag); +tag_print_description_box($tag); -print_tag_management_box($tag); - -print_tag_description_box($tag); - - -$usercount = count_items_tagged_with($tag->id,'user'); +$usercount = tag_record_count('user', $tag->id); if ($usercount > 0) { @@ -82,16 +79,10 @@ $baseurl = $CFG->wwwroot.'/tag/index.php?id=' . $tag->id; print_paging_bar($usercount, $userpage, $perpage, $baseurl.'&', 'userpage'); - - print_tagged_users_table($tag, $userpage * $perpage, $perpage); - + tag_print_tagged_users_table($tag, $userpage * $perpage, $perpage); print_box_end(); - } - - - // Print last 10 blogs // I was not able to use get_items_tagged_with() because it automatically diff --git a/tag/lib.php b/tag/lib.php index 788c49e25a5af..d6a1b53323973 100644 --- a/tag/lib.php +++ b/tag/lib.php @@ -1,1537 +1,827 @@ -tagtype = $tag_type; - $tag_object->userid = $USER->id; - - $systemcontext = get_context_instance(CONTEXT_SYSTEM); - $can_create_tags = has_capability('moodle/tag:create',$systemcontext); - - $norm_tag_names_csv = ''; - foreach ($tags as $tag) { - - // rawname keeps the original casing of the string - $tag_object->rawname = tag_normalize($tag, false); - - // name lowercases the string - $tag_object->name = tag_normalize($tag); - $norm_tag_names_csv .= $tag_object->name . ','; - - $tag_object->timemodified = time(); - - $exists = record_exists('tag', 'name', $tag_object->name); - - if ( !$exists && is_tag_name_valid($tag_object->name) ) { - if ($can_create_tags) { - insert_record('tag', $tag_object); - } - else { - require_capability('moodle/tag:create',$systemcontext); - } - } - } - - $norm_tag_names_csv = $textlib->substr($norm_tag_names_csv,0,-1); - - return tags_id( $norm_tag_names_csv ); - -} - -/** - * Deletes tags + * @version: $Id$ + * @licence http://www.gnu.org/copyleft/gpl.html GNU Public License + * @package moodlecore + * + * A "tag string" is always a rawurlencode'd string. This is the same behavior + * as http://del.icio.us + * @see http://www.php.net/manual/en/function.urlencode.php + * + * Tag strings : you can use any character in tags, except the comma (which is + * the separator) and the '\' (backslash). Note that many spaces (or other + * blank characters) will get "compressed" into one. + * + * A "record" is a php array (note that an object will work too) that contains + * the following variables : + * - type: the table containing the record that we are tagging (eg: for a + * blog, this is table 'post', and for a user it is 'user') + * - id: the id of the record * - * Ex 1: tag_delete('a very cool tag, another nice tag') - * Will delete the tags with names 'a very cool tag' and 'another nice tag' from the 'tags' table, if they exist! + * TODO: turn this into a full-fledged categorization system. This could start + * by modifying (removing, probably) the 'tag type' to use another table + * describing the relationship between tags (parents, sibling, etc.), which + * could then be merged with the 'course categorization' system... * - * Ex 2: tag_delete('computers, 123, 143, algorithms') - * Will delete tags with names 'computers' and 'algorithms' and tags with ids 123 and 143. + * BASIC INSTRUCTIONS : + * - to "tag a blog post" (for example): + * tag_set('post', $blog_post->id, $array_of_tags); * + * - to "remove all the tags on a blog post": + * tag_set('post', $blog_post->id, array()); * - * @param string $tag_names_or_ids_csv **normalized** tag names or ids of the tags to be deleted. + * Tag set will create tags that need to be created. */ -function tag_delete($tag_names_or_ids_csv) { +define('TAG_RETURN_ARRAY', 0); +define('TAG_RETURN_OBJECT', 1); +define('TAG_RETURN_TEXT', 2); +define('TAG_RETURN_HTML', 3); - //covert all names to ids - $tag_ids_csv = tag_id_from_string($tag_names_or_ids_csv); +define('TAG_CASE_LOWER', 0); +define('TAG_CASE_ORIGINAL', 1); - //put apostrophes - $tag_ids_csv_with_apos = "'" . str_replace(',', "','", $tag_ids_csv) . "'"; +require_once($CFG->dirroot .'/tag/locallib.php'); - // tag instances needs to be deleted as well - // delete_records_select('tag_instance',"tagid IN ($tag_ids_csv_with_apos)"); - // Luiz: (in near future) tag instances should be cascade deleted by RDMS referential integrity constraints, when moodle implements it - // For now, tag_instance orphans should be removed using tag_instance_table_cleanup() - - $return1 = delete_records_select('tag',"name IN ($tag_ids_csv_with_apos)"); - $return2 = delete_records_select('tag',"id IN ($tag_ids_csv_with_apos)"); - - return $return1 && $return2; - -} +/////////////////////////////////////////////////////// +/////////////////// PUBLIC TAG API //////////////////// /** - * Get all tags from the records - * - * @param string $tag_types_csv (optional, default value is "default". If '*' is passed, tags of any type will be returned). - * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter). - * @param string $fields a comma separated list of fields to return - * (optional, by default 'id, tagtype, name, rawname, flag'). The first field will be used as key for the - * array so must be a unique field such as 'id'. + * Delete one or more tag, and all their instances if there are any left. + * + * @param mixed $tagids one tagid (int), or one array of tagids to delete + * @return bool true on success, false otherwise */ -function get_all_tags($tag_types_csv="default", $sort='name ASC', $fields=DEFAULT_TAG_TABLE_FIELDS) { +function tag_delete($tagids) { - if ($tag_types_csv == '*'){ - return get_records('tag', '', '', $sort, $fields); + if (!is_array($tagids)) { + $tagids = array($tagids); } - $tag_types_csv_with_apos = "'" . str_replace(',', "','", $tag_types_csv ) . "'"; + $success = true; + foreach( $tagids as $tagid ) { + if (is_null($tagid)) { // can happen if tag doesn't exists + continue; + } + // only delete the main entry if there were no problems deleting all the + // instances - that (and the fact we won't often delete lots of tags) + // is the reason for not using delete_records_select() + if ( delete_records('tag_instance', 'tagid', $tagid) ) { + $success &= (bool) delete_records('tag', 'id', $tagid); + } + } - return get_records_list('tag', 'tagtype', $tag_types_csv_with_apos, $sort, $fields); + return $success; } /** - * Determines if a tag exists - * - * @param string $tag_name_or_id **normalized** tag name, or an id. - * @return true if exists or false otherwise + * Delete one instance of a tag. If the last instance was deleted, it will + * also delete the tag, unless it's type is 'official'. * + * @param array $record the record for which to remove the instance + * @param int $tagid the tagid that needs to be removed + * @return bool true on success, false otherwise */ -function tag_exists($tag_name_or_id) { - - if (is_numeric($tag_name_or_id)) { - return record_exists('tag', 'id', $tag_name_or_id); +function tag_delete_instance($record, $tagid) { + global $CFG; + if ( delete_records('tag_instance', 'tagid', $tagid, 'itemtype', $record['type'], 'itemid', $record['id']) ) { + if ( !record_exists_sql('SELECT * FROM '. $CFG->prefix .'tag tg, '. $CFG->prefix .'tag_instance ti '. + 'WHERE (tg.id = ti.tagid AND ti.tagid = '. $tagid .') OR '. + '(tg.id = '. $tagid .' AND tg.tagtype = "official")') ) { + return tag_delete($tagid); + } } else { - // normalised tag names are always lowercase only - return record_exists('tag', 'name', moodle_strtolower($tag_name_or_id)); + return false; } } /** - * Function that returns the id of a tag + * Function that returns the name that should be displayed for a specific tag * - * @param String $tag_name **normalized** name of the tag - * @return int id of the matching tag + * @param object $tag_object a line out of tag table, as returned by the adobd functions + * @return string */ -function tag_id($tag_name) { - $tag = get_record('tag', 'name', trim($tag_name), '', '', '', '', 'id'); +function tag_display_name($tag_object) { - if ($tag){ - return $tag->id; + if(!isset($tag_object->name)) { + return ''; } - else{ - return false; + + if( empty($CFG->keeptagnamecase) ) { + //this is the normalized tag name + $textlib = textlib_get_instance(); + return htmlspecialchars($textlib->strtotitle($tag_object->name)); + } + else { + //original casing of the tag name + return htmlspecialchars($tag_object->rawname); } } /** - * Function that returns the ids of tags - * - * Ex: tags_id('computers, algorithms') + * Find all records tagged with a tag of a given type ('post', 'user', etc.) * - * @param String $tag_names_csv comma separated **normalized** tag names. - * @return Array array with the tags ids, indexed by their **normalized** names + * @param string $tag tag to look for + * @param string $type type to restrict search to. If null, every matching + * record will be returned + * @return array of matching objects, indexed by record id, from the table containing the type requested */ -function tags_id($tag_names_csv) { - - $normalized_tag_names_csv = tag_normalize($tag_names_csv); - $tag_names_csv_with_apos = "'" . str_replace(',', "','", $normalized_tag_names_csv ) . "'"; - - $tags_ids = array(); +function tag_find_records($tag, $type) { + + global $CFG; - if ($tag_objects = get_records_list('tag','name', $tag_names_csv_with_apos, "" , "name, id" )) { - foreach ($tag_objects as $tag) { - $tags_ids[$tag->name] = $tag->id; - } + if (!$tag || !$type) { + return array(); } - return $tags_ids; + $tagid = tag_get_id($tag); + + $query = "SELECT it.* ". + "FROM {$CFG->prefix}{$type} it INNER JOIN {$CFG->prefix}tag_instance tt ON it.id = tt.itemid ". + "WHERE tt.itemtype = '{$type}' AND tt.tagid = '{$tagid}'"; + + return get_records_sql($query); } /** - * Function that returns the name of a tag + * Get the array of db record of tags associated to a record (instances). Use + * tag_get_tags_csv to get the same information in a comma-separated string. * - * @param int $tag_id id of the tag - * @return String name of the tag with the id passed + * @param array $record the record for which we want to get the tags + * @param string $type the tag type (either 'default' or 'official'). By default, + * all tags are returned. + * @return array the array of tags */ -function tag_name($tag_id) { - $tag = get_record('tag', 'id', $tag_id, '', '', '', '', 'name'); +function tag_get_tags($record, $type=null) { + + global $CFG; - if ($tag){ - return $tag->name; + if ($type) { + $type = "AND tg.tagtype = '$type'"; + } + + $tags = get_records_sql('SELECT tg.id, tg.tagtype, tg.name, tg.rawname, tg.flag, ti.ordering '. + 'FROM '. $CFG->prefix .'tag_instance ti INNER JOIN '. $CFG->prefix .'tag tg ON tg.id = ti.tagid '. + 'WHERE ti.itemtype = "'. $record['type'] .'" AND ti.itemid = "'. $record['id'] .'" '. $type .' '. + 'ORDER BY ti.ordering ASC'); + // This version of the query, reversing the ON clause, "correctly" returns + // a row with NULL values for instances that are still in the DB even though + // the tag has been deleted. This shouldn't happen, but if it did, using + // this query could help "clean it up". This causes bugs at this time. + //$tags = get_records_sql('SELECT ti.tagid, tg.tagtype, tg.name, tg.rawname, tg.flag, ti.ordering '. + // 'FROM '. $CFG->prefix .'tag_instance ti LEFT JOIN '. $CFG->prefix .'tag tg ON ti.tagid = tg.id '. + // 'WHERE ti.itemtype = "'. $record['type'] .'" AND ti.itemid = "'. $record['id'] .'" '. $type .' '. + // 'ORDER BY ti.ordering ASC'); + + if (!$tags) { + return array(); + } else { + return $tags; } - else{ - return ''; +} + +/** + * Get the array of tags display names, indexed by id. + * + * @param array $record the record for which we want to get the tags + * @param string $type the tag type (either 'default' or 'official'). By default, + * all tags are returned. + * @return array the array of tags (with the value returned by tag_display_name), indexed by id + */ +function tag_get_tags_array($record, $type=null) { + $tags = array(); + foreach(tag_get_tags($record, $type) as $tag) { + $tags[$tag->id] = tag_display_name($tag); } + return $tags; } /** - * Function that retrieves the names of tags given their ids + * Get a comma-separated string of tags associated to a record. Use tag_get_tags + * to get the same information in an array. * - * @param String $tag_ids_csv comma separated tag ids - * @return Array an array with the tags names, indexed by their ids + * @param array $record the record for which we want to get the tags + * @param int $html either TAG_RETURN_HTML or TAG_RETURN_TEXT, depending + * on the type of output desired + * @param string $type either 'official' or 'default', if null, all tags are + * returned + * @return string the comma-separated list of tags. */ - -function tags_name($tag_ids_csv) { - - //remove any white spaces - $tag_ids_csv = str_replace(' ', '', $tag_ids_csv); - - $tag_ids_csv_with_apos = "'" . str_replace(',', "','", $tag_ids_csv ) . "'"; - - $tag_objects = get_records_list('tag','id', $tag_ids_csv_with_apos, "" , "name, id" ); +function tag_get_tags_csv($record, $html=TAG_RETURN_HTML, $type=null) { + global $CFG; $tags_names = array(); - foreach ($tag_objects as $tag) { - $tags_names[$tag->id] = $tag->name; + foreach( tag_get_tags($record, $type) as $tag ) { + if ($html == TAG_RETURN_TEXT) { + $tags_names[] = tag_display_name($tag); + } else { // TAG_RETURN_HTML + $tags_names[] = ''. tag_display_name($tag) .''; + } } - - return $tags_names; + return implode(', ', $tags_names); } /** - * Function that returns the name of a tag for display. + * Get an array of tag ids associated to a record. * - * @param mixed $tag_object - * @return string + * @param array $record the record for which we want to get the tags + * @return array of tag ids, indexed and sorted by 'ordering' */ -function tag_display_name($tag_object){ +function tag_get_tags_ids($record) { + + $tag_ids = array(); + foreach( tag_get_tags($record) as $tag ) { + $tag_ids[$tag->ordering] = $tag->id; + } + ksort($tag_ids); + return $tag_ids; +} +/** + * Returns the database ID of a set of tags. + * + * @param mixed $tags one tag, or array of tags, to look for. + * @param bool $return_object get the object returned by get_recordset_sql instead + * of the id only (default: false) + * @return mixed tag-indexed array of ids (or objects, if second parameter is TAG_RETURN_OBJECT), or only an int, if only one tag is given *and* the second parameter is null. No value for a key means the tag wasn't found. + */ +function tag_get_id($tags, $return_value=null) { global $CFG; + static $tag_id_cache = array(); - if( empty($CFG->keeptagnamecase) ) { - //this is the normalized tag name - $textlib = textlib_get_instance(); - return $textlib->strtotitle($tag_object->name); - } - else { - //original casing of the tag name - return $tag_object->rawname; + $return_an_int = false; + if (!is_array($tags)) { + if(is_null($return_value) || $return_value == TAG_RETURN_OBJECT) { + $return_an_int = true; + } + $tags = array($tags); + } + + $result = array(); + + //TODO: test this and see if it helps performance without breaking anything + //foreach($tags as $key => $tag) { + // $clean_tag = moodle_strtolower($tag); + // if ( array_key_exists($clean_tag), $tag_id_cache) ) { + // $result[$clean_tag] = $tag_id_cache[$clean_tag]; + // $tags[$key] = ''; // prevent further processing for this one. + // } + //} + + $tags = array_values(tag_normalize($tags)); + foreach($tags as $key => $tag) { + $tags[$key] = addslashes(moodle_strtolower($tag)); + $result[moodle_strtolower($tag)] = null; // key must exists : no value for a key means the tag wasn't found. + } + $tag_string = "'". implode("', '", $tags) ."'"; + + if ($rs = get_recordset_sql('SELECT * FROM '. $CFG->prefix .'tag WHERE name in ('. $tag_string .') order by name')) { + while ($record = rs_fetch_next_record($rs)) { + if ($return_value == TAG_RETURN_OBJECT) { + $result[$record->name] = $record; + } else { // TAG_RETURN_ARRAY + $result[$record->name] = $record->id; + } + } } -} - -/** - * Function that retrieves a tag object by its id - * - * @param String $tag_id - * @return mixed a fieldset object containing the first matching record, or false if none found - */ -function tag_by_id($tag_id) { + if ($return_an_int) { + return array_pop($result); + } - return get_record('tag','id',$tag_id); + return $result; } /** - * Function that retrieves a tag object by its name + * Get a tag as an object (line) returned by get_recordset_sql * - * @param String $tag_name - * @return mixed a fieldset object containing the first matching record, or false if none found + * @param int $tagid a tag id + * @return object a line returned from get_recordset_sql, or false */ -function tag_by_name($tag_name) { - $tag = get_record('tag','name',$tag_name); - return $tag; +function tag_get_tag_by_id($tagid) { + global $CFG; + $rs = get_recordset_sql('SELECT * FROM '. $CFG->prefix .'tag WHERE id = '. $tagid); + return rs_fetch_next_record($rs); } /** - * In a comma separated string of ids or names of tags, replaces all tag names with their correspoding ids - * - * Ex: - * Suppose the DB contains only the following entries in the tags table: - * id name - * 10 moodle - * 12 science - * 22 education - * - * tag_id_from_string('moodle, 12, education, programming, 33, 11') - * will return '10,12,22,,33,11' + * Returns tags related to a tag * - * This is a helper function used by functions of this API to process function arguments ($tag_name_or_id) + * Related tags of a tag come from two sources: + * - manually added related tags, which are tag_instance entries for that tag + * - correlated tags, which are a calculated * - * @param string $tag_names_or_ids_csv comma separated **normalized** names or ids of tags - * @return int comma separated ids of the tags + * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag + * @param int $limitnum return a subset comprising this many records (optional, default is 10) + * @return array an array of tag objects */ -function tag_id_from_string($tag_names_or_ids_csv) { - - $tag_names_or_ids = explode(',', $tag_names_or_ids_csv); - - $tag_ids = array(); - foreach ($tag_names_or_ids as $name_or_id) { - - if (is_numeric($name_or_id)){ - $tag_ids[] = trim($name_or_id); - } - elseif (is_string($name_or_id)) { - $tag_ids[] = tag_id( $name_or_id ); - } +function tag_get_related_tags($tagid, $limitnum=10) { + //gets the manually added related tags + if (!$related_tags = tag_get_tags(array('type'=>'tag', 'id'=>$tagid))) { + $related_tags = array(); } - $tag_ids_csv = implode(',',$tag_ids); - $tag_ids_csv = str_replace(' ', '', $tag_ids_csv); + //gets the correlated tags + $automatic_related_tags = tag_get_correlated($tagid, $limitnum); + if (is_array($automatic_related_tags)) { + $related_tags = array_merge($related_tags, $automatic_related_tags); + } - return $tag_ids_csv; + return array_slice(object_array_unique($related_tags), 0 , $limitnum); } -/** - * In a comma separated string of ids or names of tags, replaces all tag ids with their correspoding names - * - * Ex: - * Suppose the DB contains only the following entries in the tags table: - * id name - * 10 moodle - * 12 science - * 22 education - * - * tag_name_from_string('mOOdle, 10, HiStOrY, 17, 22') - * will return the string 'mOOdle,moodle,HiStOrY,,education' +/** + * Get a comma-separated list of tags related to another tag. * - * This is a helper function used by functions of this API to process function arguments ($tag_name_or_id) - * - * @param string $tag_names_or_ids_csv comma separated names or ids of tags - * @return int comma separated names of the tags + * @param array $related_tags the array returned by tag_get_related_tags + * @param int $html either TAG_RETURN_HTML (default) or TAG_RETURN_TEXT : return html links, or just text. + * @return string comma-separated list */ -function tag_name_from_string($tag_names_or_ids_csv) { - - $tag_names_or_ids = explode(',', $tag_names_or_ids_csv); - - $tag_names = array(); - foreach ($tag_names_or_ids as $name_or_id) { +function tag_get_related_tags_csv($related_tags, $html=TAG_RETURN_HTML) { + global $CFG; - if (is_numeric($name_or_id)){ - $tag_names[] = tag_name($name_or_id); - } - elseif (is_string($name_or_id)) { - $tag_names[] = trim($name_or_id); + $tags_names = array(); + foreach($related_tags as $tag) { + if ( $html == TAG_RETURN_TEXT) { + $tags_names[] = rawurlencode(tag_display_name($tag)); + } else { + // TAG_RETURN_HTML + $tags_names[] = ''. rawurlencode(tag_display_name($tag)) .''; } - } - - $tag_names_csv = implode(',',$tag_names); - - return $tag_names_csv; - + return implode(', ', $tags_names); } - /** - * Determines if a tag name is valid + * Change the "value" of a tag, and update the associated 'name'. * - * @param string $name - * @return boolean + * @param int $tagid the id of the tag to modify + * @param string $newtag the new name + * @return bool true on success, false otherwise */ -function is_tag_name_valid($name){ +function tag_rename($tagid, $newtag) { - $normalized = tag_normalize($name); + if (! $newtag_clean = array_shift(tag_normalize($newtag, TAG_CASE_ORIGINAL)) ) { + return false; + } - return !strcmp($normalized, $name) && !empty($name) && !is_numeric($name); + if ( tag_get_id($newtag_clean) ) { + // 'newtag' already exists and merging tags is not yet supported. + return false; + } + if ($tag = get_record('tag', 'id', $tagid)) { + $tag->rawname = addslashes($newtag_clean); + $tag->name = addslashes(moodle_strtolower($newtag_clean)); + $tag->timemodified = time(); + return update_record('tag', $tag); + } + return false; } - /** - * Associates a tag with an item - * - * Ex 1: tag_an_item('user', '1', 'hisTOrY, RELIGIONS, roman' ) - * This will tag an user whose id is 1 with "history", "religions", "roman" - * If the tag names passed do not exist, they will get created. - * - * Ex 2: tag_an_item('user', '1', 'hisTory, 12, 11, roman') - * This will tag an user whose id is 1 with 'history', 'roman' and with tags of ids 12 and 11 + * Set the tags assigned to a record. This overwrites the current tags. + * + * This function is meant to be fed the string coming up from the user + * interface, which contains all tags assigned to a record. * - * @param string $item_type name of the table where the item is stored. Ex: 'user' - * @param string $item_id id of the item to be tagged - * @param string $tag_names_or_ids_csv comma separated tag names (can be unormalized) or ids of existing tags - * @param string $tag_type type of the tags that are beeing added (optional, default value is "default") + * @param string $record_type the type of record to tag ('post' for blogs, + * 'user' for users, 'tag' for tags, etc. + * @param int $record_id the id of the record to tag + * @param array $tags the array of tags to set on the record. If + * given an empty array, all tags will be removed. + * @return void */ +function tag_set($record_type, $record_id, $tags) { + global $db; -function tag_an_item($item_type, $item_id, $tag_names_or_ids_csv, $tag_type="default") { - - global $CFG; - //convert any tag ids passed to their corresponding tag names - $tag_names_csv = tag_name_from_string($tag_names_or_ids_csv); - - //create the tags - $tags_created_ids = tag_create($tag_names_csv,$tag_type); - - //tag instances of an item are ordered, get the last one - $query = " - SELECT - MAX(ordering) AS max_order - FROM - {$CFG->prefix}tag_instance ti - WHERE - ti.itemtype = '{$item_type}' - AND - ti.itemid = '{$item_id}' - "; - - $max_order = get_field_sql($query); - - $tag_names = explode(',', tag_normalize($tag_names_csv)); - - $ordering = array(); - foreach($tag_names as $tag_name){ - $ordering[$tag_name] = ++$max_order; - } - - //setup tag_instance object - $tag_instance = new StdClass; - $tag_instance->itemtype = $item_type; - $tag_instance->itemid = $item_id; + $record = array('type' => $record_type, 'id' => $record_id); + $tags_ids = tag_get_id($tags, TAG_RETURN_ARRAY); // force an array, even if we only have one tag. + $cleaned_tags = tag_normalize($tags); + //echo 'tags-in-tag_set'; var_dump($tags); var_dump($tags_ids); var_dump($cleaned_tags); - //create tag instances - foreach ($tags_created_ids as $tag_normalized_name => $tag_id) { + $current_ids = tag_get_tags_ids($record); + //var_dump($current_ids); + $tags_to_assign = array(); - $tag_instance->tagid = $tag_id; - $tag_instance->ordering = $ordering[$tag_normalized_name]; - $tag_instance->timemodified = time(); - $tag_instance_exists = get_record('tag_instance', 'tagid', $tag_id, 'itemtype', $item_type, 'itemid', $item_id); + // for data coherence reasons, it's better to remove deleted tags + // before adding new data: ordering could be duplicated. + foreach($current_ids as $current_id) { + if (!in_array($current_id, $tags_ids)) { + tag_delete_instance($record, $current_id); + } + } - if (!$tag_instance_exists) { - insert_record('tag_instance',$tag_instance); + foreach($tags as $ordering => $tag) { + $tag = trim($tag); + if (!$tag) { + continue; } - else { - $tag_instance_exists->timemodified = time(); - $tag_instance_exists->ordering = $ordering[$tag_normalized_name]; - update_record('tag_instance',$tag_instance_exists); + + $clean_tag = $cleaned_tags[$tag]; + $tag_current_id = $tags_ids[$clean_tag]; + + if ( is_null($tag_current_id) ) { + // create new tags + //echo "call to add tag $tag\n"; + $new_tag = tag_add($tag); + tag_assign($record, $new_tag[$clean_tag], $ordering); + } + elseif ( empty($current_ids) || !in_array($tag_current_id, $current_ids) ) { + // assign existing tags + tag_assign($record, $tag_current_id, $ordering); + } + elseif ( isset($current_ids[$ordering]) && $current_ids[$ordering] != $tag_current_id ) { + // this actually checks if the ordering number points to the same tag + //recompute ordering, if necessary + //echo 'ordering changed for ', $tag, ':', $ordering, "\n"; + tag_assign($record, $tag_current_id, $ordering); } } +} +/** + * Adds a tag to a record, without overwriting the current tags. + * + * @param string $record_type the type of record to tag ('post' for blogs, + * 'user' for users, etc. + * @param int $record_id the id of the record to tag + * @param string $tag the tag to add + * @return void + */ +function tag_set_add($record_type, $record_id, $tag) { - // update_tag_correlations($item_type, $item_id); + $record = array('type' => $record_type, 'id' => $record_id); + + $new_tags = array(); + foreach( tag_get_tags($record) as $current_tag ) { + $new_tags[] = $current_tag->rawname; + } + $new_tags[] = $tag; + + return tag_set($record_type, $record_id, $new_tags); +} +/** + * Set the type of a tag. At this time (version 1.9) the possible values + * are 'default' or 'official'. Official tags will be displayed separately "at + * tagging time" (while selecting the tags to apply to a record). + * + * @param string $tagid tagid to modify + * @param string $type either 'default' or 'official' + * @return true on success, false otherwise + */ +function tag_type_set($tagid, $type) { + if ($tag = get_record('tag', 'id', $tagid)) { + $tag->tagtype = $type; + $tag->timemodified = time(); + return update_record('tag', $tag); + } + return false; } +/////////////////////////////////////////////////////// +/////////////////// PRIVATE TAG API /////////////////// /** - * Updates the tags associated with an item - * - * Ex 1: - * Suppose user 1 is tagged only with "algorithms", "computers" and "software" - * By calling update_item_tags('user', 1, 'algorithms, software, mathematics') - * User 1 will now be tagged only with "algorithms", "software" and "mathematics" - * - * Ex 2: - * update_item_tags('user', '1', 'algorithms, 12, 13') - * User 1 will now be tagged only with "algorithms", and with tags of ids 12 and 13 - * + * A * @param array $record the record that will be tagged + * @param string $tags the comma-separated tags to set on the record. If + * given an empty array, all tags will be removed. +dds one or more tag in the database. This function should not be called + * directly : you should use tag_set. * - * @param string $item_type name of the table where the item is stored. Ex: 'user' - * @param string $item_id id of the item to be tagged - * @param string $tag_names_or_ids_csv comma separated tag names (can be unormalized) or ids of existing tags - * @param string $tag_type type of the tags that are beeing added (optional, default value is "default") + * @param mixed $tags one tag, or an array of tags, to be created + * @param string $tag_type type of tag to be created ("default" is the default + * value and "official" is the only other supported value at this time). An + * official tag is kept even if there are no records tagged with it. + * @return an array of tags ids, indexed by their lowercase normalized names. + * Any boolean false in the array indicates an error while adding the tag. */ +function tag_add($tags, $type="default") { + global $USER; -function update_item_tags($item_type, $item_id, $tag_names_or_ids_csv, $tag_type="default") { + require_capability('moodle/tag:create', get_context_instance(CONTEXT_SYSTEM)); - //if $tag_names_csv is an empty string, remove all tag associations of the item - if( empty($tag_names_or_ids_csv) ){ - untag_an_item($item_type, $item_id); - return; + if (!is_array($tags)) { + $tags = array($tags); } - //convert any tag ids passed to their corresponding tag names - $tag_names_csv = tag_name_from_string($tag_names_or_ids_csv); - - //associate the tags passed with the item - tag_an_item($item_type, $item_id, $tag_names_csv, $tag_type ); - - //get the ids of the tags passed - $existing_and_new_tags_ids = tags_id( tag_normalize($tag_names_csv) ); - - // delete any tag instance with $item_type and $item_id - // that are not in $tag_names_csv - $tags_id_csv = "'" . implode("','", $existing_and_new_tags_ids) . "'" ; + $tag_object = new StdClass; + $tag_object->tagtype = $type; + $tag_object->userid = $USER->id; + $tag_object->timemodified = time(); - $select = " - itemid = '{$item_id}' - AND - itemtype = '{$item_type}' - AND - tagid NOT IN ({$tags_id_csv}) - "; + $clean_tags = tag_normalize($tags, TAG_CASE_ORIGINAL); - delete_records_select('tag_instance', $select); + $tags_ids = array(); + foreach($clean_tags as $tag) { + $tag = trim($tag); + if (!$tag) { + $tags_ids[$tag] = false; + } else { + // note that the difference between rawname and name is only + // capitalization : the rawname is NOT the same at the rawtag. + $tag_object->rawname = addslashes($tag); + $tag_name_lc = moodle_strtolower($tag); + $tag_object->name = addslashes($tag_name_lc); + //var_dump($tag_object); + $tags_ids[$tag_name_lc] = insert_record('tag', $tag_object); + } + } + return $tags_ids; } /** - * Removes the association of an item with a tag - * - * Ex: untag_an_item('user', '1', 'history, 11, roman' ) - * The user with id 1 will no longer be tagged with 'history', 'roman' and the tag of id 11 - * Calling untag_an_item('user','1') will remove all tags associated with user 1. - * - * @param string $item_type name of the table where the item is stored. Ex: 'user' - * @param string $item_id id of the item to be untagged - * @param string $tag_names_or_ids_csv comma separated tag **normalized** names or ids of existing tags (optional, if none is given, all tags of the item will be removed) + * Assigns a tag to a record: if the record already exists, the time and + * ordering will be updated. + * + * @param array $record the record that will be tagged + * @param string $tagid the tag id to set on the record. + * @param int $ordering the order of the instance for this record + * @return bool true on success, false otherwise */ +function tag_assign($record, $tagid, $ordering) { -function untag_an_item($item_type, $item_id, $tag_names_or_ids_csv='') { - - if ($tag_names_or_ids_csv == ""){ - - delete_records('tag_instance','itemtype', $item_type, 'itemid', $item_id); - - } - else { - - $tag_ids_csv = tag_id_from_string($tag_names_or_ids_csv); + require_capability('moodle/tag:create', get_context_instance(CONTEXT_SYSTEM)); - $tag_ids_csv_with_apos = "'" . str_replace(',', "','", $tag_ids_csv ) . "'"; - - delete_records_select('tag_instance', - "tagid IN ({$tag_ids_csv_with_apos}) AND itemtype='$item_type' AND itemid='$item_id'"); + if ( $tag_instance_object = get_record('tag_instance', 'tagid', $tagid, 'itemtype', $record['type'], 'itemid', $record['id']) ) { + $tag_instance_object->ordering = $ordering; + $tag_instance_object->timemodified = time(); + return update_record('tag_instance', $tag_instance_object); + } else { + $tag_instance_object = new StdClass; + $tag_instance_object->tagid = $tagid; + $tag_instance_object->itemid = $record['id']; + $tag_instance_object->itemtype = $record['type']; + $tag_instance_object->ordering = $ordering; + $tag_instance_object->timemodified = time(); + return insert_record('tag_instance', $tag_instance_object); } - - //update_tag_correlations($item_type, $item_id); - } /** - * Function that gets the tags that are associated with an item - * - * Ex: get_item_tags('user', '1') + * Function that returns tags that start with some text, for use by the autocomplete feature * - * @param string $item_type name of the table where the item is stored. Ex: 'user' - * @param string $item_id id of the item beeing queried - * @param string $sort an order to sort the results in, a valid SQL ORDER BY parameter (default is 'ti.ordering ASC') - * @param string $fields tag fields to be selected (optional, default is 'id, name, rawname, tagtype, flag') - * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). - * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). + * @param string $text string that the tag names will be matched against * @return mixed an array of objects, or false if no records were found or an error occured. */ - -function get_item_tags($item_type, $item_id, $sort='ti.ordering ASC', $fields=DEFAULT_TAG_TABLE_FIELDS, $limitfrom='', $limitnum='', $tagtype='') { - +function tag_autocomplete($text) { global $CFG; - - $fields = 'tg.' . $fields; - $fields = str_replace(',', ',tg.', $fields); - - if ($sort) { - $sort = ' ORDER BY '. $sort; - } - - if ($tagtype) { - $tagwhere = " AND tg.tagtype = '$tagtype' "; - } else { - $tagwhere = ''; - } - - $query = " - SELECT - {$fields} - FROM - {$CFG->prefix}tag_instance ti - INNER JOIN - {$CFG->prefix}tag tg - ON - tg.id = ti.tagid - WHERE - ti.itemtype = '{$item_type}' AND - ti.itemid = '{$item_id}' - $tagwhere - {$sort} - "; - - return get_records_sql($query, $limitfrom, $limitnum); - + return get_records_sql('SELECT tg.id, tg.name, tg.rawname FROM '. $CFG->prefix .'tag tg WHERE tg.name LIKE "'. moodle_strtolower($text) .'%"'); } - - /** - * Function that returns the items of a certain type associated with a certain tag + * 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". * - * Ex 1: get_items_tagged_with('user', 'banana') - * Ex 2: get_items_tagged_with('user', '11') + * 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 string $item_type name of the table where the item is stored. Ex: 'user' - * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag - * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter). - * (to avoid field name ambiguity in the query, use the identifier "it" Ex: 'it.name ASC' ) - * @param string $fields a comma separated list of fields to return - * (optional, by default all fields are returned). The first field will be used as key for the - * array so must be a unique field such as 'id'. ) - * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). - * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). - * @return mixed an array of objects indexed by their ids, or false if no records were found or an error occured. + * @param number $min_correlation cutoff percentage (optional, default is 2) */ - -function get_items_tagged_with($item_type, $tag_name_or_id, $sort='', $fields='*', $limitfrom='', $limitnum='') { +function tag_compute_correlations($min_correlation=2) { global $CFG; - $tag_id = tag_id_from_string($tag_name_or_id); - - $fields = 'it.' . $fields; - $fields = str_replace(',', ',it.', $fields); + $all_tags = get_records_list('tag'); + + + $tag_correlation_obj = new object(); + foreach($all_tags as $tag) { + + // query that counts how many times any tag appears together in items + // with the tag passed as argument ($tag_id) + $query = "SELECT tb.tagid , COUNT(*) AS nr ". + "FROM {$CFG->prefix}tag_instance ta INNER JOIN {$CFG->prefix}tag_instance tb ON ta.itemid = tb.itemid ". + "WHERE ta.tagid = {$tag->id} AND tb.tagid != {$tag->id} ". + "GROUP BY tb.tagid ". + "HAVING nr > $min_correlation ". + "ORDER BY nr DESC"; // todo: find out if it's necessary to order. + + $correlated = array(); + + // Correlated tags happen when they appear together in more occasions + // than $min_correlation. + if ($tag_correlations = get_records_sql($query)) { + foreach($tag_correlations as $correlation) { + // if($correlation->nr >= $min_correlation){ + $correlated[] = $correlation->tagid; + // } + } + } - if ($sort) { - $sort = ' ORDER BY '. $sort; + $correlated = implode(',', $correlated); + //var_dump($correlated); + + //saves correlation info in the caching table + if ($tag_correlation_obj = get_record('tag_correlation', 'tagid', $tag->id)) { + $tag_correlation_obj->correlatedtags = $correlated; + update_record('tag_correlation', $tag_correlation_obj); + } else { + $tag_correlation_obj->tagid = $tag->id; + $tag_correlation_obj->correlatedtags = $correlated; + insert_record('tag_correlation', $tag_correlation_obj); + } } +} + +/** + * Tasks that should be performed at cron time + */ +function tag_cron() { + tag_compute_correlations(); +} + +/** + * Get the name of a tag + * + * @param mixed $tagids the id of the tag, or an array of ids + * @return mixed string name of one tag, or id-indexed array of strings + */ +function tag_get_name($tagids) { - $query = " - SELECT - {$fields} - FROM - {$CFG->prefix}{$item_type} it - INNER JOIN - {$CFG->prefix}tag_instance tt - ON - it.id = tt.itemid - WHERE - tt.itemtype = '{$item_type}' AND - tt.tagid = '{$tag_id}' - {$sort} - "; + $return_a_string = false; + if ( !is_array($tagids) ) { + $return_a_string = true; + $tagids = array($tagids); + } + $tag_names = array(); + foreach(get_records_list('tag', 'id', implode(',', $tagids)) as $tag) { + $tag_names[$tag->id] = $tag->name; + } - return get_records_sql($query, $limitfrom, $limitnum); + if ($return_a_string) { + return array_pop($tag_names); + } + return $tag_names; } /** - * Returns the number of items tagged with a tag + * Returns the correlated tags of a tag, retrieved from the tag_correlation + * table. Make sure cron runs, otherwise the table will be empty and this + * function won't return anything. * - * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag - * @param string $item_type name of the table where the item is stored. Ex: 'user' (optional, if none is set any - * type will be counted) - * @return int the count. If an error occurrs, 0 is returned. + * @param int $tag_id is a single tag id + * @return array an array of tag objects, empty if no correlated tags are found */ -function count_items_tagged_with($tag_name_or_id, $item_type='') { +function tag_get_correlated($tag_id, $limitnum=null) { - global $CFG; - - $tag_id = tag_id_from_string($tag_name_or_id); + $tag_correlation = get_record('tag_correlation', 'tagid', $tag_id); - if (empty($item_type)){ - $query = " - SELECT - COUNT(*) AS count - FROM - {$CFG->prefix}tag_instance tt - WHERE - tagid = {$tag_id}"; + if (!$tag_correlation || empty($tag_correlation->correlatedtags)) { + return array(); } - else - { - $query = " - SELECT - COUNT(*) AS count - FROM - {$CFG->prefix}{$item_type} it - INNER JOIN - {$CFG->prefix}tag_instance tt - ON - it.id = tt.itemid - WHERE - tt.itemtype = '{$item_type}' AND - tt.tagid = '{$tag_id}' "; + + if (!$result = get_records_select('tag', "id IN ({$tag_correlation->correlatedtags})", '', '*', 0, $limitnum)) { + return array(); } - - return count_records_sql($query); - + return $result; } - /** - * Determines if an item is tagged with a certain tag + * Function that normalizes a list of tag names. * - * @param string $item_type name of the table where the item is stored. Ex: 'user' - * @param string $item_id id of the item beeing queried - * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag - * @return bool true if a matching record exists, else false. + * @param mixed $tags array of tags, or a single tag. + * @param int $case case to use for returned value (default: lower case). Either CASE_LOWER or CASE_UPPER + * @return array of lowercased normalized tags, indexed by the normalized tag. (Eg: 'Banana' => 'banana') */ -function is_item_tagged_with($item_type,$item_id, $tag_name_or_id) { +function tag_normalize($rawtags, $case = TAG_CASE_LOWER) { + + // cache normalized tags, to prevent (in some cases) costly (repeated) calls to clean_param + static $cleaned_tags_lc = array(); // lower case - use for comparison + static $cleaned_tags_mc = array(); // mixed case - use for saving to database - $tag_id = tag_id_from_string($tag_name_or_id); + if ( !is_array($rawtags) ) { + $rawtags = array($rawtags); + } - return record_exists('tag_instance','itemtype',$item_type,'itemid',$item_id, 'tagid', $tag_id); + $result = array(); + foreach($rawtags as $rawtag) { + $rawtag = trim($rawtag); + if (!$rawtag) { + continue; + } + if ( !array_key_exists($rawtag, $cleaned_tags_lc) ) { + $cleaned_tags_lc[$rawtag] = moodle_strtolower( clean_param($rawtag, PARAM_TAG) ); + $cleaned_tags_mc[$rawtag] = clean_param($rawtag, PARAM_TAG); + } + if ( $case == TAG_CASE_LOWER ) { + $result[$rawtag] = $cleaned_tags_lc[$rawtag]; + } else { // TAG_CASE_ORIGINAL + $result[$rawtag] = $cleaned_tags_mc[$rawtag]; + } + } + + return $result; } + /** * Search for tags with names that match some text * - * @param string $text string that the tag names will be matched against + * @param string $text escaped string that the tag names will be matched against * @param boolean $ordered If true, tags are ordered by their popularity. If false, no ordering. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). * @return mixed an array of objects, or false if no records were found or an error occured. */ - -function search_tags($text, $ordered=true, $limitfrom='' , $limitnum='' ) { +function tag_find_tags($text, $ordered=true, $limitfrom='', $limitnum='') { global $CFG; - $text = tag_normalize($text); + $text = array_shift(tag_normalize($text, TAG_CASE_LOWER)); if ($ordered) { - $query = " - SELECT - tg.id, tg.name, tg.rawname, COUNT(ti.id) AS count - FROM - {$CFG->prefix}tag tg - LEFT JOIN - {$CFG->prefix}tag_instance ti - ON - tg.id = ti.tagid - WHERE - tg.name - LIKE - '%{$text}%' - GROUP BY - tg.id, tg.name, tg.rawname - ORDER BY - count - DESC"; + $query = "SELECT tg.id, tg.name, tg.rawname, COUNT(ti.id) AS count ". + "FROM {$CFG->prefix}tag tg LEFT JOIN {$CFG->prefix}tag_instance ti ON tg.id = ti.tagid ". + "WHERE tg.name LIKE '%{$text}%' ". + "GROUP BY tg.id, tg.name, tg.rawname ". + "ORDER BY count DESC"; } else { - $query = " - SELECT - tg.id, tg.name, tg.rawname - FROM - {$CFG->prefix}tag tg - WHERE - tg.name - LIKE - '%{$text}%' - "; + $query = "SELECT tg.id, tg.name, tg.rawname ". + "FROM {$CFG->prefix}tag tg ". + "WHERE tg.name LIKE '%{$text}%'"; } - - return get_records_sql($query, $limitfrom , $limitnum); - } +/////////////////////////////////////////////////////////// +////// functions copied over from the first version ////// + /** - * Function that returns tags that start with some text - * - * @param string $text string that the tag names will be matched against - * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). - * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). - * @return mixed an array of objects, or false if no records were found or an error occured. + * Flag a tag as inapropriate + * + * @param mixed $tagids one (int) tagid, or an array of tagids + * @return void */ -function similar_tags($text, $limitfrom='' , $limitnum='' ) { - global $CFG; - - $text = moodle_strtolower($text); - - $query = "SELECT tg.id, tg.name, tg.rawname - FROM {$CFG->prefix}tag tg - WHERE tg.name LIKE '{$text}%'"; - - return get_records_sql($query, $limitfrom , $limitnum); -} - -/** - * Returns tags related to a tag - * - * Related tags of a tag come from two sources: - * - manually added related tags, which are tag_instance entries for that tag - * - correlated tags, which are a calculated - * - * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag - * @param int $limitnum return a subset comprising this many records (optional, default is 10) - * @return array an array of tag objects - */ - -function related_tags($tag_name_or_id, $limitnum=10) { - - $tag_id = tag_id_from_string($tag_name_or_id); - - //gets the manually added related tags - if (!$manual_related_tags = get_item_tags('tag', $tag_id, 'ti.ordering ASC', DEFAULT_TAG_TABLE_FIELDS)) { - $manual_related_tags = array(); - } - - //gets the correlated tags - $automatic_related_tags = correlated_tags($tag_id, $limitnum); - - $related_tags = array_merge($manual_related_tags, $automatic_related_tags); - - return array_slice(object_array_unique($related_tags), 0 , $limitnum); -} - -/** - * Returns the correlated tags of a tag - * The correlated tags are retrieved from the tag_correlation table, which is a caching table. - * - * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag - * @return array an array of tag objects, or empty array if none - */ -function correlated_tags($tag_name_or_id, $limitnum=null) { - - $tag_id = tag_id_from_string($tag_name_or_id); - - if (!$tag_correlation = get_record('tag_correlation', 'tagid', $tag_id)) { - return array(); +function tag_set_flag($tagids) { + if ( !is_array($tagids) ) { + $tagids = array($tagids); } - - if (empty($tag_correlation->correlatedtags)) { - return array(); - } - - if (!$result = get_records_select('tag', "id IN ({$tag_correlation->correlatedtags})", '', DEFAULT_TAG_TABLE_FIELDS, 0, $limitnum)) { - return array(); - } - - return $result; -} - -/** - * Recalculates tag correlations of all the tags associated with an item - * This function could be called whenever the tags associations with an item changes - * ( for example when tag_an_item() or untag_an_item() is called ) - * - * @param string $item_type name of the table where the item is stored. Ex: 'user' - * @param string $item_id id of the item - */ -function update_tag_correlations($item_type, $item_id) { - - $item_tags = get_item_tags($item_type, $item_id); - - foreach ($item_tags as $tag) { - cache_correlated_tags($tag->id); - } -} - -/** - * Calculates and stores the correlated tags of a tag. - * 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 string $tag_name_or_id is a single **normalized** tag name or the id of a tag - * @param number $min_correlation cutoff percentage (optional, default is 0.25) - * @param int $limitnum return a subset comprising this many records (optional, default is 10) - */ -function cache_correlated_tags($tag_name_or_id, $min_correlation=2) { - global $CFG; - - $tag_id = tag_id_from_string($tag_name_or_id); - - // query that counts how many times any tag appears together in items - // with the tag passed as argument ($tag_id) - $query = "SELECT tb.tagid , COUNT(*) AS nr - FROM {$CFG->prefix}tag_instance ta - INNER JOIN {$CFG->prefix}tag_instance tb ON ta.itemid = tb.itemid - WHERE ta.tagid = {$tag_id} AND tb.tagid != {$tag_id} - GROUP BY tb.tagid - ORDER BY nr DESC"; - - $correlated = array(); - - // Correlated tags happen when they appear together in more occasions - // than $min_correlation. - if ($tag_correlations = get_records_sql($query)) { - foreach($tag_correlations as $correlation) { - if($correlation->nr >= $min_correlation){ - $correlated[] = $correlation->tagid; - } - } - } - - $correlated = implode(',', $correlated); - - //saves correlation info in the caching table - if ($tag_correlation_obj = get_record('tag_correlation', 'tagid', $tag_id)) { - $tag_correlation_obj->correlatedtags = $correlated; - update_record('tag_correlation', $tag_correlation_obj); - - } else { - $tag_correlation_obj = new object(); - $tag_correlation_obj->tagid = $tag_id; - $tag_correlation_obj->correlatedtags = $correlated; - insert_record('tag_correlation', $tag_correlation_obj); - } -} - -/** - * This function cleans up the 'tag_instance' table - * It removes orphans in 'tag_instances' table - * - */ -function tag_instance_table_cleanup() { - - global $CFG; - - //get the itemtypes present in the 'tag_instance' table - $query = " - SELECT - DISTINCT(itemtype) - FROM - {$CFG->prefix}tag_instance - "; - - if ($items_types = get_records_sql($query)) { - - // for each itemtype, remove tag_instances that are orphans - // That is: For a given tag_instance, if in the itemtype table there's no entry with id equal to itemid, - // then this tag_instance is an orphan and it will be removed. - foreach ($items_types as $type) { - - $query = " - {$CFG->prefix}tag_instance.id - IN - ( SELECT sq1.id - FROM - (SELECT sq2.* - FROM {$CFG->prefix}tag_instance sq2 - LEFT JOIN {$CFG->prefix}{$type->itemtype} item - ON sq2.itemid = item.id - WHERE item.id IS NULL - AND sq2.itemtype = '{$type->itemtype}') - sq1 - ) "; - - delete_records_select('tag_instance', $query); - } - } - - // remove tag_instances that are orphans because tagid does not correspond to an - // existing tag - $query = " - {$CFG->prefix}tag_instance.id - IN - (SELECT sq1.id - FROM - (SELECT sq2.* - FROM {$CFG->prefix}tag_instance sq2 - LEFT JOIN {$CFG->prefix}tag tg - ON sq2.tagid = tg.id - WHERE tg.id IS NULL ) - sq1 - ) - "; - - delete_records_select('tag_instance', $query); -} - - -/** - * Function that normalizes a list of tag names - * - * Ex: tag_normalize('bANAana') -> returns 'banana' - * tag_normalize('lots of spaces') -> returns 'lots of spaces' - * tag_normalize('%!%!% non alpha numeric %!%!%') -> returns 'non alpha numeric' - * tag_normalize('tag one, TAG TWO, TAG three, and anotheR tag') - * -> returns 'tag one,tag two,tag three,and another tag' - * - * @param string $tag_names_csv unnormalized CSV tag names - * @return string **normalized** CSV tag names - */ - -function tag_normalize($tag_names_csv, $lowercase=true) { - $tag_names_csv = clean_param($tag_names_csv, PARAM_TAGLIST); - - if ($lowercase){ - $tag_names_csv = moodle_strtolower($tag_names_csv); - } - - return $tag_names_csv; -} - -function tag_flag_inappropriate($tag_names_or_ids_csv){ - - $tag_ids_csv = tag_id_from_string($tag_names_or_ids_csv); - - $tag_ids = explode(',', $tag_ids_csv); - - foreach ($tag_ids as $id){ - $tag = get_record('tag','id',$id, '', '', '', '', 'id,flag'); - + foreach ($tagids as $tagid) { + $tag = get_record('tag', 'id', $tagid); $tag->flag++; $tag->timemodified = time(); - update_record('tag', $tag); } } -function tag_flag_reset($tag_names_or_ids_csv){ - - global $CFG; - - $tag_ids_csv = tag_id_from_string($tag_names_or_ids_csv); - - $tag_ids_csv_with_apos = "'" . str_replace(',', "','", $tag_ids_csv) . "'"; - - $timemodified = time(); - - $query = " - UPDATE - {$CFG->prefix}tag tg - SET - tg.flag = 0, - tg.timemodified = {$timemodified} - WHERE - tg.id - IN - ({$tag_ids_csv_with_apos}) - "; - - execute_sql($query, false); -} - -/** - * Function that updates tags names. - * Updates only if the new name suggested for a tag doesn´t exist already. - * - * @param Array $tags_names_changed array of new tag names indexed by tag ids. - * @return Array array of tags names that were effectively updated, indexed by tag ids. - */ -function tag_update_name($tags_names_changed){ - - $tags_names_updated = array(); - - foreach ($tags_names_changed as $id => $newname){ - - $norm_newname = tag_normalize($newname); - - if( !tag_exists($norm_newname) && is_tag_name_valid($norm_newname) ) { - - $tag = tag_by_id($id); - - $tags_names_updated[$id] = $tag->name; - - // rawname keeps the original casing of the string - $tag->rawname = tag_normalize($newname, false); - - // name lowercases the string - $tag->name = $norm_newname; - - $tag->timemodified = time(); - - update_record('tag',$tag); - - } - } - - return $tags_names_updated; - -} - -/** - * Function that returns comma separated HTML links to the tag pages of the tags passed - * - * @param array $tag_objects an array of tag objects - * @return string CSV, HTML links to tag pages - */ - -function tag_links_csv($tag_objects) { - - global $CFG; - $tag_links = ''; - - if (empty($tag_objects)) { - return ''; - } - - $systemcontext = get_context_instance(CONTEXT_SYSTEM); - $can_manage_tags = has_capability('moodle/tag:manage', $systemcontext); - - foreach ($tag_objects as $tag){ - //highlight tags that have been flagged as inappropriate for those who can manage them - $tagname = tag_display_name($tag); - if ($tag->flag > 0 && $can_manage_tags) { - $tagname = '' . $tagname . ''; - } - $tag_links .= ' '.$tagname.','; - } - - return rtrim($tag_links, ','); -} - -/** - * Function that returns comma separated names of the tags passed - * Example of string that might be returned: 'history, wars, greek history' - * - * @param array $tag_objects - * @return string CSV tag names - */ - -function tag_names_csv($tag_objects) { - - if (empty($tag_objects)) { - return ''; - } - - $tags = array(); - - foreach ($tag_objects as $tag){ - $tags[] = tag_display_name($tag); - } - - return implode(', ', $tags); -} - - -/** - * Returns most popular tags, ordered by their popularity - * - * @param int $nr_of_tags number of random tags to be returned - * @param unknown_type $tag_type - * @return mixed an array of tag objects with the following fields: id, name and count - */ -function popular_tags_count($nr_of_tags=20, $tag_type = 'default') { - - global $CFG; - - $query = " - SELECT - tg.rawname, tg.id, tg.name, COUNT(ti.id) AS count, tg.flag - FROM - {$CFG->prefix}tag_instance ti - INNER JOIN - {$CFG->prefix}tag tg - ON - tg.id = ti.tagid - GROUP BY - tg.id, tg.rawname, tg.name, tg.flag - ORDER BY - count - DESC - "; - - return get_records_sql($query,0,$nr_of_tags); - - -} - -function tag_cron(){ - - tag_instance_table_cleanup(); - - if ($tags = get_all_tags('*')) { - - foreach ($tags as $tag){ - cache_correlated_tags($tag->id); - } - } - -} -/*-------------------- Printing functions -------------------- */ - -/** - * Prints a box that contains the management links of a tag - * - * @param $tag_object - * @param $return if true return html string - */ - -function print_tag_management_box($tag_object, $return=false) { - - global $USER, $CFG; - - $tagname = tag_display_name($tag_object); - - $output = ''; - - if (!isguestuser()) { - - $output .= print_box_start('box','tag-management-box', true); - - $systemcontext = get_context_instance(CONTEXT_SYSTEM); - - $links = array(); - - // if the user is not tagged with the $tag_object tag, a link "add blahblah to my interests" will appear - if( !is_item_tagged_with('user', $USER->id, $tag_object->id )) { - $links[] = ''.get_string('addtagtomyinterests','tag',$tagname). ''; - } - - // only people with moodle/tag:edit capability may edit the tag description - if ( has_capability('moodle/tag:edit',$systemcontext) && is_item_tagged_with('user', $USER->id, $tag_object->id ) ) { - $links[] = ''.get_string('edittag', 'tag').''; - } - - // flag as inappropriate link - $links[] = '' . get_string('flagasinappropriate','tag',$tagname). ''; - - // Manage all tags links - if ( has_capability('moodle/tag:manage',$systemcontext) ) { - $links[] = '' . get_string('managetags', 'tag') . '' ; - } - - $output .= implode(' | ', $links); - - $output .= print_box_end(true); - - } - - if ($return) { - return $output; - } else { - echo $output; - } - -} - -/** - * Prints a box with the description of a tag and its related tags - * - * @param unknown_type $tag_object - * @param $return if true return html string +/** + * Remove the inapropriate flag on a tag + * + * @param mixed $tagids one (int) tagid, or an array of tagids + * @return bool true if function succeeds, false otherwise */ - -function print_tag_description_box($tag_object, $return=false) { - - global $USER, $CFG; - - $tagname = tag_display_name($tag_object); - $related_tags = related_tags($tag_object->id); - - $content = !empty($tag_object->description) || $related_tags; - - $output = ''; - - if ($content) { - $output .= print_box_start('generalbox', 'tag-description',true); - } - - if (!empty($tag_object->description)) { - $options = new object(); - $options->para = false; - $output .= format_text($tag_object->description, $tag_object->descriptionformat, $options); - } - - if ($related_tags) { - $output .= '

    '.get_string('relatedtags','tag').': ' . tag_links_csv($related_tags); - } - - if ($content) { - $output .= print_box_end(true); - } - - if ($return) { - return $output; - } else { - echo $output; - } -} - -/** - * Prints a table of the users tagged with the tag passed as argument - * - * @param $tag_object - * @param int $users_per_row number of users per row to display - * @param int $limitfrom prints users starting at this point (optional, required if $limitnum is set). - * @param int $limitnum prints this many users (optional, required if $limitfrom is set). - * @param $return if true return html string - */ - -function print_tagged_users_table($tag_object, $limitfrom='' , $limitnum='', $return=false) { - - //List of users with this tag - $userlist = array_values( get_items_tagged_with( - 'user', - $tag_object->id, - 'lastaccess DESC' , - 'id, firstname, lastname, picture', - $limitfrom, - $limitnum) ); - - $output = print_user_list($userlist, true); - - if ($return) { - return $output; - } - else { - echo $output; - } - -} - -/** - * Prints a list of users - * @param array $userlist an array of user objects - * @param $return if true return html string - */ -function print_user_list($userlist, $return=false) { - - $output = ''; - - $output .= '
      '; - foreach ($userlist as $user){ - $output .= '
    • '. print_user_box( $user , true) ."
    • \n"; - } - $output .= "
    \n"; - - if ($return) { - return $output; - } - else { - echo $output; - } - -} - -/** - * Prints an individual user box - * - * @param $user user object (contains the following fields: id, firstname, lastname and picture) - * @param $return if true return html string - */ -function print_user_box($user, $return=false) { - +function tag_unset_flag($tagids) { global $CFG; - $textlib = textlib_get_instance(); - $usercontext = get_context_instance(CONTEXT_USER, $user->id); + require_capability('moodle/tag:manage', get_context_instance(CONTEXT_SYSTEM)); - $profilelink = ''; - if ( has_capability('moodle/user:viewdetails', $usercontext) ) { - $profilelink = $CFG->wwwroot.'/user/view.php?id='.$user->id; - } - - $output = ''; - - $output .= print_box_start('user-box', 'user'.$user->id, true); - - $fullname = fullname($user); - $alt = ''; - if (!empty($profilelink)) { - $output .= ''; - $alt = $fullname; - } - - //print user image - if image is only content of link it needs ALT text! - if ($user->picture) { - $output .= ''.$alt.''; - } else { - $output .= ''.$alt.''; - } - - $output .= '
    '; - - if (!empty($profilelink)) { - $output .= '
    '; - } - - //truncate name if it's too big - if ($textlib->strlen($fullname) > 26) $fullname = $textlib->substr($fullname,0,26) . '...'; - - $output .= '' . $fullname . ''; - - $output .= print_box_end(true); - - if ($return) { - return $output; - } - else { - echo $output; - } - -} - -/** - * Prints the tag search box - * @param $return if true return html string - * - */ -function print_tag_search_box($return=false) { - - global $CFG; - - $output = ''; - $output .= print_box_start('','tag-search-box', true); - - $output .= '
    '; - $output .= '
    '; - $output .= ''; - $output .= '
    '; - $output .= '
    '; - $output .= '
    '; - - $output .= print_box_end(true); - - if ($return) { - return $output; - } - else { - echo $output; + if ( is_array($tagids) ) { + $tagids = implode(',', $tagids); } + $timemodified = time(); + return execute_sql('UPDATE '. $CFG->prefix .'tag tg SET tg.flag = 0, tg.timemodified = '. $timemodified .' WHERE tg.id IN ('. $tagids .')', false); } /** - * Prints the tag search results + * Count how many records are tagged with a specific tag, * - * @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 $return if true return html string + * @param string $record record to look for ('post', 'user', etc.) + * @param int $tag is a single tag id + * @return int number of mathing tags. */ -function print_tag_search_results($query, $page, $perpage, $return=false) { - - global $CFG, $USER; - - $count = sizeof( search_tags($query,false) ); - $tags = array_values(search_tags($query, true, $page * $perpage , $perpage)); - - $baseurl = $CFG->wwwroot.'/tag/search.php?query=' . $query; - - $output = ''; - - // link "Add $query to my interests" - $addtaglink = ''; - if( !is_item_tagged_with('user', $USER->id, $query )) { - $addtaglink = ''; - $addtaglink .= get_string('addtagtomyinterests','tag',$query). ''; - } - - - if($tags) { // there are results to display!! - - $output .= print_heading(get_string('searchresultsfor', 'tag', $query) . " : {$count}", '', 3, 'main' ,true); - - //print a link "Add $query to my interests" - if (!empty($addtaglink)) { - $output .= print_box($addtaglink,'box','tag-management-box',true); - } - - $nr_of_lis_per_ul = 6; - $nr_of_uls = ceil( sizeof($tags) / $nr_of_lis_per_ul); - - $output .= '
      '; - for($i = 0; $i < $nr_of_uls; $i++) { - $output .= '
    • '; - foreach (array_slice($tags, $i * $nr_of_lis_per_ul, $nr_of_lis_per_ul ) as $tag) { - $tag_link = ' '.tag_display_name($tag).''; - $output .= '•' . $tag_link . '
      '; - } - $output .= '
    • '; - } - $output .= '
    '; - $output .= '
     
    '; // <-- small layout hack in order to look good in Firefox - - $output .= print_paging_bar($count, $page, $perpage, $baseurl.'&', 'page', false, true); - } - else { //no results were found!! - - $output .= print_heading(get_string('noresultsfor', 'tag', $query), '', 3, 'main' , true); - - //print a link "Add $query to my interests" - if (!empty($addtaglink)) { - $output .= print_box($addtaglink,'box','tag-management-box', true); - } - - } - - if ($return) { - return $output; - } - else { - echo $output; - } - - +function tag_record_count($record_type, $tagid) { + return count_records('tag_instance', 'itemtype', $record_type, 'tagid', $tagid); } /** - * Prints a tag cloud + * Determine if a record is tagged with a specific tag * - * @param array $tagcloud array of tag objects (fields: id, name, rawname, count and flag) - * @param boolean $shuffle wether or not to shuffle the array passed - * @param int $max_size maximum text size, in percentage - * @param int $min_size minimum text size, in percentage - * @param $return if true return html string + * @param array $record the record to look for + * @param string $tag a tag name + * @return bool true if it is tagged, false otherwise */ -function print_tag_cloud($tagcloud, $shuffle=true, $max_size=180, $min_size=80, $return=false) { - - global $CFG; - - if (empty($tagcloud)) { - return; - } - - if ($shuffle) { - shuffle($tagcloud); +function tag_record_tagged_with($record, $tag) { + if ($tagid = tag_get_id($tag)) { + return count_records('tag_instance', 'itemtype', $record['type'], 'itemid', $record['id'], 'tagid', $tagid); } else { - ksort($tagcloud); + return 0; // tag doesn't exist } - - $count = array(); - foreach ($tagcloud as $key => $value){ - if(!empty($value->count)) { - $count[$key] = log10($value->count); - } - else{ - $count[$key] = 0; - } - } - - $max = max($count); - $min = min($count); - - $spread = $max - $min; - if (0 == $spread) { // we don't want to divide by zero - $spread = 1; - } - - $step = ($max_size - $min_size)/($spread); - - $systemcontext = get_context_instance(CONTEXT_SYSTEM); - $can_manage_tags = has_capability('moodle/tag:manage', $systemcontext); - - //prints the tag cloud - $output = '
      '; - foreach ($tagcloud as $key => $tag) { - - $size = $min_size + ((log10($tag->count) - $min) * $step); - $size = ceil($size); - - $style = 'style="font-size: '.$size.'%"'; - $title = 'title="'.s(get_string('thingstaggedwith','tag', $tag)).'"'; - $href = 'href="'.$CFG->wwwroot.'/tag/index.php?id='.$tag->id.'"'; - - //highlight tags that have been flagged as inappropriate for those who can manage them - $tagname = tag_display_name($tag); - if ($tag->flag > 0 && $can_manage_tags) { - $tagname = '' . tag_display_name($tag) . ''; - } - - $tag_link = '
    • '.$tagname.'
    • '; - - $output .= $tag_link; - - } - $output .= '
    '; - - if ($return) { - return $output; - } else { - echo $output; - } - } ?> diff --git a/tag/manage.php b/tag/manage.php index 2d8b5caffe9da..2a130e26a880f 100644 --- a/tag/manage.php +++ b/tag/manage.php @@ -35,7 +35,7 @@ // get all the possible tag types from db $existing_tagtypes = array(); -if ($ptypes = get_records_sql("SELECT DISTINCT(tagtype), id FROM {$CFG->prefix}tag")) { +if ($ptypes = get_records_sql("SELECT DISTINCT(tagtype) FROM {$CFG->prefix}tag")) { foreach ($ptypes as $ptype) { $existing_tagtypes[$ptype->tagtype] = $ptype->tagtype; } @@ -49,11 +49,9 @@ if (!data_submitted() or !confirm_sesskey()) { break; } - $str_tagschecked = tag_name_from_string(implode($tagschecked, ',')); - $str_tagschecked = str_replace(',', ', ', $str_tagschecked); - - tag_delete(implode($tagschecked, ',')); - + + $str_tagschecked = implode(', ', tag_get_name($tagschecked)); + tag_delete($tagschecked); $notice = $str_tagschecked.' -- '.get_string('deleted','tag'); break; @@ -61,12 +59,9 @@ if (!data_submitted() or !confirm_sesskey()) { break; } - $str_tagschecked = tag_name_from_string(implode($tagschecked, ',')); - $str_tagschecked = str_replace(',', ', ', $str_tagschecked); - - tag_flag_reset(implode($tagschecked, ',')); - - $notice = $str_tagschecked.' -- '.get_string('reset','tag'); + $str_tagschecked = implode(', ', tag_get_name($tagschecked)); + tag_unset_flag($tagschecked); + $notice = $str_tagschecked .' -- '. get_string('reset', 'tag'); break; case 'changetype': @@ -75,7 +70,6 @@ } $changed = array(); - foreach ($tagschecked as $tag_id) { if (!array_key_exists($tagtypes[$tag_id], $existing_tagtypes)) { //can not add new types here!! @@ -83,19 +77,14 @@ } // update tag type; - $tag = tag_by_id($tag_id); - $tag->timemodified = time(); - $tag->tagtype = $tagtypes[$tag_id]; - - if (update_record('tag', $tag)) { + if (tag_type_set($tag_id, $tagtypes[$tag_id])) { $changed[] = $tag_id; } } - if ($changed) { - $str_changed = tag_name_from_string(implode($changed, ',')); - $str_changed = str_replace(',', ', ', $str_changed); - $notice = $str_changed.' -- '.get_string('typechanged','tag'); + if (!empty($changed)) { + $str_changed = implode(', ', tag_get_name($changed)); + $notice = $str_changed .' -- '. get_string('typechanged','tag'); } break; @@ -103,12 +92,12 @@ if (!data_submitted() or !confirm_sesskey()) { break; } - + $tags_names_changed = array(); - foreach ($tagschecked as $tag_id) { if ($newnames[$tag_id] != '') { - if (tag_exists($newnames[$tag_id])) { + if (! $tags_names_updated[] = tag_rename($tag_id, $newnames[$tag_id]) ) { + // if tag already exists, or is not a valid tag name, etc. $err_notice .= $newnames[$tag_id]. '-- ' . get_string('namesalreadybeeingused','tag').'
    '; } else { $tags_names_changed[$tag_id] = $newnames[$tag_id]; @@ -116,14 +105,25 @@ } } - $tags_names_updated = tag_update_name($tags_names_changed); - //notice to inform what tags had their names effectively updated - if ($tags_names_updated){ - $notice = implode($tags_names_updated, ', '); + if ($tags_names_changed){ + $notice = implode($tags_names_changed, ', '); $notice .= ' -- ' . get_string('updated','tag'); } break; + case 'addofficialtag': + $new_otags = explode(',', optional_param('otagsadd', '', PARAM_TAG)); + $notice = ''; + foreach ( $new_otags as $new_otag ) { + if ( $new_otag_id = tag_get_id($new_otag) ) { + // tag exists, change the type + tag_set_type($new_otag_id, 'official'); + } else { + tag_add($new_otag, 'official'); + } + $notice .= get_string('addedotag', 'tag', $new_otag) .' '; + } + break; } echo '
    '; @@ -132,18 +132,27 @@ notify($err_notice, 'red'); } if ($notice) { - notify($notice , 'green'); + notify($notice, 'green'); } +// small form to add an official tag +print('
    '); +print(''); +print('
    '. + ''. + ''. + '
    '); +print('
    '); + //setup table -$tablecolumns = array('id','name', 'fullname', 'count', 'flag', 'timemodified', 'rawname', 'tagtype', ''); -$tableheaders = array(get_string('id' , 'tag'), - get_string('name' , 'tag'), - get_string('owner','tag'), - get_string('count','tag'), - get_string('flag','tag'), - get_string('timemodified','tag'), +$tablecolumns = array('id', 'name', 'fullname', 'count', 'flag', 'timemodified', 'rawname', 'tagtype', ''); +$tableheaders = array(get_string('id', 'tag'), + get_string('name', 'tag'), + get_string('owner', 'tag'), + get_string('count', 'tag'), + get_string('flag', 'tag'), + get_string('timemodified', 'tag'), get_string('newname', 'tag'), get_string('tagtype', 'tag'), get_string('select', 'tag')); @@ -174,36 +183,29 @@ $table->setup(); if ($table->get_sql_sort()) { - $sort = ' ORDER BY '.$table->get_sql_sort(); + $sort = 'ORDER BY '. $table->get_sql_sort(); } else { $sort = ''; } if ($table->get_sql_where()) { - $where = 'WHERE '.$table->get_sql_where(); + $where = 'WHERE '. $table->get_sql_where(); } else { $where = ''; } -$query = " - SELECT tg.id, tg.name, tg.rawname, tg.tagtype, COUNT(ti.id) AS count, u.id AS owner, tg.flag, tg.timemodified, u.firstname, u.lastname - FROM {$CFG->prefix}tag_instance ti - RIGHT JOIN {$CFG->prefix}tag tg ON tg.id = ti.tagid - LEFT JOIN {$CFG->prefix}user u ON tg.userid = u.id - {$where} - GROUP BY tg.id, tg.name, tg.rawname, tg.tagtype, u.id, tg.flag, tg.timemodified, u.firstname, u.lastname - {$sort}"; +$query = 'SELECT tg.id, tg.name, tg.rawname, tg.tagtype, COUNT(ti.id) AS count, u.id AS owner, tg.flag, tg.timemodified, u.firstname, u.lastname '. + 'FROM '. $CFG->prefix .'tag_instance ti RIGHT JOIN '. $CFG->prefix .'tag tg ON tg.id = ti.tagid LEFT JOIN '. $CFG->prefix .'user u ON tg.userid = u.id '. + $where .' '. + 'GROUP BY tg.id, tg.name, tg.rawname, tg.tagtype, u.id, tg.flag, tg.timemodified, u.firstname, u.lastname '. + $sort; - -$totalcount = count_records_sql("SELECT COUNT(DISTINCT(tg.id)) - FROM {$CFG->prefix}tag tg - LEFT JOIN {$CFG->prefix}user u ON u.id = tg.userid - $where"); +$totalcount = count_records_sql('SELECT COUNT(DISTINCT(tg.id)) FROM '. $CFG->prefix .'tag tg LEFT JOIN '. $CFG->prefix .'user u ON u.id = tg.userid '. $where); $table->initialbars(true); // always initial bars $table->pagesize($perpage, $totalcount); -echo '
    '; +echo '
    '; //retrieve tags from DB if ($tagrecords = get_records_sql($query, $table->get_page_start(), $table->get_page_size())) { @@ -211,10 +213,8 @@ $taglist = array_values($tagrecords); //print_tag_cloud(array_values(get_records_sql($query)), false); - //populate table with data foreach ($taglist as $tag ){ - $id = $tag->id; $name = ''. tag_display_name($tag) .''; $owner = '' . fullname($tag) . ''; @@ -223,7 +223,6 @@ $timemodified = format_time(time() - $tag->timemodified); $checkbox = ''; $text = ''; - $tagtype = choose_from_menu($existing_tagtypes, 'tagtypes['.$tag->id.']', $tag->tagtype, '', '', '0', true); //if the tag if flagged, highlight it @@ -242,7 +241,6 @@ $table->add_data($data); } - echo ' '; echo ' '; echo ' '; @@ -256,18 +254,16 @@ '; echo ''; - } $table->print_html(); - echo '
    '; if ($perpage == SHOW_ALL_PAGE_SIZE) { - echo ''; + echo ''; } else if ($totalcount > 0 and $perpage < $totalcount) { - echo ''; + echo ''; } echo '
    '; diff --git a/tag/search.php b/tag/search.php index 30a5608733e5b..ddb5bd0fe6be4 100644 --- a/tag/search.php +++ b/tag/search.php @@ -20,24 +20,24 @@ $navigation = build_navigation($navlinks); $systemcontext = get_context_instance(CONTEXT_SYSTEM); +$manage_link = ' '; if ( has_capability('moodle/tag:manage',$systemcontext) ) { $manage_link = "wwwroot}/tag/manage.php\">" . get_string('managetags', 'tag') . "" ; } print_header_simple(get_string('tags', 'tag'), '', $navigation, '', '', '', $manage_link); - print_heading(get_string('searchtags', 'tag'), '', 2); -print_tag_search_box(); +tag_print_search_box(); if(!empty($query)) { - print_tag_search_results($query, $page, $perpage); + tag_print_search_results($query, $page, $perpage); } echo '

    '; print_box_start('generalbox', 'big-tag-cloud-box'); -print_tag_cloud(popular_tags_count(150), false, 170,70); +tag_print_cloud(150, false, 170,70); print_box_end(); print_footer(); diff --git a/tag/tag_autocomplete.php b/tag/tag_autocomplete.php index abcecd3cb9b28..a7dc68902f3f3 100644 --- a/tag/tag_autocomplete.php +++ b/tag/tag_autocomplete.php @@ -11,7 +11,7 @@ $query = optional_param('query', '', PARAM_TAG); -if ($similar_tags = similar_tags($query)) { +if ($similar_tags = tag_autocomplete($query)) { foreach ($similar_tags as $tag){ echo tag_display_name($tag) . "\t" . $tag->id . "\n"; } diff --git a/theme/standard/styles_layout.css b/theme/standard/styles_layout.css index 5d15cc5e78fe7..808044dad8b53 100644 --- a/theme/standard/styles_layout.css +++ b/theme/standard/styles_layout.css @@ -2971,7 +2971,7 @@ table#tag-management-list tr td{ padding-right :4px; } -form#tag-management-form { +.tag-management-form { text-align:center; } /* tag management end*/ diff --git a/user/editadvanced.php b/user/editadvanced.php index 3cdb707dd0a11..d1da2b49c0f9c 100644 --- a/user/editadvanced.php +++ b/user/editadvanced.php @@ -61,7 +61,7 @@ //user interests separated by commas if (!empty($CFG->usetags)) { require_once($CFG->dirroot.'/tag/lib.php'); - $user->interests = tag_names_csv(get_item_tags('user',$id)); + $user->interests = html_entity_decode(tag_get_tags_csv(array('type'=>'user','id'=>$id), TAG_RETURN_TEXT)); // formslib uses htmlentities itself } //create form diff --git a/user/editlib.php b/user/editlib.php index 3d3e81b93d91c..b7c927a308edc 100644 --- a/user/editlib.php +++ b/user/editlib.php @@ -54,8 +54,8 @@ function useredit_update_trackforums($user, $usernew) { } } -function useredit_update_interests($user, $cvs_tag_names) { - update_item_tags('user', $user->id, $cvs_tag_names); +function useredit_update_interests($user, $csv_tag_names) { + tag_set('user', $user->id, explode(',', $csv_tag_names)); } function useredit_shared_definition(&$mform) { diff --git a/user/tag.php b/user/tag.php index 6042908deca13..2382477f69249 100644 --- a/user/tag.php +++ b/user/tag.php @@ -4,7 +4,9 @@ require_once('../tag/lib.php'); $action = optional_param('action', '', PARAM_ALPHA); - +$id = optional_param('id', 0, PARAM_INT); +$tag = optional_param('tag', '', PARAM_TAG); + require_login(); if (empty($CFG->usetags)) { @@ -22,29 +24,20 @@ switch ($action) { case 'addinterest': - $id = optional_param('id', 0, PARAM_INT); - $name = optional_param('name', '', PARAM_TEXT); + if (empty($tag) && $id) { // for backward-compatibility (people saving bookmarks, mostly..) + $tag = tag_get_name($id); + } - if (empty($name) && $id) { - $name = tag_name($id); - } + tag_set_add('user', $USER->id, $tag); - tag_an_item('user',$USER->id, $name); - - if (!empty($name) && !$id) { - $id = tag_id(tag_normalize($name)); - } - - redirect($CFG->wwwroot.'/tag/index.php?id='.$id); + redirect($CFG->wwwroot.'/tag/index.php?tag='. rawurlencode($tag)); break; case 'flaginappropriate': - $id = required_param('id', PARAM_INT); - - tag_flag_inappropriate($id); + tag_set_flag(tag_get_id($tag)); - redirect($CFG->wwwroot.'/tag/index.php?id='.$id, get_string('responsiblewillbenotified','tag')); + redirect($CFG->wwwroot.'/tag/index.php?tag='. rawurlencode($tag), get_string('responsiblewillbenotified', 'tag')); break; } diff --git a/user/view.php b/user/view.php index 45337e5a1fae5..497d70666f0e8 100644 --- a/user/view.php +++ b/user/view.php @@ -381,12 +381,8 @@ /// Printing Interests if( !empty($CFG->usetags)) { - $interests = get_item_tags('user', $user->id); - - $instereststr = tag_links_csv($interests); - - if ($interests) { - print_row(get_string('interests').": ",rtrim($instereststr)); + if ( $interests = tag_get_tags_csv(array('type'=>'user', 'id'=>$user->id)) ) { + print_row(get_string('interests') .": ", $interests); } } /// End of Printing Interests