home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Blogs / wordpress2.6.exe / wordpress2.6 / wp-includes / taxonomy.php < prev    next >
Encoding:
PHP Script  |  2008-08-06  |  67.7 KB  |  2,157 lines

  1. <?php
  2. /**
  3.  * @package WordPress
  4.  * @subpackage Taxonomy
  5.  * @since 2.3
  6.  */
  7.  
  8. //
  9. // Taxonomy Registration
  10. //
  11.  
  12. /**
  13.  * Default Taxonomy Objects
  14.  * @since 2.3
  15.  * @global array $wp_taxonomies
  16.  */
  17. $wp_taxonomies = array();
  18. $wp_taxonomies['category'] = (object) array('name' => 'category', 'object_type' => 'post', 'hierarchical' => true, 'update_count_callback' => '_update_post_term_count');
  19. $wp_taxonomies['post_tag'] = (object) array('name' => 'post_tag', 'object_type' => 'post', 'hierarchical' => false, 'update_count_callback' => '_update_post_term_count');
  20. $wp_taxonomies['link_category'] = (object) array('name' => 'link_category', 'object_type' => 'link', 'hierarchical' => false);
  21.  
  22. /**
  23.  * Return all of the taxonomy names that are of $object_type.
  24.  *
  25.  * It appears that this function can be used to find all of the names inside of
  26.  * $wp_taxonomies global variable.
  27.  *
  28.  * <code><?php $taxonomies = get_object_taxonomies('post'); ?></code> Should
  29.  * result in <code>Array('category', 'post_tag')</code>
  30.  *
  31.  * @package WordPress
  32.  * @subpackage Taxonomy
  33.  * @since 2.3
  34.  *
  35.  * @uses $wp_taxonomies
  36.  *
  37.  * @param array|string|object $object Name of the type of taxonomy object, or an object (row from posts)
  38.  * @return array The names of all taxonomy of $object_type.
  39.  */
  40. function get_object_taxonomies($object) {
  41.     global $wp_taxonomies;
  42.  
  43.     if ( is_object($object) ) {
  44.         if ( $object->post_type == 'attachment' )
  45.             return get_attachment_taxonomies($object);
  46.         $object = $object->post_type;
  47.     }
  48.  
  49.     $object = (array) $object;
  50.  
  51.     $taxonomies = array();
  52.     foreach ( $wp_taxonomies as $taxonomy ) {
  53.         if ( array_intersect($object, (array) $taxonomy->object_type) )
  54.             $taxonomies[] = $taxonomy->name;
  55.     }
  56.  
  57.     return $taxonomies;
  58. }
  59.  
  60. /**
  61.  * Retrieves the taxonomy object of $taxonomy.
  62.  *
  63.  * The get_taxonomy function will first check that the parameter string given
  64.  * is a taxonomy object and if it is, it will return it.
  65.  *
  66.  * @package WordPress
  67.  * @subpackage Taxonomy
  68.  * @since 2.3
  69.  *
  70.  * @uses $wp_taxonomies
  71.  * @uses is_taxonomy() Checks whether taxonomy exists
  72.  *
  73.  * @param string $taxonomy Name of taxonomy object to return
  74.  * @return object|bool The Taxonomy Object or false if $taxonomy doesn't exist
  75.  */
  76. function get_taxonomy( $taxonomy ) {
  77.     global $wp_taxonomies;
  78.  
  79.     if ( ! is_taxonomy($taxonomy) )
  80.         return false;
  81.  
  82.     return $wp_taxonomies[$taxonomy];
  83. }
  84.  
  85. /**
  86.  * Checks that the taxonomy name exists.
  87.  *
  88.  * @package WordPress
  89.  * @subpackage Taxonomy
  90.  * @since 2.3
  91.  *
  92.  * @uses $wp_taxonomies
  93.  *
  94.  * @param string $taxonomy Name of taxonomy object
  95.  * @return bool Whether the taxonomy exists or not.
  96.  */
  97. function is_taxonomy( $taxonomy ) {
  98.     global $wp_taxonomies;
  99.  
  100.     return isset($wp_taxonomies[$taxonomy]);
  101. }
  102.  
  103. /**
  104.  * Whether the taxonomy object is hierarchical.
  105.  *
  106.  * Checks to make sure that the taxonomy is an object first. Then Gets the
  107.  * object, and finally returns the hierarchical value in the object.
  108.  *
  109.  * A false return value might also mean that the taxonomy does not exist.
  110.  *
  111.  * @package WordPress
  112.  * @subpackage Taxonomy
  113.  * @since 2.3
  114.  *
  115.  * @uses is_taxonomy() Checks whether taxonomy exists
  116.  * @uses get_taxonomy() Used to get the taxonomy object
  117.  *
  118.  * @param string $taxonomy Name of taxonomy object
  119.  * @return bool Whether the taxonomy is hierarchical
  120.  */
  121. function is_taxonomy_hierarchical($taxonomy) {
  122.     if ( ! is_taxonomy($taxonomy) )
  123.         return false;
  124.  
  125.     $taxonomy = get_taxonomy($taxonomy);
  126.     return $taxonomy->hierarchical;
  127. }
  128.  
  129. /**
  130.  * Create or modify a taxonomy object. Do not use before init.
  131.  *
  132.  * A simple function for creating or modifying a taxonomy object based on the
  133.  * parameters given. The function will accept an array (third optional
  134.  * parameter), along with strings for the taxonomy name and another string for
  135.  * the object type.
  136.  *
  137.  * Nothing is returned, so expect error maybe or use is_taxonomy() to check
  138.  * whether taxonomy exists.
  139.  *
  140.  * Optional $args contents:
  141.  *
  142.  * hierarachical - has some defined purpose at other parts of the API and is a
  143.  * boolean value.
  144.  *
  145.  * update_count_callback - works much like a hook, in that it will be called
  146.  * when the count is updated.
  147.  *
  148.  * rewrite - false to prevent rewrite, or array('slug'=>$slug) to customize
  149.  * permastruct; default will use $taxonomy as slug.
  150.  *
  151.  * query_var - false to prevent queries, or string to customize query var
  152.  * (?$query_var=$term); default will use $taxonomy as query var.
  153.  *
  154.  * @package WordPress
  155.  * @subpackage Taxonomy
  156.  * @since 2.3
  157.  * @uses $wp_taxonomies Inserts new taxonomy object into the list
  158.  * @uses $wp_rewrite Adds rewrite tags and permastructs
  159.  * @uses $wp Adds query vars
  160.  *
  161.  * @param string $taxonomy Name of taxonomy object
  162.  * @param array|string $object_type Name of the object type for the taxonomy object.
  163.  * @param array|string $args See above description for the two keys values.
  164.  */
  165. function register_taxonomy( $taxonomy, $object_type, $args = array() ) {
  166.     global $wp_taxonomies, $wp_rewrite, $wp;
  167.  
  168.     $defaults = array('hierarchical' => false, 'update_count_callback' => '', 'rewrite' => true, 'query_var' => true);
  169.     $args = wp_parse_args($args, $defaults);
  170.  
  171.     if ( false !== $args['query_var'] && !empty($wp) ) {
  172.         if ( true === $args['query_var'] )
  173.             $args['query_var'] = $taxonomy;
  174.         $args['query_var'] = sanitize_title_with_dashes($args['query_var']);
  175.         $wp->add_query_var($args['query_var']);
  176.     }
  177.  
  178.     if ( false !== $args['rewrite'] && !empty($wp_rewrite) ) {
  179.         if ( !is_array($args['rewrite']) )
  180.             $args['rewrite'] = array();
  181.         if ( !isset($args['rewrite']['slug']) )
  182.             $args['rewrite']['slug'] = sanitize_title_with_dashes($taxonomy);
  183.         $wp_rewrite->add_rewrite_tag("%$taxonomy%", '([^/]+)', $args['query_var'] ? "{$args['query_var']}=" : "taxonomy=$taxonomy&term=$term");
  184.         $wp_rewrite->add_permastruct($taxonomy, "{$args['rewrite']['slug']}/%$taxonomy%");
  185.     }
  186.  
  187.     $args['name'] = $taxonomy;
  188.     $args['object_type'] = $object_type;
  189.     $wp_taxonomies[$taxonomy] = (object) $args;
  190. }
  191.  
  192. //
  193. // Term API
  194. //
  195.  
  196. /**
  197.  * Retrieve object_ids of valid taxonomy and term.
  198.  *
  199.  * The strings of $taxonomies must exist before this function will continue. On
  200.  * failure of finding a valid taxonomy, it will return an WP_Error class, kind
  201.  * of like Exceptions in PHP 5, except you can't catch them. Even so, you can
  202.  * still test for the WP_Error class and get the error message.
  203.  *
  204.  * The $terms aren't checked the same as $taxonomies, but still need to exist
  205.  * for $object_ids to be returned.
  206.  *
  207.  * It is possible to change the order that object_ids is returned by either
  208.  * using PHP sort family functions or using the database by using $args with
  209.  * either ASC or DESC array. The value should be in the key named 'order'.
  210.  *
  211.  * @package WordPress
  212.  * @subpackage Taxonomy
  213.  * @since 2.3
  214.  *
  215.  * @uses $wpdb
  216.  * @uses wp_parse_args() Creates an array from string $args.
  217.  *
  218.  * @param string|array $terms String of term or array of string values of terms that will be used
  219.  * @param string|array $taxonomies String of taxonomy name or Array of string values of taxonomy names
  220.  * @param array|string $args Change the order of the object_ids, either ASC or DESC
  221.  * @return WP_Error|array If the taxonomy does not exist, then WP_Error will be returned. On success
  222.  *    the array can be empty meaning that there are no $object_ids found or it will return the $object_ids found.
  223.  */
  224. function get_objects_in_term( $terms, $taxonomies, $args = array() ) {
  225.     global $wpdb;
  226.  
  227.     if ( !is_array( $terms) )
  228.         $terms = array($terms);
  229.  
  230.     if ( !is_array($taxonomies) )
  231.         $taxonomies = array($taxonomies);
  232.  
  233.     foreach ( $taxonomies as $taxonomy ) {
  234.         if ( ! is_taxonomy($taxonomy) )
  235.             return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
  236.     }
  237.  
  238.     $defaults = array('order' => 'ASC');
  239.     $args = wp_parse_args( $args, $defaults );
  240.     extract($args, EXTR_SKIP);
  241.  
  242.     $order = ( 'desc' == strtolower($order) ) ? 'DESC' : 'ASC';
  243.  
  244.     $terms = array_map('intval', $terms);
  245.  
  246.     $taxonomies = "'" . implode("', '", $taxonomies) . "'";
  247.     $terms = "'" . implode("', '", $terms) . "'";
  248.  
  249.     $object_ids = $wpdb->get_col("SELECT tr.object_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tt.term_id IN ($terms) ORDER BY tr.object_id $order");
  250.  
  251.     if ( ! $object_ids )
  252.         return array();
  253.  
  254.     return $object_ids;
  255. }
  256.  
  257. /**
  258.  * Get all Term data from database by Term ID.
  259.  *
  260.  * The usage of the get_term function is to apply filters to a term object. It
  261.  * is possible to get a term object from the database before applying the
  262.  * filters.
  263.  *
  264.  * $term ID must be part of $taxonomy, to get from the database. Failure, might
  265.  * be able to be captured by the hooks. Failure would be the same value as $wpdb
  266.  * returns for the get_row method.
  267.  *
  268.  * There are two hooks, one is specifically for each term, named 'get_term', and
  269.  * the second is for the taxonomy name, 'term_$taxonomy'. Both hooks gets the
  270.  * term object, and the taxonomy name as parameters. Both hooks are expected to
  271.  * return a Term object.
  272.  *
  273.  * 'get_term' hook - Takes two parameters the term Object and the taxonomy name.
  274.  * Must return term object. Used in get_term() as a catch-all filter for every
  275.  * $term.
  276.  *
  277.  * 'get_$taxonomy' hook - Takes two parameters the term Object and the taxonomy
  278.  * name. Must return term object. $taxonomy will be the taxonomy name, so for
  279.  * example, if 'category', it would be 'get_category' as the filter name. Useful
  280.  * for custom taxonomies or plugging into default taxonomies.
  281.  *
  282.  * @package WordPress
  283.  * @subpackage Taxonomy
  284.  * @since 2.3
  285.  *
  286.  * @uses $wpdb
  287.  * @uses sanitize_term() Cleanses the term based on $filter context before returning.
  288.  * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
  289.  *
  290.  * @param int|object $term If integer, will get from database. If object will apply filters and return $term.
  291.  * @param string $taxonomy Taxonomy name that $term is part of.
  292.  * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N
  293.  * @param string $filter Optional, default is raw or no WordPress defined filter will applied.
  294.  * @return mixed|null|WP_Error Term Row from database. Will return null if $term is empty. If taxonomy does not
  295.  * exist then WP_Error will be returned.
  296.  */
  297. function &get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') {
  298.     global $wpdb;
  299.  
  300.     if ( empty($term) )
  301.         return null;
  302.  
  303.     if ( ! is_taxonomy($taxonomy) )
  304.         return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
  305.  
  306.     if ( is_object($term) ) {
  307.         wp_cache_add($term->term_id, $term, $taxonomy);
  308.         $_term = $term;
  309.     } else {
  310.         $term = (int) $term;
  311.         if ( ! $_term = wp_cache_get($term, $taxonomy) ) {
  312.             $_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND t.term_id = %s LIMIT 1", $taxonomy, $term) );
  313.             wp_cache_add($term, $_term, $taxonomy);
  314.         }
  315.     }
  316.  
  317.     $_term = apply_filters('get_term', $_term, $taxonomy);
  318.     $_term = apply_filters("get_$taxonomy", $_term, $taxonomy);
  319.     $_term = sanitize_term($_term, $taxonomy, $filter);
  320.  
  321.     if ( $output == OBJECT ) {
  322.         return $_term;
  323.     } elseif ( $output == ARRAY_A ) {
  324.         return get_object_vars($_term);
  325.     } elseif ( $output == ARRAY_N ) {
  326.         return array_values(get_object_vars($_term));
  327.     } else {
  328.         return $_term;
  329.     }
  330. }
  331.  
  332. /**
  333.  * Get all Term data from database by Term field and data.
  334.  *
  335.  * Warning: $value is not escaped for 'name' $field. You must do it yourself, if
  336.  * required.
  337.  *
  338.  * The default $field is 'id', therefore it is possible to also use null for
  339.  * field, but not recommended that you do so.
  340.  *
  341.  * If $value does not exist, the return value will be false. If $taxonomy exists
  342.  * and $field and $value combinations exist, the Term will be returned.
  343.  *
  344.  * @package WordPress
  345.  * @subpackage Taxonomy
  346.  * @since 2.3
  347.  *
  348.  * @uses $wpdb
  349.  * @uses sanitize_term() Cleanses the term based on $filter context before returning.
  350.  * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
  351.  *
  352.  * @param string $field Either 'slug', 'name', or 'id'
  353.  * @param string|int $value Search for this term value
  354.  * @param string $taxonomy Taxonomy Name
  355.  * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N
  356.  * @param string $filter Optional, default is raw or no WordPress defined filter will applied.
  357.  * @return mixed Term Row from database. Will return false if $taxonomy does not exist or $term was not found.
  358.  */
  359. function get_term_by($field, $value, $taxonomy, $output = OBJECT, $filter = 'raw') {
  360.     global $wpdb;
  361.  
  362.     if ( ! is_taxonomy($taxonomy) )
  363.         return false;
  364.  
  365.     if ( 'slug' == $field ) {
  366.         $field = 't.slug';
  367.         $value = sanitize_title($value);
  368.         if ( empty($value) )
  369.             return false;
  370.     } else if ( 'name' == $field ) {
  371.         // Assume already escaped
  372.         $field = 't.name';
  373.     } else {
  374.         $field = 't.term_id';
  375.         $value = (int) $value;
  376.     }
  377.  
  378.     $term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND $field = %s LIMIT 1", $taxonomy, $value) );
  379.     if ( !$term )
  380.         return false;
  381.  
  382.     wp_cache_add($term->term_id, $term, $taxonomy);
  383.  
  384.     $term = sanitize_term($term, $taxonomy, $filter);
  385.  
  386.     if ( $output == OBJECT ) {
  387.         return $term;
  388.     } elseif ( $output == ARRAY_A ) {
  389.         return get_object_vars($term);
  390.     } elseif ( $output == ARRAY_N ) {
  391.         return array_values(get_object_vars($term));
  392.     } else {
  393.         return $term;
  394.     }
  395. }
  396.  
  397. /**
  398.  * Merge all term children into a single array.
  399.  *
  400.  * This recursive function will merge all of the children of $term into the same
  401.  * array. Only useful for taxonomies which are hierarchical.
  402.  *
  403.  * Will return an empty array if $term does not exist in $taxonomy.
  404.  *
  405.  * @package WordPress
  406.  * @subpackage Taxonomy
  407.  * @since 2.3
  408.  *
  409.  * @uses $wpdb
  410.  * @uses _get_term_hierarchy()
  411.  * @uses get_term_children() Used to get the children of both $taxonomy and the parent $term
  412.  *
  413.  * @param string $term Name of Term to get children
  414.  * @param string $taxonomy Taxonomy Name
  415.  * @return array|WP_Error List of Term Objects. WP_Error returned if $taxonomy does not exist
  416.  */
  417. function get_term_children( $term, $taxonomy ) {
  418.     if ( ! is_taxonomy($taxonomy) )
  419.         return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
  420.  
  421.     $terms = _get_term_hierarchy($taxonomy);
  422.  
  423.     if ( ! isset($terms[$term]) )
  424.         return array();
  425.  
  426.     $children = $terms[$term];
  427.  
  428.     foreach ( $terms[$term] as $child ) {
  429.         if ( isset($terms[$child]) )
  430.             $children = array_merge($children, get_term_children($child, $taxonomy));
  431.     }
  432.  
  433.     return $children;
  434. }
  435.  
  436. /**
  437.  * Get sanitized Term field.
  438.  *
  439.  * Does checks for $term, based on the $taxonomy. The function is for contextual
  440.  * reasons and for simplicity of usage. See sanitize_term_field() for more
  441.  * information.
  442.  *
  443.  * @package WordPress
  444.  * @subpackage Taxonomy
  445.  * @since 2.3
  446.  *
  447.  * @uses sanitize_term_field() Passes the return value in sanitize_term_field on success.
  448.  *
  449.  * @param string $field Term field to fetch
  450.  * @param int $term Term ID
  451.  * @param string $taxonomy Taxonomy Name
  452.  * @param string $context Optional, default is display. Look at sanitize_term_field() for available options.
  453.  * @return mixed Will return an empty string if $term is not an object or if $field is not set in $term.
  454.  */
  455. function get_term_field( $field, $term, $taxonomy, $context = 'display' ) {
  456.     $term = (int) $term;
  457.     $term = get_term( $term, $taxonomy );
  458.     if ( is_wp_error($term) )
  459.         return $term;
  460.  
  461.     if ( !is_object($term) )
  462.         return '';
  463.  
  464.     if ( !isset($term->$field) )
  465.         return '';
  466.  
  467.     return sanitize_term_field($field, $term->$field, $term->term_id, $taxonomy, $context);
  468. }
  469.  
  470. /**
  471.  * Sanitizes Term for editing.
  472.  *
  473.  * Return value is sanitize_term() and usage is for sanitizing the term for
  474.  * editing. Function is for contextual and simplicity.
  475.  *
  476.  * @package WordPress
  477.  * @subpackage Taxonomy
  478.  * @since 2.3
  479.  *
  480.  * @uses sanitize_term() Passes the return value on success
  481.  *
  482.  * @param int|object $id Term ID or Object
  483.  * @param string $taxonomy Taxonomy Name
  484.  * @return mixed|null|WP_Error Will return empty string if $term is not an object.
  485.  */
  486. function get_term_to_edit( $id, $taxonomy ) {
  487.     $term = get_term( $id, $taxonomy );
  488.  
  489.     if ( is_wp_error($term) )
  490.         return $term;
  491.  
  492.     if ( !is_object($term) )
  493.         return '';
  494.  
  495.     return sanitize_term($term, $taxonomy, 'edit');
  496. }
  497.  
  498. /**
  499.  * Retrieve the terms in taxonomy or list of taxonomies.
  500.  *
  501.  * You can fully inject any customizations to the query before it is sent, as
  502.  * well as control the output with a filter.
  503.  *
  504.  * The 'get_terms' filter will be called when the cache has the term and will
  505.  * pass the found term along with the array of $taxonomies and array of $args.
  506.  * This filter is also called before the array of terms is passed and will pass
  507.  * the array of terms, along with the $taxonomies and $args.
  508.  *
  509.  * The 'list_terms_exclusions' filter passes the compiled exclusions along with
  510.  * the $args.
  511.  *
  512.  * The list that $args can contain, which will overwrite the defaults.
  513.  *
  514.  * orderby - Default is 'name'. Can be name, count, or nothing (will use
  515.  * term_id).
  516.  *
  517.  * order - Default is ASC. Can use DESC.
  518.  * hide_empty - Default is true. Will not return empty $terms.
  519.  * fields - Default is all.
  520.  * slug - Any terms that has this value. Default is empty string.
  521.  * hierarchical - Whether to return hierarchical taxonomy. Default is true.
  522.  * name__like - Default is empty string.
  523.  *
  524.  * The argument 'pad_counts' will count all of the children along with the
  525.  * $terms.
  526.  *
  527.  * The 'get' argument allows for overwriting 'hide_empty' and 'child_of', which
  528.  * can be done by setting the value to 'all', instead of its default empty
  529.  * string value.
  530.  *
  531.  * The 'child_of' argument will be used if you use multiple taxonomy or the
  532.  * first $taxonomy isn't hierarchical or 'parent' isn't used. The default is 0,
  533.  * which will be translated to a false value. If 'child_of' is set, then
  534.  * 'child_of' value will be tested against $taxonomy to see if 'child_of' is
  535.  * contained within. Will return an empty array if test fails.
  536.  *
  537.  * If 'parent' is set, then it will be used to test against the first taxonomy.
  538.  * Much like 'child_of'. Will return an empty array if the test fails.
  539.  *
  540.  * @package WordPress
  541.  * @subpackage Taxonomy
  542.  * @since 2.3
  543.  *
  544.  * @uses $wpdb
  545.  * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings.
  546.  *
  547.  * @param string|array Taxonomy name or list of Taxonomy names
  548.  * @param string|array $args The values of what to search for when returning terms
  549.  * @return array|WP_Error List of Term Objects and their children. Will return WP_Error, if any of $taxonomies do not exist.
  550.  */
  551. function &get_terms($taxonomies, $args = '') {
  552.     global $wpdb;
  553.     $empty_array = array();
  554.  
  555.     $single_taxonomy = false;
  556.     if ( !is_array($taxonomies) ) {
  557.         $single_taxonomy = true;
  558.         $taxonomies = array($taxonomies);
  559.     }
  560.  
  561.     foreach ( $taxonomies as $taxonomy ) {
  562.         if ( ! is_taxonomy($taxonomy) )
  563.             return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
  564.     }
  565.  
  566.     $in_taxonomies = "'" . implode("', '", $taxonomies) . "'";
  567.  
  568.     $defaults = array('orderby' => 'name', 'order' => 'ASC',
  569.         'hide_empty' => true, 'exclude' => '', 'include' => '',
  570.         'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '',
  571.         'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '',
  572.         'pad_counts' => false, 'offset' => '', 'search' => '');
  573.     $args = wp_parse_args( $args, $defaults );
  574.     $args['number'] = absint( $args['number'] );
  575.     $args['offset'] = absint( $args['offset'] );
  576.     if ( !$single_taxonomy || !is_taxonomy_hierarchical($taxonomies[0]) ||
  577.         '' != $args['parent'] ) {
  578.         $args['child_of'] = 0;
  579.         $args['hierarchical'] = false;
  580.         $args['pad_counts'] = false;
  581.     }
  582.  
  583.     if ( 'all' == $args['get'] ) {
  584.         $args['child_of'] = 0;
  585.         $args['hide_empty'] = 0;
  586.         $args['hierarchical'] = false;
  587.         $args['pad_counts'] = false;
  588.     }
  589.     extract($args, EXTR_SKIP);
  590.  
  591.     if ( $child_of ) {
  592.         $hierarchy = _get_term_hierarchy($taxonomies[0]);
  593.         if ( !isset($hierarchy[$child_of]) )
  594.             return $empty_array;
  595.     }
  596.  
  597.     if ( $parent ) {
  598.         $hierarchy = _get_term_hierarchy($taxonomies[0]);
  599.         if ( !isset($hierarchy[$parent]) )
  600.             return $empty_array;
  601.     }
  602.  
  603.     // $args can be whatever, only use the args defined in defaults to compute the key
  604.     $filter_key = ( has_filter('list_terms_exclusions') ) ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : '';
  605.     $key = md5( serialize( compact(array_keys($defaults)) ) . serialize( $taxonomies ) . $filter_key );
  606.  
  607.     if ( $cache = wp_cache_get( 'get_terms', 'terms' ) ) {
  608.         if ( isset( $cache[ $key ] ) )
  609.             return apply_filters('get_terms', $cache[$key], $taxonomies, $args);
  610.     }
  611.  
  612.     if ( 'count' == $orderby )
  613.         $orderby = 'tt.count';
  614.     else if ( 'name' == $orderby )
  615.         $orderby = 't.name';
  616.     else if ( 'slug' == $orderby )
  617.         $orderby = 't.slug';
  618.     else if ( 'term_group' == $orderby )
  619.         $orderby = 't.term_group';
  620.     else
  621.         $orderby = 't.term_id';
  622.  
  623.     $where = '';
  624.     $inclusions = '';
  625.     if ( !empty($include) ) {
  626.         $exclude = '';
  627.         $interms = preg_split('/[\s,]+/',$include);
  628.         if ( count($interms) ) {
  629.             foreach ( $interms as $interm ) {
  630.                 if (empty($inclusions))
  631.                     $inclusions = ' AND ( t.term_id = ' . intval($interm) . ' ';
  632.                 else
  633.                     $inclusions .= ' OR t.term_id = ' . intval($interm) . ' ';
  634.             }
  635.         }
  636.     }
  637.  
  638.     if ( !empty($inclusions) )
  639.         $inclusions .= ')';
  640.     $where .= $inclusions;
  641.  
  642.     $exclusions = '';
  643.     if ( !empty($exclude) ) {
  644.         $exterms = preg_split('/[\s,]+/',$exclude);
  645.         if ( count($exterms) ) {
  646.             foreach ( $exterms as $exterm ) {
  647.                 if (empty($exclusions))
  648.                     $exclusions = ' AND ( t.term_id <> ' . intval($exterm) . ' ';
  649.                 else
  650.                     $exclusions .= ' AND t.term_id <> ' . intval($exterm) . ' ';
  651.             }
  652.         }
  653.     }
  654.  
  655.     if ( !empty($exclusions) )
  656.         $exclusions .= ')';
  657.     $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args );
  658.     $where .= $exclusions;
  659.  
  660.     if ( !empty($slug) ) {
  661.         $slug = sanitize_title($slug);
  662.         $where .= " AND t.slug = '$slug'";
  663.     }
  664.  
  665.     if ( !empty($name__like) )
  666.         $where .= " AND t.name LIKE '{$name__like}%'";
  667.  
  668.     if ( '' != $parent ) {
  669.         $parent = (int) $parent;
  670.         $where .= " AND tt.parent = '$parent'";
  671.     }
  672.  
  673.     if ( $hide_empty && !$hierarchical )
  674.         $where .= ' AND tt.count > 0';
  675.  
  676.     if ( !empty($number) ) {
  677.         if( $offset )
  678.             $number = 'LIMIT ' . $offset . ',' . $number;
  679.         else
  680.             $number = 'LIMIT ' . $number;
  681.  
  682.     } else
  683.         $number = '';
  684.  
  685.     if ( !empty($search) ) {
  686.         $search = like_escape($search);
  687.         $where .= " AND (t.name LIKE '%$search%')";
  688.     }
  689.  
  690.     $select_this = '';
  691.     if ( 'all' == $fields )
  692.         $select_this = 't.*, tt.*';
  693.     else if ( 'ids' == $fields )
  694.         $select_this = 't.term_id';
  695.     else if ( 'names' == $fields )
  696.         $select_this = 't.name';
  697.  
  698.     $query = "SELECT $select_this FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy IN ($in_taxonomies) $where ORDER BY $orderby $order $number";
  699.  
  700.     if ( 'all' == $fields ) {
  701.         $terms = $wpdb->get_results($query);
  702.         update_term_cache($terms);
  703.     } else if ( ('ids' == $fields) || ('names' == $fields) ) {
  704.         $terms = $wpdb->get_col($query);
  705.     }
  706.  
  707.     if ( empty($terms) ) {
  708.         $cache[ $key ] = array();
  709.         wp_cache_set( 'get_terms', $cache, 'terms' );
  710.         return apply_filters('get_terms', array(), $taxonomies, $args);
  711.     }
  712.  
  713.     if ( $child_of || $hierarchical ) {
  714.         $children = _get_term_hierarchy($taxonomies[0]);
  715.         if ( ! empty($children) )
  716.             $terms = & _get_term_children($child_of, $terms, $taxonomies[0]);
  717.     }
  718.  
  719.     // Update term counts to include children.
  720.     if ( $pad_counts )
  721.         _pad_term_counts($terms, $taxonomies[0]);
  722.  
  723.     // Make sure we show empty categories that have children.
  724.     if ( $hierarchical && $hide_empty ) {
  725.         foreach ( $terms as $k => $term ) {
  726.             if ( ! $term->count ) {
  727.                 $children = _get_term_children($term->term_id, $terms, $taxonomies[0]);
  728.                 foreach ( $children as $child )
  729.                     if ( $child->count )
  730.                         continue 2;
  731.  
  732.                 // It really is empty
  733.                 unset($terms[$k]);
  734.             }
  735.         }
  736.     }
  737.     reset ( $terms );
  738.  
  739.     $cache[ $key ] = $terms;
  740.     wp_cache_set( 'get_terms', $cache, 'terms' );
  741.  
  742.     $terms = apply_filters('get_terms', $terms, $taxonomies, $args);
  743.     return $terms;
  744. }
  745.  
  746. /**
  747.  * Check if Term exists.
  748.  *
  749.  * Returns the index of a defined term, or 0 (false) if the term doesn't exist.
  750.  *
  751.  * @package WordPress
  752.  * @subpackage Taxonomy
  753.  * @since 2.3
  754.  *
  755.  * @uses $wpdb
  756.  *
  757.  * @param int|string $term The term to check
  758.  * @param string $taxonomy The taxonomy name to use
  759.  * @return mixed Get the term id or Term Object, if exists.
  760.  */
  761. function is_term($term, $taxonomy = '') {
  762.     global $wpdb;
  763.  
  764.     $select = "SELECT term_id FROM $wpdb->terms as t WHERE ";
  765.     $tax_select = "SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE ";
  766.  
  767.     if ( is_int($term) ) {
  768.         if ( 0 == $term )
  769.             return 0;
  770.         $where = 't.term_id = %d';
  771.         if ( !empty($taxonomy) )
  772.             return $wpdb->get_row( $wpdb->prepare( $tax_select . $where . " AND tt.taxonomy = %s", $term, $taxonomy ), ARRAY_A );
  773.         else
  774.             return $wpdb->get_var( $wpdb->prepare( $select . $where, $term ) );
  775.     }
  776.  
  777.     if ( '' === $slug = sanitize_title($term) )
  778.         return 0;
  779.  
  780.     $where = 't.slug = %s';
  781.     $else_where = 't.name = %s';
  782.  
  783.     if ( !empty($taxonomy) ) {
  784.         if ( $result = $wpdb->get_row( $wpdb->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE $where AND tt.taxonomy = %s", $slug, $taxonomy), ARRAY_A) )
  785.             return $result;
  786.             
  787.         return $wpdb->get_row( $wpdb->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE $else_where AND tt.taxonomy = %s", $term, $taxonomy), ARRAY_A);
  788.     }
  789.  
  790.     if ( $result = $wpdb->get_var( $wpdb->prepare("SELECT term_id FROM $wpdb->terms as t WHERE $where", $slug) ) )
  791.         return $result;
  792.  
  793.     return $wpdb->get_var( $wpdb->prepare("SELECT term_id FROM $wpdb->terms as t WHERE $else_where", $term) );
  794. }
  795.  
  796. /**
  797.  * Sanitize Term all fields.
  798.  *
  799.  * Relys on sanitize_term_field() to sanitize the term. The difference is that
  800.  * this function will sanitize <strong>all</strong> fields. The context is based
  801.  * on sanitize_term_field().
  802.  *
  803.  * The $term is expected to be either an array or an object.
  804.  *
  805.  * @package WordPress
  806.  * @subpackage Taxonomy
  807.  * @since 2.3
  808.  *
  809.  * @uses sanitize_term_field Used to sanitize all fields in a term
  810.  *
  811.  * @param array|object $term The term to check
  812.  * @param string $taxonomy The taxonomy name to use
  813.  * @param string $context Default is 'display'.
  814.  * @return array|object Term with all fields sanitized
  815.  */
  816. function sanitize_term($term, $taxonomy, $context = 'display') {
  817.  
  818.     if ( 'raw' == $context )
  819.         return $term;
  820.  
  821.     $fields = array('term_id', 'name', 'description', 'slug', 'count', 'parent', 'term_group');
  822.  
  823.     $do_object = false;
  824.     if ( is_object($term) )
  825.         $do_object = true;
  826.  
  827.     foreach ( $fields as $field ) {
  828.         if ( $do_object )
  829.             $term->$field = sanitize_term_field($field, $term->$field, $term->term_id, $taxonomy, $context);
  830.         else
  831.             $term[$field] = sanitize_term_field($field, $term[$field], $term['term_id'], $taxonomy, $context);
  832.     }
  833.  
  834.     return $term;
  835. }
  836.  
  837. /**
  838.  * Cleanse the field value in the term based on the context.
  839.  *
  840.  * Passing a term field value through the function should be assumed to have
  841.  * cleansed the value for whatever context the term field is going to be used.
  842.  *
  843.  * If no context or an unsupported context is given, then default filters will
  844.  * be applied.
  845.  *
  846.  * There are enough filters for each context to support a custom filtering
  847.  * without creating your own filter function. Simply create a function that
  848.  * hooks into the filter you need.
  849.  *
  850.  * @package WordPress
  851.  * @subpackage Taxonomy
  852.  * @since 2.3
  853.  *
  854.  * @uses $wpdb
  855.  *
  856.  * @param string $field Term field to sanitize
  857.  * @param string $value Search for this term value
  858.  * @param int $term_id Term ID
  859.  * @param string $taxonomy Taxonomy Name
  860.  * @param string $context Either edit, db, display, attribute, or js.
  861.  * @return mixed sanitized field
  862.  */
  863. function sanitize_term_field($field, $value, $term_id, $taxonomy, $context) {
  864.     if ( 'parent' == $field  || 'term_id' == $field || 'count' == $field || 'term_group' == $field ) {
  865.         $value = (int) $value;
  866.         if ( $value < 0 )
  867.             $value = 0;
  868.     }
  869.  
  870.     if ( 'raw' == $context )
  871.         return $value;
  872.  
  873.     if ( 'edit' == $context ) {
  874.         $value = apply_filters("edit_term_$field", $value, $term_id, $taxonomy);
  875.         $value = apply_filters("edit_${taxonomy}_$field", $value, $term_id);
  876.         if ( 'description' == $field )
  877.             $value = format_to_edit($value);
  878.         else
  879.             $value = attribute_escape($value);
  880.     } else if ( 'db' == $context ) {
  881.         $value = apply_filters("pre_term_$field", $value, $taxonomy);
  882.         $value = apply_filters("pre_${taxonomy}_$field", $value);
  883.         // Back compat filters
  884.         if ( 'slug' == $field )
  885.             $value = apply_filters('pre_category_nicename', $value);
  886.  
  887.     } else if ( 'rss' == $context ) {
  888.         $value = apply_filters("term_${field}_rss", $value, $taxonomy);
  889.         $value = apply_filters("${taxonomy}_${field}_rss", $value);
  890.     } else {
  891.         // Use display filters by default.
  892.         $value = apply_filters("term_$field", $value, $term_id, $taxonomy, $context);
  893.         $value = apply_filters("${taxonomy}_$field", $value, $term_id, $context);
  894.     }
  895.  
  896.     if ( 'attribute' == $context )
  897.         $value = attribute_escape($value);
  898.     else if ( 'js' == $context )
  899.         $value = js_escape($value);
  900.  
  901.     return $value;
  902. }
  903.  
  904. /**
  905.  * Count how many terms are in Taxonomy.
  906.  *
  907.  * Default $args is 'ignore_empty' which can be <code>'ignore_empty=true'</code>
  908.  * or <code>array('ignore_empty' => true);</code>.
  909.  *
  910.  * @package WordPress
  911.  * @subpackage Taxonomy
  912.  * @since 2.3
  913.  *
  914.  * @uses $wpdb
  915.  * @uses wp_parse_args() Turns strings into arrays and merges defaults into an array.
  916.  *
  917.  * @param string $taxonomy Taxonomy name
  918.  * @param array|string $args Overwrite defaults
  919.  * @return int How many terms are in $taxonomy
  920.  */
  921. function wp_count_terms( $taxonomy, $args = array() ) {
  922.     global $wpdb;
  923.  
  924.     $defaults = array('ignore_empty' => false);
  925.     $args = wp_parse_args($args, $defaults);
  926.     extract($args, EXTR_SKIP);
  927.  
  928.     $where = '';
  929.     if ( $ignore_empty )
  930.         $where = 'AND count > 0';
  931.  
  932.     return $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->term_taxonomy WHERE taxonomy = %s $where", $taxonomy) );
  933. }
  934.  
  935. /**
  936.  * Will unlink the term from the taxonomy.
  937.  *
  938.  * Will remove the term's relationship to the taxonomy, not the term or taxonomy
  939.  * itself. The term and taxonomy will still exist. Will require the term's
  940.  * object ID to perform the operation.
  941.  *
  942.  * @package WordPress
  943.  * @subpackage Taxonomy
  944.  * @since 2.3
  945.  * @uses $wpdb
  946.  *
  947.  * @param int $object_id The term Object Id that refers to the term
  948.  * @param string|array $taxonomy List of Taxonomy Names or single Taxonomy name.
  949.  */
  950. function wp_delete_object_term_relationships( $object_id, $taxonomies ) {
  951.     global $wpdb;
  952.  
  953.     $object_id = (int) $object_id;
  954.  
  955.     if ( !is_array($taxonomies) )
  956.         $taxonomies = array($taxonomies);
  957.  
  958.     foreach ( $taxonomies as $taxonomy ) {
  959.         $terms = wp_get_object_terms($object_id, $taxonomy, 'fields=tt_ids');
  960.         $in_terms = "'" . implode("', '", $terms) . "'";
  961.         $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_terms)", $object_id) );
  962.         wp_update_term_count($terms, $taxonomy);
  963.     }
  964. }
  965.  
  966. /**
  967.  * Removes a term from the database.
  968.  *
  969.  * If the term is a parent of other terms, then the children will be updated to
  970.  * that term's parent.
  971.  *
  972.  * The $args 'default' will only override the terms found, if there is only one
  973.  * term found. Any other and the found terms are used.
  974.  *
  975.  * @package WordPress
  976.  * @subpackage Taxonomy
  977.  * @since 2.3
  978.  *
  979.  * @uses $wpdb
  980.  * @uses do_action() Calls both 'delete_term' and 'delete_$taxonomy' action
  981.  *    hooks, passing term object, term id. 'delete_term' gets an additional
  982.  *    parameter with the $taxonomy parameter.
  983.  *
  984.  * @param int $term Term ID
  985.  * @param string $taxonomy Taxonomy Name
  986.  * @param array|string $args Optional. Change 'default' term id and override found term ids.
  987.  * @return bool|WP_Error Returns false if not term; true if completes delete action.
  988.  */
  989. function wp_delete_term( $term, $taxonomy, $args = array() ) {
  990.     global $wpdb;
  991.  
  992.     $term = (int) $term;
  993.  
  994.     if ( ! $ids = is_term($term, $taxonomy) )
  995.         return false;
  996.     if ( is_wp_error( $ids ) )
  997.         return $ids;
  998.  
  999.     $tt_id = $ids['term_taxonomy_id'];
  1000.  
  1001.     $defaults = array();
  1002.     $args = wp_parse_args($args, $defaults);
  1003.     extract($args, EXTR_SKIP);
  1004.  
  1005.     if ( isset($default) ) {
  1006.         $default = (int) $default;
  1007.         if ( ! is_term($default, $taxonomy) )
  1008.             unset($default);
  1009.     }
  1010.  
  1011.     // Update children to point to new parent
  1012.     if ( is_taxonomy_hierarchical($taxonomy) ) {
  1013.         $term_obj = get_term($term, $taxonomy);
  1014.         if ( is_wp_error( $term_obj ) )
  1015.             return $term_obj;
  1016.         $parent = $term_obj->parent;
  1017.  
  1018.         $wpdb->update( $wpdb->term_taxonomy, compact( 'parent' ), array( 'parent' => $term_obj->term_id) + compact( 'taxonomy' ) );
  1019.     }
  1020.  
  1021.     $objects = $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $tt_id ) );
  1022.  
  1023.     foreach ( (array) $objects as $object ) {
  1024.         $terms = wp_get_object_terms($object, $taxonomy, 'fields=ids');
  1025.         if ( 1 == count($terms) && isset($default) )
  1026.             $terms = array($default);
  1027.         else
  1028.             $terms = array_diff($terms, array($term));
  1029.         $terms = array_map('intval', $terms);
  1030.         wp_set_object_terms($object, $terms, $taxonomy);
  1031.     }
  1032.  
  1033.     $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $tt_id ) );
  1034.  
  1035.     // Delete the term if no taxonomies use it.
  1036.     if ( !$wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy WHERE term_id = %d", $term) ) )
  1037.         $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->terms WHERE term_id = %d", $term) );
  1038.  
  1039.     clean_term_cache($term, $taxonomy);
  1040.  
  1041.     do_action('delete_term', $term, $tt_id, $taxonomy);
  1042.     do_action("delete_$taxonomy", $term, $tt_id);
  1043.  
  1044.     return true;
  1045. }
  1046.  
  1047. /**
  1048.  * Retrieves the terms associated with the given object(s), in the supplied taxonomies.
  1049.  *
  1050.  * The following information has to do the $args parameter and for what can be
  1051.  * contained in the string or array of that parameter, if it exists.
  1052.  *
  1053.  * The first argument is called, 'orderby' and has the default value of 'name'.
  1054.  * The other value that is supported is 'count'.
  1055.  *
  1056.  * The second argument is called, 'order' and has the default value of 'ASC'.
  1057.  * The only other value that will be acceptable is 'DESC'.
  1058.  *
  1059.  * The final argument supported is called, 'fields' and has the default value of
  1060.  * 'all'. There are multiple other options that can be used instead. Supported
  1061.  * values are as follows: 'all', 'ids', 'names', and finally
  1062.  * 'all_with_object_id'.
  1063.  *
  1064.  * The fields argument also decides what will be returned. If 'all' or
  1065.  * 'all_with_object_id' is choosen or the default kept intact, then all matching
  1066.  * terms objects will be returned. If either 'ids' or 'names' is used, then an
  1067.  * array of all matching term ids or term names will be returned respectively.
  1068.  *
  1069.  * @package WordPress
  1070.  * @subpackage Taxonomy
  1071.  * @since 2.3
  1072.  * @uses $wpdb
  1073.  *
  1074.  * @param int|array $object_id The id of the object(s) to retrieve.
  1075.  * @param string|array $taxonomies The taxonomies to retrieve terms from.
  1076.  * @param array|string $args Change what is returned
  1077.  * @return array|WP_Error The requested term data or empty array if no terms found. WP_Error if $taxonomy does not exist.
  1078.  */
  1079. function wp_get_object_terms($object_ids, $taxonomies, $args = array()) {
  1080.     global $wpdb;
  1081.  
  1082.     if ( !is_array($taxonomies) )
  1083.         $taxonomies = array($taxonomies);
  1084.  
  1085.     foreach ( $taxonomies as $taxonomy ) {
  1086.         if ( ! is_taxonomy($taxonomy) )
  1087.             return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
  1088.     }
  1089.  
  1090.     if ( !is_array($object_ids) )
  1091.         $object_ids = array($object_ids);
  1092.     $object_ids = array_map('intval', $object_ids);
  1093.  
  1094.     $defaults = array('orderby' => 'name', 'order' => 'ASC', 'fields' => 'all');
  1095.     $args = wp_parse_args( $args, $defaults );
  1096.  
  1097.     $terms = array();
  1098.     if ( count($taxonomies) > 1 ) {
  1099.         foreach ( $taxonomies as $index => $taxonomy ) {
  1100.             $t = get_taxonomy($taxonomy);
  1101.             if ( isset($t->args) && is_array($t->args) && $args != array_merge($args, $t->args) ) {
  1102.                 unset($taxonomies[$index]);
  1103.                 $terms = array_merge($terms, wp_get_object_terms($object_ids, $taxonomy, array_merge($args, $t->args)));
  1104.             }
  1105.         }
  1106.     } else {
  1107.         $t = get_taxonomy($taxonomies[0]);
  1108.         if ( isset($t->args) && is_array($t->args) )
  1109.             $args = array_merge($args, $t->args);
  1110.     }
  1111.  
  1112.     extract($args, EXTR_SKIP);
  1113.  
  1114.     if ( 'count' == $orderby )
  1115.         $orderby = 'tt.count';
  1116.     else if ( 'name' == $orderby )
  1117.         $orderby = 't.name';
  1118.     else if ( 'slug' == $orderby )
  1119.         $orderby = 't.slug';
  1120.     else if ( 'term_group' == $orderby )
  1121.         $orderby = 't.term_group';
  1122.     else if ( 'term_order' == $orderby )
  1123.         $orderby = 'tr.term_order';
  1124.     else
  1125.         $orderby = 't.term_id';
  1126.  
  1127.     $taxonomies = "'" . implode("', '", $taxonomies) . "'";
  1128.     $object_ids = implode(', ', $object_ids);
  1129.  
  1130.     $select_this = '';
  1131.     if ( 'all' == $fields )
  1132.         $select_this = 't.*, tt.*';
  1133.     else if ( 'ids' == $fields )
  1134.         $select_this = 't.term_id';
  1135.     else if ( 'names' == $fields )
  1136.         $select_this = 't.name';
  1137.     else if ( 'all_with_object_id' == $fields )
  1138.         $select_this = 't.*, tt.*, tr.object_id';
  1139.  
  1140.     $query = "SELECT $select_this FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id INNER JOIN $wpdb->term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tr.object_id IN ($object_ids) ORDER BY $orderby $order";
  1141.  
  1142.     if ( 'all' == $fields || 'all_with_object_id' == $fields ) {
  1143.         $terms = array_merge($terms, $wpdb->get_results($query));
  1144.         update_term_cache($terms);
  1145.     } else if ( 'ids' == $fields || 'names' == $fields ) {
  1146.         $terms = array_merge($terms, $wpdb->get_col($query));
  1147.     } else if ( 'tt_ids' == $fields ) {
  1148.         $terms = $wpdb->get_col("SELECT tr.term_taxonomy_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tr.object_id IN ($object_ids) AND tt.taxonomy IN ($taxonomies) ORDER BY tr.term_taxonomy_id $order");
  1149.     }
  1150.  
  1151.     if ( ! $terms )
  1152.         return array();
  1153.  
  1154.     return $terms;
  1155. }
  1156.  
  1157. /**
  1158.  * Adds a new term to the database. Optionally marks it as an alias of an existing term.
  1159.  *
  1160.  * Error handling is assigned for the nonexistance of the $taxonomy and $term
  1161.  * parameters before inserting. If both the term id and taxonomy exist
  1162.  * previously, then an array will be returned that contains the term id and the
  1163.  * contents of what is returned. The keys of the array are 'term_id' and
  1164.  * 'term_taxonomy_id' containing numeric values.
  1165.  *
  1166.  * It is assumed that the term does not yet exist or the above will apply. The
  1167.  * term will be first added to the term table and then related to the taxonomy
  1168.  * if everything is well. If everything is correct, then several actions will be
  1169.  * run prior to a filter and then several actions will be run after the filter
  1170.  * is run.
  1171.  *
  1172.  * The arguments decide how the term is handled based on the $args parameter.
  1173.  * The following is a list of the available overrides and the defaults.
  1174.  *
  1175.  * 'alias_of'. There is no default, but if added, expected is the slug that the
  1176.  * term will be an alias of. Expected to be a string.
  1177.  *
  1178.  * 'description'. There is no default. If exists, will be added to the database
  1179.  * along with the term. Expected to be a string.
  1180.  *
  1181.  * 'parent'. Expected to be numeric and default is 0 (zero). Will assign value
  1182.  * of 'parent' to the term.
  1183.  *
  1184.  * 'slug'. Expected to be a string. There is no default.
  1185.  *
  1186.  * If 'slug' argument exists then the slug will be checked to see if it is not
  1187.  * a valid term. If that check succeeds (it is not a valid term), then it is
  1188.  * added and the term id is given. If it fails, then a check is made to whether
  1189.  * the taxonomy is hierarchical and the parent argument is not empty. If the
  1190.  * second check succeeds, the term will be inserted and the term id will be
  1191.  * given.
  1192.  *
  1193.  * @package WordPress
  1194.  * @subpackage Taxonomy
  1195.  * @since 2.3
  1196.  * @uses $wpdb
  1197.  *
  1198.  * @uses do_action() Calls 'create_term' hook with the term id and taxonomy id as parameters.
  1199.  * @uses do_action() Calls 'create_$taxonomy' hook with term id and taxonomy id as parameters.
  1200.  * @uses apply_filters() Calls 'term_id_filter' hook with term id and taxonomy id as parameters.
  1201.  * @uses do_action() Calls 'created_term' hook with the term id and taxonomy id as parameters.
  1202.  * @uses do_action() Calls 'created_$taxonomy' hook with term id and taxonomy id as parameters.
  1203.  *
  1204.  * @param int|string $term The term to add or update.
  1205.  * @param string $taxonomy The taxonomy to which to add the term
  1206.  * @param array|string $args Change the values of the inserted term
  1207.  * @return array|WP_Error The Term ID and Term Taxonomy ID
  1208.  */
  1209. function wp_insert_term( $term, $taxonomy, $args = array() ) {
  1210.     global $wpdb;
  1211.  
  1212.     if ( ! is_taxonomy($taxonomy) )
  1213.         return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
  1214.  
  1215.     if ( is_int($term) && 0 == $term )
  1216.         return new WP_Error('invalid_term_id', __('Invalid term ID'));
  1217.  
  1218.     if ( '' == trim($term) )
  1219.         return new WP_Error('empty_term_name', __('A name is required for this term'));
  1220.  
  1221.     $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => '');
  1222.     $args = wp_parse_args($args, $defaults);
  1223.     $args['name'] = $term;
  1224.     $args['taxonomy'] = $taxonomy;
  1225.     $args = sanitize_term($args, $taxonomy, 'db');
  1226.     extract($args, EXTR_SKIP);
  1227.  
  1228.     // expected_slashed ($name)
  1229.     $name = stripslashes($name);
  1230.     $description = stripslashes($description);
  1231.  
  1232.     if ( empty($slug) )
  1233.         $slug = sanitize_title($name);
  1234.  
  1235.     $term_group = 0;
  1236.     if ( $alias_of ) {
  1237.         $alias = $wpdb->get_row( $wpdb->prepare( "SELECT term_id, term_group FROM $wpdb->terms WHERE slug = %s", $alias_of) );
  1238.         if ( $alias->term_group ) {
  1239.             // The alias we want is already in a group, so let's use that one.
  1240.             $term_group = $alias->term_group;
  1241.         } else {
  1242.             // The alias isn't in a group, so let's create a new one and firstly add the alias term to it.
  1243.             $term_group = $wpdb->get_var("SELECT MAX(term_group) FROM $wpdb->terms") + 1;
  1244.             $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->terms SET term_group = %d WHERE term_id = %d", $term_group, $alias->term_id ) );
  1245.         }
  1246.     }
  1247.  
  1248.     if ( ! $term_id = is_term($slug) ) {
  1249.         if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) )
  1250.             return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
  1251.         $term_id = (int) $wpdb->insert_id;
  1252.     } else if ( is_taxonomy_hierarchical($taxonomy) && !empty($parent) ) {
  1253.         // If the taxonomy supports hierarchy and the term has a parent, make the slug unique
  1254.         // by incorporating parent slugs.
  1255.         $slug = wp_unique_term_slug($slug, (object) $args);
  1256.         if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) )
  1257.             return new WP_Error('db_insert_error', __('Could not insert term into the database'), $wpdb->last_error);
  1258.         $term_id = (int) $wpdb->insert_id;
  1259.     }
  1260.  
  1261.     if ( empty($slug) ) {
  1262.         $slug = sanitize_title($slug, $term_id);
  1263.         $wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) );
  1264.     }
  1265.  
  1266.     $tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id ) );
  1267.  
  1268.     if ( !empty($tt_id) )
  1269.         return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
  1270.  
  1271.     $wpdb->insert( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) );
  1272.     $tt_id = (int) $wpdb->insert_id;
  1273.  
  1274.     do_action("create_term", $term_id, $tt_id);
  1275.     do_action("create_$taxonomy", $term_id, $tt_id);
  1276.  
  1277.     $term_id = apply_filters('term_id_filter', $term_id, $tt_id);
  1278.  
  1279.     clean_term_cache($term_id, $taxonomy);
  1280.  
  1281.     do_action("created_term", $term_id, $tt_id);
  1282.     do_action("created_$taxonomy", $term_id, $tt_id);
  1283.  
  1284.     return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
  1285. }
  1286.  
  1287. /**
  1288.  * Create Term and Taxonomy Relationships.
  1289.  *
  1290.  * Relates an object (post, link etc) to a term and taxonomy type. Creates the
  1291.  * term and taxonomy relationship if it doesn't already exist. Creates a term if
  1292.  * it doesn't exist (using the slug).
  1293.  *
  1294.  * A relationship means that the term is grouped in or belongs to the taxonomy.
  1295.  * A term has no meaning until it is given context by defining which taxonomy it
  1296.  * exists under.
  1297.  *
  1298.  * @package WordPress
  1299.  * @subpackage Taxonomy
  1300.  * @since 2.3
  1301.  * @uses $wpdb
  1302.  *
  1303.  * @param int $object_id The object to relate to.
  1304.  * @param array|int|string $term The slug or id of the term.
  1305.  * @param array|string $taxonomy The context in which to relate the term to the object.
  1306.  * @param bool $append If false will delete difference of terms.
  1307.  * @return array|WP_Error Affected Term IDs
  1308.  */
  1309. function wp_set_object_terms($object_id, $terms, $taxonomy, $append = false) {
  1310.     global $wpdb;
  1311.  
  1312.     $object_id = (int) $object_id;
  1313.  
  1314.     if ( ! is_taxonomy($taxonomy) )
  1315.         return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));
  1316.  
  1317.     if ( !is_array($terms) )
  1318.         $terms = array($terms);
  1319.  
  1320.     if ( ! $append )
  1321.         $old_terms =  wp_get_object_terms($object_id, $taxonomy, 'fields=tt_ids');
  1322.  
  1323.     $tt_ids = array();
  1324.     $term_ids = array();
  1325.  
  1326.     foreach ($terms as $term) {
  1327.         if ( !strlen(trim($term)) )
  1328.             continue;
  1329.  
  1330.         if ( !$id = is_term($term, $taxonomy) )
  1331.             $id = wp_insert_term($term, $taxonomy);
  1332.         if ( is_wp_error($id) )
  1333.             return $id;
  1334.         $term_ids[] = $id['term_id'];
  1335.         $id = $id['term_taxonomy_id'];
  1336.         $tt_ids[] = $id;
  1337.  
  1338.         if ( $wpdb->get_var( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id = %d", $object_id, $id ) ) )
  1339.             continue;
  1340.         $wpdb->insert( $wpdb->term_relationships, array( 'object_id' => $object_id, 'term_taxonomy_id' => $id ) );
  1341.     }
  1342.  
  1343.     wp_update_term_count($tt_ids, $taxonomy);
  1344.  
  1345.     if ( ! $append ) {
  1346.         $delete_terms = array_diff($old_terms, $tt_ids);
  1347.         if ( $delete_terms ) {
  1348.             $in_delete_terms = "'" . implode("', '", $delete_terms) . "'";
  1349.             $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_delete_terms)", $object_id) );
  1350.             wp_update_term_count($delete_terms, $taxonomy);
  1351.         }
  1352.     }
  1353.  
  1354.     $t = get_taxonomy($taxonomy);
  1355.     if ( ! $append && isset($t->sort) && $t->sort ) {
  1356.         $values = array();
  1357.         $term_order = 0;
  1358.         $final_tt_ids = wp_get_object_terms($object_id, $taxonomy, 'fields=tt_ids');
  1359.         foreach ( $tt_ids as $tt_id )
  1360.             if ( in_array($tt_id, $final_tt_ids) )
  1361.                 $values[] = $wpdb->prepare( "(%d, %d, %d)", $object_id, $tt_id, ++$term_order);
  1362.         if ( $values )
  1363.             $wpdb->query("INSERT INTO $wpdb->term_relationships (object_id, term_taxonomy_id, term_order) VALUES " . join(',', $values) . " ON DUPLICATE KEY UPDATE term_order = VALUES(term_order)");
  1364.     }
  1365.  
  1366.     return $tt_ids;
  1367. }
  1368.  
  1369. /**
  1370.  * Will make slug unique, if it isn't already.
  1371.  *
  1372.  * The $slug has to be unique global to every taxonomy, meaning that one
  1373.  * taxonomy term can't have a matching slug with another taxonomy term. Each
  1374.  * slug has to be globally unique for every taxonomy.
  1375.  *
  1376.  * The way this works is that if the taxonomy that the term belongs to is
  1377.  * heirarchical and has a parent, it will append that parent to the $slug.
  1378.  *
  1379.  * If that still doesn't return an unique slug, then it try to append a number
  1380.  * until it finds a number that is truely unique.
  1381.  *
  1382.  * The only purpose for $term is for appending a parent, if one exists.
  1383.  *
  1384.  * @package WordPress
  1385.  * @subpackage Taxonomy
  1386.  * @since 2.3
  1387.  * @uses $wpdb
  1388.  *
  1389.  * @param string $slug The string that will be tried for a unique slug
  1390.  * @param object $term The term object that the $slug will belong too
  1391.  * @return string Will return a true unique slug.
  1392.  */
  1393. function wp_unique_term_slug($slug, $term) {
  1394.     global $wpdb;
  1395.  
  1396.     // If the taxonomy supports hierarchy and the term has a parent, make the slug unique
  1397.     // by incorporating parent slugs.
  1398.     if ( is_taxonomy_hierarchical($term->taxonomy) && !empty($term->parent) ) {
  1399.         $the_parent = $term->parent;
  1400.         while ( ! empty($the_parent) ) {
  1401.             $parent_term = get_term($the_parent, $term->taxonomy);
  1402.             if ( is_wp_error($parent_term) || empty($parent_term) )
  1403.                 break;
  1404.                 $slug .= '-' . $parent_term->slug;
  1405.             if ( empty($parent_term->parent) )
  1406.                 break;
  1407.             $the_parent = $parent_term->parent;
  1408.         }
  1409.     }
  1410.  
  1411.     // If we didn't get a unique slug, try appending a number to make it unique.
  1412.     if ( !empty($args['term_id']) )
  1413.         $query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s AND term_id != %d", $slug, $args['term_id'] );
  1414.     else
  1415.         $query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $slug );
  1416.  
  1417.     if ( $wpdb->get_var( $query ) ) {
  1418.         $num = 2;
  1419.         do {
  1420.             $alt_slug = $slug . "-$num";
  1421.             $num++;
  1422.             $slug_check = $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $alt_slug ) );
  1423.         } while ( $slug_check );
  1424.         $slug = $alt_slug;
  1425.     }
  1426.  
  1427.     return $slug;
  1428. }
  1429.  
  1430. /**
  1431.  * Update term based on arguments provided.
  1432.  *
  1433.  * The $args will indiscriminately override all values with the same field name.
  1434.  * Care must be taken to not override important information need to update or
  1435.  * update will fail (or perhaps create a new term, neither would be acceptable).
  1436.  *
  1437.  * Defaults will set 'alias_of', 'description', 'parent', and 'slug' if not
  1438.  * defined in $args already.
  1439.  *
  1440.  * 'alias_of' will create a term group, if it doesn't already exist, and update
  1441.  * it for the $term.
  1442.  *
  1443.  * If the 'slug' argument in $args is missing, then the 'name' in $args will be
  1444.  * used. It should also be noted that if you set 'slug' and it isn't unique then
  1445.  * a WP_Error will be passed back. If you don't pass any slug, then a unique one
  1446.  * will be created for you.
  1447.  *
  1448.  * For what can be overrode in $args, check the term scheme can contain and stay
  1449.  * away from the term keys.
  1450.  *
  1451.  * @package WordPress
  1452.  * @subpackage Taxonomy
  1453.  * @since 2.3
  1454.  *
  1455.  * @uses $wpdb
  1456.  * @uses do_action() Will call both 'edit_term' and 'edit_$taxonomy' twice.
  1457.  * @uses apply_filters() Will call the 'term_id_filter' filter and pass the term
  1458.  *    id and taxonomy id.
  1459.  *
  1460.  * @param int $term The ID of the term
  1461.  * @param string $taxonomy The context in which to relate the term to the object.
  1462.  * @param array|string $args Overwrite term field values
  1463.  * @return array|WP_Error Returns Term ID and Taxonomy Term ID
  1464.  */
  1465. function wp_update_term( $term, $taxonomy, $args = array() ) {
  1466.     global $wpdb;
  1467.  
  1468.     if ( ! is_taxonomy($taxonomy) )
  1469.         return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
  1470.  
  1471.     $term_id = (int) $term;
  1472.  
  1473.     // First, get all of the original args
  1474.     $term = get_term ($term_id, $taxonomy, ARRAY_A);
  1475.  
  1476.     // Escape data pulled from DB.
  1477.     $term = add_magic_quotes($term);
  1478.  
  1479.     // Merge old and new args with new args overwriting old ones.
  1480.     $args = array_merge($term, $args);
  1481.  
  1482.     $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => '');
  1483.     $args = wp_parse_args($args, $defaults);
  1484.     $args = sanitize_term($args, $taxonomy, 'db');
  1485.     extract($args, EXTR_SKIP);
  1486.  
  1487.     // expected_slashed ($name)
  1488.     $name = stripslashes($name);
  1489.     $description = stripslashes($description);
  1490.  
  1491.     if ( '' == trim($name) )
  1492.         return new WP_Error('empty_term_name', __('A name is required for this term'));
  1493.  
  1494.     $empty_slug = false;
  1495.     if ( empty($slug) ) {
  1496.         $empty_slug = true;
  1497.         $slug = sanitize_title($name);
  1498.     }
  1499.  
  1500.     if ( $alias_of ) {
  1501.         $alias = $wpdb->get_row( $wpdb->prepare( "SELECT term_id, term_group FROM $wpdb->terms WHERE slug = %s", $alias_of) );
  1502.         if ( $alias->term_group ) {
  1503.             // The alias we want is already in a group, so let's use that one.
  1504.             $term_group = $alias->term_group;
  1505.         } else {
  1506.             // The alias isn't in a group, so let's create a new one and firstly add the alias term to it.
  1507.             $term_group = $wpdb->get_var("SELECT MAX(term_group) FROM $wpdb->terms") + 1;
  1508.             $wpdb->update( $wpdb->terms, compact('term_group'), array( 'term_id' => $alias->term_id ) );
  1509.         }
  1510.     }
  1511.  
  1512.     // Check for duplicate slug
  1513.     $id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->terms WHERE slug = %s", $slug ) );
  1514.     if ( $id && ($id != $term_id) ) {
  1515.         // If an empty slug was passed or the parent changed, reset the slug to something unique.
  1516.         // Otherwise, bail.
  1517.         if ( $empty_slug || ( $parent != $term->parent) )
  1518.             $slug = wp_unique_term_slug($slug, (object) $args);
  1519.         else
  1520.             return new WP_Error('duplicate_term_slug', sprintf(__('The slug "%s" is already in use by another term'), $slug));
  1521.     }
  1522.  
  1523.     $wpdb->update($wpdb->terms, compact( 'name', 'slug', 'term_group' ), compact( 'term_id' ) );
  1524.  
  1525.     if ( empty($slug) ) {
  1526.         $slug = sanitize_title($name, $term_id);
  1527.         $wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) );
  1528.     }
  1529.  
  1530.     $tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) );
  1531.  
  1532.     $wpdb->update( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent' ), array( 'term_taxonomy_id' => $tt_id ) );
  1533.  
  1534.     do_action("edit_term", $term_id, $tt_id);
  1535.     do_action("edit_$taxonomy", $term_id, $tt_id);
  1536.  
  1537.     $term_id = apply_filters('term_id_filter', $term_id, $tt_id);
  1538.  
  1539.     clean_term_cache($term_id, $taxonomy);
  1540.  
  1541.     do_action("edited_term", $term_id, $tt_id);
  1542.     do_action("edited_$taxonomy", $term_id, $tt_id);
  1543.  
  1544.     return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
  1545. }
  1546.  
  1547. /**
  1548.  * Enable or disable term counting.
  1549.  *
  1550.  * @since 2.6
  1551.  *
  1552.  * @param bool $defer Optional.
  1553.  * @return bool
  1554.  */
  1555. function wp_defer_term_counting($defer=NULL) {
  1556.     static $_defer = false;
  1557.  
  1558.     if ( is_bool($defer) ) {
  1559.         $_defer = $defer;
  1560.         // flush any deferred counts
  1561.         if ( !$defer )
  1562.             wp_update_term_count( NULL, NULL, true );
  1563.     }
  1564.  
  1565.     return $_defer;
  1566. }
  1567.  
  1568. /**
  1569.  * Updates the amount of terms in taxonomy.
  1570.  *
  1571.  * If there is a taxonomy callback applyed, then it will be called for updating
  1572.  * the count.
  1573.  *
  1574.  * The default action is to count what the amount of terms have the relationship
  1575.  * of term ID. Once that is done, then update the database.
  1576.  *
  1577.  * @package WordPress
  1578.  * @subpackage Taxonomy
  1579.  * @since 2.3
  1580.  * @uses $wpdb
  1581.  *
  1582.  * @param int|array $terms The ID of the terms
  1583.  * @param string $taxonomy The context of the term.
  1584.  * @return bool If no terms will return false, and if successful will return true.
  1585.  */
  1586. function wp_update_term_count( $terms, $taxonomy, $do_deferred=false ) {
  1587.     static $_deferred = array();
  1588.  
  1589.     if ( $do_deferred ) {
  1590.         foreach ( array_keys($_deferred) as $tax ) {
  1591.             wp_update_term_count_now( $_deferred[$tax], $tax );
  1592.             unset( $_deferred[$tax] );
  1593.         }
  1594.     }
  1595.  
  1596.     if ( empty($terms) )
  1597.         return false;
  1598.  
  1599.     if ( !is_array($terms) )
  1600.         $terms = array($terms);
  1601.  
  1602.     if ( wp_defer_term_counting() ) {
  1603.         if ( !isset($_deferred[$taxonomy]) )
  1604.             $_deferred[$taxonomy] = array();
  1605.         $_deferred[$taxonomy] = array_unique( array_merge($_deferred[$taxonomy], $terms) );
  1606.         return true;
  1607.     }
  1608.  
  1609.     return wp_update_term_count_now( $terms, $taxonomy );
  1610. }
  1611.  
  1612. /**
  1613.  * Perform term count update immediately.
  1614.  *
  1615.  * @since 2.6
  1616.  *
  1617.  * @param array $terms IDs of Terms to update.
  1618.  * @param string $taxonomy The context of the term.
  1619.  * @return bool Always true when complete.
  1620.  */
  1621. function wp_update_term_count_now( $terms, $taxonomy ) {
  1622.     global $wpdb;
  1623.  
  1624.     $terms = array_map('intval', $terms);
  1625.  
  1626.     $taxonomy = get_taxonomy($taxonomy);
  1627.     if ( !empty($taxonomy->update_count_callback) ) {
  1628.         call_user_func($taxonomy->update_count_callback, $terms);
  1629.     } else {
  1630.         // Default count updater
  1631.         foreach ($terms as $term) {
  1632.             $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $term) );
  1633.             $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
  1634.         }
  1635.  
  1636.     }
  1637.  
  1638.     clean_term_cache($terms);
  1639.  
  1640.     return true;
  1641. }
  1642.  
  1643. //
  1644. // Cache
  1645. //
  1646.  
  1647.  
  1648. /**
  1649.  * Removes the taxonomy relationship to terms from the cache.
  1650.  *
  1651.  * Will remove the entire taxonomy relationship containing term $object_id. The
  1652.  * term IDs have to exist within the taxonomy $object_type for the deletion to
  1653.  * take place.
  1654.  *
  1655.  * @package WordPress
  1656.  * @subpackage Taxonomy
  1657.  * @since 2.3
  1658.  *
  1659.  * @see get_object_taxonomies() for more on $object_type
  1660.  * @uses do_action() Will call action hook named, 'clean_object_term_cache' after completion.
  1661.  *    Passes, function params in same order.
  1662.  *
  1663.  * @param int|array $object_ids Single or list of term object ID(s)
  1664.  * @param array|string $object_type The taxonomy object type
  1665.  */
  1666. function clean_object_term_cache($object_ids, $object_type) {
  1667.     if ( !is_array($object_ids) )
  1668.         $object_ids = array($object_ids);
  1669.  
  1670.     foreach ( $object_ids as $id )
  1671.         foreach ( get_object_taxonomies($object_type) as $taxonomy )
  1672.             wp_cache_delete($id, "{$taxonomy}_relationships");
  1673.  
  1674.     do_action('clean_object_term_cache', $object_ids, $object_type);
  1675. }
  1676.  
  1677.  
  1678. /**
  1679.  * Will remove all of the term ids from the cache.
  1680.  *
  1681.  * @package WordPress
  1682.  * @subpackage Taxonomy
  1683.  * @since 2.3
  1684.  * @uses $wpdb
  1685.  *
  1686.  * @param int|array $ids Single or list of Term IDs
  1687.  * @param string $taxonomy Can be empty and will assume tt_ids, else will use for context.
  1688.  */
  1689. function clean_term_cache($ids, $taxonomy = '') {
  1690.     global $wpdb;
  1691.  
  1692.     if ( !is_array($ids) )
  1693.         $ids = array($ids);
  1694.  
  1695.     $taxonomies = array();
  1696.     // If no taxonomy, assume tt_ids.
  1697.     if ( empty($taxonomy) ) {
  1698.         $tt_ids = implode(', ', $ids);
  1699.         $terms = $wpdb->get_results("SELECT term_id, taxonomy FROM $wpdb->term_taxonomy WHERE term_taxonomy_id IN ($tt_ids)");
  1700.         foreach ( (array) $terms as $term ) {
  1701.             $taxonomies[] = $term->taxonomy;
  1702.             wp_cache_delete($term->term_id, $term->taxonomy);
  1703.         }
  1704.         $taxonomies = array_unique($taxonomies);
  1705.     } else {
  1706.         foreach ( $ids as $id ) {
  1707.             wp_cache_delete($id, $taxonomy);
  1708.         }
  1709.         $taxonomies = array($taxonomy);
  1710.     }
  1711.  
  1712.     foreach ( $taxonomies as $taxonomy ) {
  1713.         wp_cache_delete('all_ids', $taxonomy);
  1714.         wp_cache_delete('get', $taxonomy);
  1715.         delete_option("{$taxonomy}_children");
  1716.     }
  1717.  
  1718.     wp_cache_delete('get_terms', 'terms');
  1719.  
  1720.     do_action('clean_term_cache', $ids, $taxonomy);
  1721. }
  1722.  
  1723.  
  1724. /**
  1725.  * Retrieves the taxonomy relationship to the term object id.
  1726.  *
  1727.  * @package WordPress
  1728.  * @subpackage Taxonomy
  1729.  * @since 2.3
  1730.  *
  1731.  * @uses wp_cache_get() Retrieves taxonomy relationship from cache
  1732.  *
  1733.  * @param int|array $id Term object ID
  1734.  * @param string $taxonomy Taxonomy Name
  1735.  * @return bool|array Empty array if $terms found, but not $taxonomy. False if nothing is in cache for $taxonomy and $id.
  1736.  */
  1737. function &get_object_term_cache($id, $taxonomy) {
  1738.     $cache = wp_cache_get($id, "{$taxonomy}_relationships");
  1739.     return $cache;
  1740. }
  1741.  
  1742.  
  1743. /**
  1744.  * Updates the cache for Term ID(s).
  1745.  *
  1746.  * Will only update the cache for terms not already cached.
  1747.  *
  1748.  * The $object_ids expects that the ids be separated by commas, if it is a
  1749.  * string.
  1750.  *
  1751.  * It should be noted that update_object_term_cache() is very time extensive. It
  1752.  * is advised that the function is not called very often or at least not for a
  1753.  * lot of terms that exist in a lot of taxonomies. The amount of time increases
  1754.  * for each term and it also increases for each taxonomy the term belongs to.
  1755.  *
  1756.  * @package WordPress
  1757.  * @subpackage Taxonomy
  1758.  * @since 2.3
  1759.  * @uses wp_get_object_terms() Used to get terms from the database to update
  1760.  *
  1761.  * @param string|array $object_ids Single or list of term object ID(s)
  1762.  * @param array|string $object_type The taxonomy object type
  1763.  * @return null|bool Null value is given with empty $object_ids. False if
  1764.  */
  1765. function update_object_term_cache($object_ids, $object_type) {
  1766.     if ( empty($object_ids) )
  1767.         return;
  1768.  
  1769.     if ( !is_array($object_ids) )
  1770.         $object_ids = explode(',', $object_ids);
  1771.  
  1772.     $object_ids = array_map('intval', $object_ids);
  1773.  
  1774.     $taxonomies = get_object_taxonomies($object_type);
  1775.  
  1776.     $ids = array();
  1777.     foreach ( (array) $object_ids as $id ) {
  1778.         foreach ( $taxonomies as $taxonomy ) {
  1779.             if ( false === wp_cache_get($id, "{$taxonomy}_relationships") ) {
  1780.                 $ids[] = $id;
  1781.                 break;
  1782.             }
  1783.         }
  1784.     }
  1785.  
  1786.     if ( empty( $ids ) )
  1787.         return false;
  1788.  
  1789.     $terms = wp_get_object_terms($ids, $taxonomies, 'fields=all_with_object_id');
  1790.  
  1791.     $object_terms = array();
  1792.     foreach ( (array) $terms as $term )
  1793.         $object_terms[$term->object_id][$term->taxonomy][$term->term_id] = $term;
  1794.  
  1795.     foreach ( $ids as $id ) {
  1796.         foreach ( $taxonomies  as $taxonomy ) {
  1797.             if ( ! isset($object_terms[$id][$taxonomy]) ) {
  1798.                 if ( !isset($object_terms[$id]) )
  1799.                     $object_terms[$id] = array();
  1800.                 $object_terms[$id][$taxonomy] = array();
  1801.             }
  1802.         }
  1803.     }
  1804.  
  1805.     foreach ( $object_terms as $id => $value ) {
  1806.         foreach ( $value as $taxonomy => $terms ) {
  1807.             wp_cache_set($id, $terms, "{$taxonomy}_relationships");
  1808.         }
  1809.     }
  1810. }
  1811.  
  1812.  
  1813. /**
  1814.  * Updates Terms to Taxonomy in cache.
  1815.  *
  1816.  * @package WordPress
  1817.  * @subpackage Taxonomy
  1818.  * @since 2.3
  1819.  *
  1820.  * @param array $terms List of Term objects to change
  1821.  * @param string $taxonomy Optional. Update Term to this taxonomy in cache
  1822.  */
  1823. function update_term_cache($terms, $taxonomy = '') {
  1824.     foreach ( $terms as $term ) {
  1825.         $term_taxonomy = $taxonomy;
  1826.         if ( empty($term_taxonomy) )
  1827.             $term_taxonomy = $term->taxonomy;
  1828.  
  1829.         wp_cache_add($term->term_id, $term, $term_taxonomy);
  1830.     }
  1831. }
  1832.  
  1833. //
  1834. // Private
  1835. //
  1836.  
  1837.  
  1838. /**
  1839.  * Retrieves children of taxonomy.
  1840.  *
  1841.  * @package WordPress
  1842.  * @subpackage Taxonomy
  1843.  * @access private
  1844.  * @since 2.3
  1845.  *
  1846.  * @uses update_option() Stores all of the children in "$taxonomy_children"
  1847.  *     option. That is the name of the taxonomy, immediately followed by '_children'.
  1848.  *
  1849.  * @param string $taxonomy Taxonomy Name
  1850.  * @return array Empty if $taxonomy isn't hierarachical or returns children.
  1851.  */
  1852. function _get_term_hierarchy($taxonomy) {
  1853.     if ( !is_taxonomy_hierarchical($taxonomy) )
  1854.         return array();
  1855.     $children = get_option("{$taxonomy}_children");
  1856.     if ( is_array($children) )
  1857.         return $children;
  1858.  
  1859.     $children = array();
  1860.     $terms = get_terms($taxonomy, 'get=all');
  1861.     foreach ( $terms as $term ) {
  1862.         if ( $term->parent > 0 )
  1863.             $children[$term->parent][] = $term->term_id;
  1864.     }
  1865.     update_option("{$taxonomy}_children", $children);
  1866.  
  1867.     return $children;
  1868. }
  1869.  
  1870.  
  1871. /**
  1872.  * Get array of child terms.
  1873.  *
  1874.  * If $terms is an array of objects, then objects will returned from the
  1875.  * function. If $terms is an array of IDs, then an array of ids of children will
  1876.  * be returned.
  1877.  *
  1878.  * @package WordPress
  1879.  * @subpackage Taxonomy
  1880.  * @access private
  1881.  * @since 2.3
  1882.  *
  1883.  * @param int $term_id Look for this Term ID in $terms
  1884.  * @param array $terms List of Term IDs
  1885.  * @param string $taxonomy Term Context
  1886.  * @return array Empty if $terms is empty else returns full list of child terms.
  1887.  */
  1888. function &_get_term_children($term_id, $terms, $taxonomy) {
  1889.     $empty_array = array();
  1890.     if ( empty($terms) )
  1891.         return $empty_array;
  1892.  
  1893.     $term_list = array();
  1894.     $has_children = _get_term_hierarchy($taxonomy);
  1895.  
  1896.     if  ( ( 0 != $term_id ) && ! isset($has_children[$term_id]) )
  1897.         return $empty_array;
  1898.  
  1899.     foreach ( $terms as $term ) {
  1900.         $use_id = false;
  1901.         if ( !is_object($term) ) {
  1902.             $term = get_term($term, $taxonomy);
  1903.             if ( is_wp_error( $term ) )
  1904.                 return $term;
  1905.             $use_id = true;
  1906.         }
  1907.  
  1908.         if ( $term->term_id == $term_id )
  1909.             continue;
  1910.  
  1911.         if ( $term->parent == $term_id ) {
  1912.             if ( $use_id )
  1913.                 $term_list[] = $term->term_id;
  1914.             else
  1915.                 $term_list[] = $term;
  1916.  
  1917.             if ( !isset($has_children[$term->term_id]) )
  1918.                 continue;
  1919.  
  1920.             if ( $children = _get_term_children($term->term_id, $terms, $taxonomy) )
  1921.                 $term_list = array_merge($term_list, $children);
  1922.         }
  1923.     }
  1924.  
  1925.     return $term_list;
  1926. }
  1927.  
  1928.  
  1929. /**
  1930.  * Add count of children to parent count.
  1931.  *
  1932.  * Recalculates term counts by including items from child terms. Assumes all
  1933.  * relevant children are already in the $terms argument.
  1934.  *
  1935.  * @package WordPress
  1936.  * @subpackage Taxonomy
  1937.  * @access private
  1938.  * @since 2.3
  1939.  * @uses $wpdb
  1940.  *
  1941.  * @param array $terms List of Term IDs
  1942.  * @param string $taxonomy Term Context
  1943.  * @return null Will break from function if conditions are not met.
  1944.  */
  1945. function _pad_term_counts(&$terms, $taxonomy) {
  1946.     global $wpdb;
  1947.  
  1948.     // This function only works for post categories.
  1949.     if ( 'category' != $taxonomy )
  1950.         return;
  1951.  
  1952.     $term_hier = _get_term_hierarchy($taxonomy);
  1953.  
  1954.     if ( empty($term_hier) )
  1955.         return;
  1956.  
  1957.     $term_items = array();
  1958.  
  1959.     foreach ( $terms as $key => $term ) {
  1960.         $terms_by_id[$term->term_id] = & $terms[$key];
  1961.         $term_ids[$term->term_taxonomy_id] = $term->term_id;
  1962.     }
  1963.  
  1964.     // Get the object and term ids and stick them in a lookup table
  1965.     $results = $wpdb->get_results("SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships INNER JOIN $wpdb->posts ON object_id = ID WHERE term_taxonomy_id IN (".join(',', array_keys($term_ids)).") AND post_type = 'post' AND post_status = 'publish'");
  1966.     foreach ( $results as $row ) {
  1967.         $id = $term_ids[$row->term_taxonomy_id];
  1968.         ++$term_items[$id][$row->object_id];
  1969.     }
  1970.  
  1971.     // Touch every ancestor's lookup row for each post in each term
  1972.     foreach ( $term_ids as $term_id ) {
  1973.         $child = $term_id;
  1974.         while ( $parent = $terms_by_id[$child]->parent ) {
  1975.             if ( !empty($term_items[$term_id]) )
  1976.                 foreach ( $term_items[$term_id] as $item_id => $touches )
  1977.                     ++$term_items[$parent][$item_id];
  1978.             $child = $parent;
  1979.         }
  1980.     }
  1981.  
  1982.     // Transfer the touched cells
  1983.     foreach ( (array) $term_items as $id => $items )
  1984.         if ( isset($terms_by_id[$id]) )
  1985.             $terms_by_id[$id]->count = count($items);
  1986. }
  1987.  
  1988. //
  1989. // Default callbacks
  1990. //
  1991.  
  1992. /**
  1993.  * Will update term count based on posts.
  1994.  *
  1995.  * Private function for the default callback for post_tag and category
  1996.  * taxonomies.
  1997.  *
  1998.  * @package WordPress
  1999.  * @subpackage Taxonomy
  2000.  * @access private
  2001.  * @since 2.3
  2002.  * @uses $wpdb
  2003.  *
  2004.  * @param array $terms List of Term IDs
  2005.  */
  2006. function _update_post_term_count( $terms ) {
  2007.     global $wpdb;
  2008.  
  2009.     foreach ( $terms as $term ) {
  2010.         $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type = 'post' AND term_taxonomy_id = %d", $term ) );
  2011.         $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
  2012.     }
  2013. }
  2014.  
  2015.  
  2016. /**
  2017.  * Generates a permalink for a taxonomy term archive.
  2018.  *
  2019.  * @since 2.6
  2020.  *
  2021.  * @param object|int|string $term
  2022.  * @param string $taxonomy
  2023.  * @return string HTML link to taxonomy term archive
  2024.  */
  2025. function get_term_link( $term, $taxonomy ) {
  2026.     global $wp_rewrite;
  2027.  
  2028.     // use legacy functions for core taxonomies until they are fully plugged in
  2029.     if ( $taxonomy == 'category' )
  2030.         return get_category_link($term);
  2031.     if ( $taxonomy == 'post_tag' )
  2032.         return get_tag_link($term);
  2033.  
  2034.     $termlink = $wp_rewrite->get_extra_permastruct($taxonomy);
  2035.  
  2036.     if ( !is_object($term) ) {
  2037.         if ( is_int($term) ) {
  2038.             $term = &get_term($term, $taxonomy);
  2039.         } else {
  2040.             $term = &get_term_by('slug', $term, $taxonomy);
  2041.         }
  2042.     }
  2043.     if ( is_wp_error( $term ) )
  2044.         return $term;
  2045.  
  2046.     $slug = $term->slug;
  2047.  
  2048.     if ( empty($termlink) ) {
  2049.         $file = get_option('home') . '/';
  2050.         $t = get_taxonomy($taxonomy);
  2051.         if ( $t->query_var )
  2052.             $termlink = "$file?$t->query_var=$slug";
  2053.         else
  2054.             $termlink = "$file?taxonomy=$taxonomy&term=$slug";
  2055.     } else {
  2056.         $termlink = str_replace("%$taxonomy%", $slug, $termlink);
  2057.         $termlink = get_option('home') . user_trailingslashit($termlink, 'category');
  2058.     }
  2059.     return apply_filters('term_link', $termlink, $term, $taxonomy);
  2060. }
  2061.  
  2062. /**
  2063.  * Display the taxonomies of a post with available options.
  2064.  *
  2065.  * This function can be used within the loop to display the taxonomies for a
  2066.  * post without specifying the Post ID. You can also use it outside the Loop to
  2067.  * display the taxonomies for a specific post.
  2068.  *
  2069.  * The available defaults are:
  2070.  * 'post' : default is 0. The post ID to get taxonomies of.
  2071.  * 'before' : default is empty string. Display before taxonomies list.
  2072.  * 'sep' : default is empty string. Separate every taxonomy with value in this.
  2073.  * 'after' : default is empty string. Display this after the taxonomies list.
  2074.  *
  2075.  * @since 2.6
  2076.  * @uses get_the_taxonomies()
  2077.  *
  2078.  * @param array $args Override the defaults.
  2079.  */
  2080. function the_taxonomies($args = array()) {
  2081.     $defaults = array(
  2082.         'post' => 0,
  2083.         'before' => '',
  2084.         'sep' => ' ',
  2085.         'after' => '',
  2086.     );
  2087.  
  2088.     $r = wp_parse_args( $args, $defaults );
  2089.     extract( $r, EXTR_SKIP );
  2090.  
  2091.     echo $before . join($sep, get_the_taxonomies($post)) . $after;
  2092. }
  2093.  
  2094. /**
  2095.  * Retrieve all taxonomies associated with a post.
  2096.  *
  2097.  * This function can be used within the loop. It will also return an array of
  2098.  * the taxonomies with links to the taxonomy and name.
  2099.  *
  2100.  * @since 2.6
  2101.  *
  2102.  * @param int $post Optional. Post ID or will use Global Post ID (in loop).
  2103.  * @return array
  2104.  */
  2105. function get_the_taxonomies($post = 0) {
  2106.     if ( is_int($post) )
  2107.         $post =& get_post($post);
  2108.     elseif ( !is_object($post) )
  2109.         $post =& $GLOBALS['post'];
  2110.  
  2111.     $taxonomies = array();
  2112.  
  2113.     if ( !$post )
  2114.         return $taxonomies;
  2115.  
  2116.     $template = apply_filters('taxonomy_template', '%s: %l.');
  2117.  
  2118.     foreach ( get_object_taxonomies($post) as $taxonomy ) {
  2119.         $t = (array) get_taxonomy($taxonomy);
  2120.         if ( empty($t['label']) )
  2121.             $t['label'] = $taxonomy;
  2122.         if ( empty($t['args']) )
  2123.             $t['args'] = array();
  2124.         if ( empty($t['template']) )
  2125.             $t['template'] = $template;
  2126.  
  2127.         $terms = get_object_term_cache($post->ID, $taxonomy);
  2128.         if ( empty($terms) )
  2129.             $terms = wp_get_object_terms($post->ID, $taxonomy, $t['args']);
  2130.  
  2131.         $links = array();
  2132.  
  2133.         foreach ( $terms as $term )
  2134.             $links[] = "<a href='" . attribute_escape(get_term_link($term, $taxonomy)) . "'>$term->name</a>";
  2135.  
  2136.         if ( $links )
  2137.             $taxonomies[$taxonomy] = wp_sprintf($t['template'], $t['label'], $links, $terms);
  2138.     }
  2139.     return $taxonomies;
  2140. }
  2141.  
  2142. /**
  2143.  * Retrieve all taxonomies of a post with just the names.
  2144.  *
  2145.  * @since 2.6
  2146.  * @uses get_object_taxonomies()
  2147.  *
  2148.  * @param int $post Optional. Post ID
  2149.  * @return array
  2150.  */
  2151. function get_post_taxonomies($post = 0) {
  2152.     $post =& get_post($post);
  2153.  
  2154.     return get_object_taxonomies($post);
  2155. }
  2156.  
  2157. ?>