From c7aebb6755ed92bc26c65660f6ca620d2344bf3b Mon Sep 17 00:00:00 2001 From: moodler Date: Fri, 29 Feb 2008 06:25:22 +0000 Subject: [PATCH] MDL-13725 Remove tag_get_tag_by_id completely I also re-ordered tag/lib.php a bit --- blocks/tag_flickr/block_tag_flickr.php | 14 +- blocks/tag_youtube/block_tag_youtube.php | 28 +- blog/edit.php | 5 +- tag/edit.php | 14 +- tag/index.php | 18 +- tag/lib.php | 795 ++++++++++++----------- 6 files changed, 445 insertions(+), 429 deletions(-) diff --git a/blocks/tag_flickr/block_tag_flickr.php b/blocks/tag_flickr/block_tag_flickr.php index 97551d80b21..8149dacef2e 100644 --- a/blocks/tag_flickr/block_tag_flickr.php +++ b/blocks/tag_flickr/block_tag_flickr.php @@ -43,13 +43,13 @@ class block_tag_flickr extends block_base { $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'); + $tag_object = tag_get('name', $tag); + } else if ($tagid) { + $tag_object = tag_get('id', $tagid); + } + + if (empty($tag_object)) { + errorcode('tagnotfound'); } //include related tags in the photo query ? diff --git a/blocks/tag_youtube/block_tag_youtube.php b/blocks/tag_youtube/block_tag_youtube.php index 1f439e5a25e..98456687016 100644 --- a/blocks/tag_youtube/block_tag_youtube.php +++ b/blocks/tag_youtube/block_tag_youtube.php @@ -82,13 +82,13 @@ class block_tag_youtube extends block_base { $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'); + $tag_object = tag_get('name', $tag); + } else if ($tagid) { + $tag_object = tag_get('id', $tagid); + } + + if (empty($tag_object)) { + errorcode('tagnotfound'); } $query_tag = html_entity_decode(tag_display_name($tag_object)); @@ -114,13 +114,13 @@ class block_tag_youtube extends block_base { $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'); + $tag_object = tag_get('name', $tag); + } else if ($tagid) { + $tag_object = tag_get('id', $tagid); + } + + if (empty($tag_object)) { + errorcode('tagnotfound'); } $query_tag = html_entity_decode(tag_display_name($tag_object)); diff --git a/blog/edit.php b/blog/edit.php index b0bf31f17c2..5a251deb123 100755 --- a/blog/edit.php +++ b/blog/edit.php @@ -308,8 +308,9 @@ function add_tags_info($postid) { if ($otags = optional_param('otags', '', PARAM_INT)) { foreach ($otags as $tagid) { // TODO : make this use the tag name in the form - $tag = tag_get_tag_by_id($tagid); - $tags[] = $tag->name; + if ($tag = tag_get('id', $tagid)) { + $tags[] = $tag->name; + } } } diff --git a/tag/edit.php b/tag/edit.php index 6e6abb9f697..8da09367e67 100644 --- a/tag/edit.php +++ b/tag/edit.php @@ -14,13 +14,17 @@ if (empty($CFG->usetags)) { $tag_id = optional_param('id', 0, PARAM_INT); $tag_name = optional_param('tag', '', PARAM_TAG); + if ($tag_name) { - $tag = 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'); + $tag = tag_get('name', $tag_name, '*'); +} else if ($tag_id) { + $tag = tag_get('id', $tag_id, '*'); } + +if (empty($tag)) { + redirect($CFG->wwwroot.'/tag/search.php'); +} + $tagname = tag_display_name($tag); //Editing a tag requires moodle/tag:edit capability diff --git a/tag/index.php b/tag/index.php index 4a631064d8e..7cd4f81ca67 100644 --- a/tag/index.php +++ b/tag/index.php @@ -13,20 +13,24 @@ if (empty($CFG->usetags)) { } $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 -} +$tagname = optional_param('tag', '', PARAM_TAG); // tag + $edit = optional_param('edit', -1, PARAM_BOOL); $userpage = optional_param('userpage', 0, PARAM_INT); // which page to show $perpage = optional_param('perpage', 24, PARAM_INT); -if (!isset($tag) || !$tag->id) { + +if ($tagname) { + $tag = tag_get('name', $tagname, '*'); +} else if ($tagid) { + $tag = tag_get('id', $tagid, '*'); +} + +if (empty($tag)) { redirect($CFG->wwwroot.'/tag/search.php'); } + //create a new page_tag object, defined in pagelib.php $PAGE = page_create_object(PAGE_TAG_INDEX, $tag->id); $pageblocks = blocks_setup($PAGE,BLOCKS_PINNED_BOTH); diff --git a/tag/lib.php b/tag/lib.php index d258fb63cc8..945de06da18 100644 --- a/tag/lib.php +++ b/tag/lib.php @@ -54,400 +54,7 @@ require_once($CFG->dirroot .'/tag/locallib.php'); /////////////////////////////////////////////////////// /////////////////// PUBLIC TAG API //////////////////// -/** - * 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 tag_delete($tagids) { - - if (!is_array($tagids)) { - $tagids = array($tagids); - } - - $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 $success; -} - -/** - * 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_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 { - return false; - } -} - -/** - * Set the description of a tag - * - * @param int $tagid the id of the tag - * @param string $description the description - * @param int $descriptionformat the moodle text format of the description - * @return true on success, false otherwise - */ -function tag_description_set($tagid, $description, $descriptionformat) { - if ($tag = get_record('tag', 'id', $tagid, '', '', '', '', 'id')) { - $tag->description = addslashes($description); - $tag->descriptionformat = addslashes($descriptionformat); - $tag->timemodified = time(); - return update_record('tag', $tag); - } - return false; -} - -/** - * Function that returns the name that should be displayed for a specific tag - * - * @param object $tag_object a line out of tag table, as returned by the adobd functions - * @return string - */ -function tag_display_name($tag_object) { - - global $CFG; - - if(!isset($tag_object->name)) { - return ''; - } - - 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); - } -} - -/** - * Find all records tagged with a tag of a given type ('post', 'user', etc.) - * - * @param string $tag tag to look for - * @param string $type type to restrict search to. If null, every matching - * record will be returned - * @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 array of matching objects, indexed by record id, from the table containing the type requested - */ -function tag_find_records($tag, $type, $limitfrom='', $limitnum='') { - - global $CFG; - - if (!$tag || !$type) { - return array(); - } - - $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, $limitfrom, $limitnum); -} - - -/** - * Simple function to just return a single tag object - * - * @param string $field which field do we use to identify the tag: id, name or rawname - * @param string $value the required value of the aforementioned field - * @param string $returnfields which fields do we want returned? - * @return tag object - * - **/ -function tag_get($field, $value, $returnfields='id, name, rawname') { - return get_record('tag', $field, $value, '', '', '', '', $returnfields); -} - - -/** - * 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 string $record_type the record type for which we want to get the tags - * @param int $record_id the record id 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_get_tags($record_type, $record_id, $type=null) { - - global $CFG; - - if ($type) { - $type = "AND tg.tagtype = '$type'"; - } - - // if the fields in this query are changed, you need to do the same changes in tag_get_correlated_tags - $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; - } -} - -/** - * Get the array of tags display names, indexed by id. - * - * @param string $record_type the record type for which we want to get the tags - * @param int $record_id the record id 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, $record_id, $type=null) { - $tags = array(); - foreach(tag_get_tags($record_type, $record_id, $type) as $tag) { - $tags[$tag->id] = tag_display_name($tag); - } - return $tags; -} - -/** - * 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 $record_type the record type for which we want to get the tags - * @param int $record_id the record id 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 tag_get_tags_csv($record_type, $record_id, $html=TAG_RETURN_HTML, $type=null) { - global $CFG; - - $tags_names = array(); - foreach( tag_get_tags($record_type, $record_id, $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 implode(', ', $tags_names); -} - -/** - * Get an array of tag ids associated to a record. - * - * @param string $record the record type for which we want to get the tags - * @param int $record_id the record id for which we want to get the tags - * @return array of tag ids, indexed and sorted by 'ordering' - */ -function tag_get_tags_ids($record_type, $record_id) { - - $tag_ids = array(); - foreach( tag_get_tags($record_type, $record_id) 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_value specify the type of the returned value. Either - * TAG_RETURN_OBJECT, or TAG_RETURN_ARRAY (default). If TAG_RETURN_ARRAY - * is specified, an array will be returned even if only one tag was - * passed in $tags. - * @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(); - - $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; - } - } - } - - if ($return_an_int) { - return array_pop($result); - } - - return $result; -} - -/** - * Get a tag as an object (line) returned by get_recordset_sql - * - * @param int $tagid a tag id - * @return object a line returned from get_recordset_sql, or false - */ -function tag_get_tag_by_id($tagid) { - return get_record('tag', 'id', $tagid); -} - -/** - * 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 $type the function will return either manually - * (TAG_RELATED_MANUAL) related tags or correlated (TAG_RELATED_CORRELATED) - * tags. Default is TAG_RELATED_ALL, which returns everything. - * @param int $limitnum return a subset comprising this many records (optional, - * default is 10) - * @return array an array of tag objects - */ -function tag_get_related_tags($tagid, $type=TAG_RELATED_ALL, $limitnum=10) { - - $related_tags = array(); - - if ( $type == TAG_RELATED_ALL || $type == TAG_RELATED_MANUAL) { - //gets the manually added related tags - $related_tags = tag_get_tags('tag', $tagid); - } - - if ( $type == TAG_RELATED_ALL || $type == TAG_RELATED_CORRELATED ) { - //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 array_slice(object_array_unique($related_tags), 0 , $limitnum); -} - -/** - * Get a comma-separated list of tags related to another tag. - * - * @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_get_related_tags_csv($related_tags, $html=TAG_RETURN_HTML) { - global $CFG; - - $tags_names = array(); - foreach($related_tags as $tag) { - if ( $html == TAG_RETURN_TEXT) { - $tags_names[] = tag_display_name($tag); - } else { - // TAG_RETURN_HTML - $tags_names[] = ''. tag_display_name($tag) .''; - } - } - - return implode(', ', $tags_names); -} - -/** - * Change the "value" of a tag, and update the associated 'name'. - * - * @param int $tagid the id of the tag to modify - * @param string $newtag the new rawname - * @return bool true on success, false otherwise - */ -function tag_rename($tagid, $newrawname) { - - if (! $newrawname_clean = array_shift(tag_normalize($newrawname, TAG_CASE_ORIGINAL)) ) { - return false; - } - - if (! $newname_clean = moodle_strtolower($newrawname_clean)) { - return false; - } - - // Prevent the rename if a tag with that name already exists - if ($existing = tag_get('name', $newname_clean, 'id, name, rawname')) { - if ($existing->id != $tagid) { // Another tag already exists with this name - return false; - } - } - - if ($tag = tag_get('id', $tagid, 'id, name, rawname')) { - $tag->rawname = addslashes($newrawname_clean); - $tag->name = addslashes($newname_clean); - $tag->timemodified = time(); - return update_record('tag', $tag); - } - return false; -} +/// Functions for settings tags ////////////////////// /** * Set the tags assigned to a record. This overwrites the current tags. @@ -570,6 +177,406 @@ function tag_type_set($tagid, $type) { return false; } + +/** + * Set the description of a tag + * + * @param int $tagid the id of the tag + * @param string $description the description + * @param int $descriptionformat the moodle text format of the description + * @return true on success, false otherwise + */ +function tag_description_set($tagid, $description, $descriptionformat) { + if ($tag = get_record('tag', 'id', $tagid, '', '', '', '', 'id')) { + $tag->description = addslashes($description); + $tag->descriptionformat = addslashes($descriptionformat); + $tag->timemodified = time(); + return update_record('tag', $tag); + } + return false; +} + + + + + + +/// Functions for getting information about tags ////// + +/** + * Simple function to just return a single tag object when you know the name or something + * + * @param string $field which field do we use to identify the tag: id, name or rawname + * @param string $value the required value of the aforementioned field + * @param string $returnfields which fields do we want returned? + * @return tag object + * + **/ +function tag_get($field, $value, $returnfields='id, name, rawname') { + if ($field == 'name') { + $value = moodle_strtolower($value); // To cope with input that might just be wrong case + } + return get_record('tag', $field, $value, '', '', '', '', $returnfields); +} + + +/** + * 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 string $record_type the record type for which we want to get the tags + * @param int $record_id the record id 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_get_tags($record_type, $record_id, $type=null) { + + global $CFG; + + if ($type) { + $type = "AND tg.tagtype = '$type'"; + } + + // if the fields in this query are changed, you need to do the same changes in tag_get_correlated_tags + $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; + } +} + +/** + * Get the array of tags display names, indexed by id. + * + * @param string $record_type the record type for which we want to get the tags + * @param int $record_id the record id 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, $record_id, $type=null) { + $tags = array(); + foreach(tag_get_tags($record_type, $record_id, $type) as $tag) { + $tags[$tag->id] = tag_display_name($tag); + } + return $tags; +} + +/** + * 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 $record_type the record type for which we want to get the tags + * @param int $record_id the record id 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 tag_get_tags_csv($record_type, $record_id, $html=TAG_RETURN_HTML, $type=null) { + global $CFG; + + $tags_names = array(); + foreach(tag_get_tags($record_type, $record_id, $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 implode(', ', $tags_names); +} + +/** + * Get an array of tag ids associated to a record. + * + * @param string $record the record type for which we want to get the tags + * @param int $record_id the record id for which we want to get the tags + * @return array of tag ids, indexed and sorted by 'ordering' + */ +function tag_get_tags_ids($record_type, $record_id) { + + $tag_ids = array(); + foreach (tag_get_tags($record_type, $record_id) 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_value specify the type of the returned value. Either + * TAG_RETURN_OBJECT, or TAG_RETURN_ARRAY (default). If TAG_RETURN_ARRAY + * is specified, an array will be returned even if only one tag was + * passed in $tags. + * @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(); + + $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; + } + } + } + + if ($return_an_int) { + return array_pop($result); + } + + return $result; +} + + +/** + * 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 $type the function will return either manually + * (TAG_RELATED_MANUAL) related tags or correlated (TAG_RELATED_CORRELATED) + * tags. Default is TAG_RELATED_ALL, which returns everything. + * @param int $limitnum return a subset comprising this many records (optional, + * default is 10) + * @return array an array of tag objects + */ +function tag_get_related_tags($tagid, $type=TAG_RELATED_ALL, $limitnum=10) { + + $related_tags = array(); + + if ( $type == TAG_RELATED_ALL || $type == TAG_RELATED_MANUAL) { + //gets the manually added related tags + $related_tags = tag_get_tags('tag', $tagid); + } + + if ( $type == TAG_RELATED_ALL || $type == TAG_RELATED_CORRELATED ) { + //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 array_slice(object_array_unique($related_tags), 0 , $limitnum); +} + +/** + * Get a comma-separated list of tags related to another tag. + * + * @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_get_related_tags_csv($related_tags, $html=TAG_RETURN_HTML) { + global $CFG; + + $tags_names = array(); + foreach($related_tags as $tag) { + if ( $html == TAG_RETURN_TEXT) { + $tags_names[] = tag_display_name($tag); + } else { + // TAG_RETURN_HTML + $tags_names[] = ''. tag_display_name($tag) .''; + } + } + + return implode(', ', $tags_names); +} + +/** + * Change the "value" of a tag, and update the associated 'name'. + * + * @param int $tagid the id of the tag to modify + * @param string $newtag the new rawname + * @return bool true on success, false otherwise + */ +function tag_rename($tagid, $newrawname) { + + if (! $newrawname_clean = array_shift(tag_normalize($newrawname, TAG_CASE_ORIGINAL)) ) { + return false; + } + + if (! $newname_clean = moodle_strtolower($newrawname_clean)) { + return false; + } + + // Prevent the rename if a tag with that name already exists + if ($existing = tag_get('name', $newname_clean, 'id, name, rawname')) { + if ($existing->id != $tagid) { // Another tag already exists with this name + return false; + } + } + + if ($tag = tag_get('id', $tagid, 'id, name, rawname')) { + $tag->rawname = addslashes($newrawname_clean); + $tag->name = addslashes($newname_clean); + $tag->timemodified = time(); + return update_record('tag', $tag); + } + return false; +} + + +/** + * 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 tag_delete($tagids) { + + if (!is_array($tagids)) { + $tagids = array($tagids); + } + + $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 $success; +} + +/** + * 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_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 { + return false; + } +} + + +/** + * Function that returns the name that should be displayed for a specific tag + * + * @param object $tag_object a line out of tag table, as returned by the adobd functions + * @return string + */ +function tag_display_name($tag_object) { + + global $CFG; + + if(!isset($tag_object->name)) { + return ''; + } + + 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); + } +} + +/** + * Find all records tagged with a tag of a given type ('post', 'user', etc.) + * + * @param string $tag tag to look for + * @param string $type type to restrict search to. If null, every matching + * record will be returned + * @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 array of matching objects, indexed by record id, from the table containing the type requested + */ +function tag_find_records($tag, $type, $limitfrom='', $limitnum='') { + + global $CFG; + + if (!$tag || !$type) { + return array(); + } + + $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, $limitfrom, $limitnum); +} + + + + /////////////////////////////////////////////////////// /////////////////// PRIVATE TAG API ///////////////////