1309 | | |
1310 | | /** |
1311 | | * Get all Term data from database by Term ID. |
1312 | | * |
1313 | | * The usage of the get_term function is to apply filters to a term object. It |
1314 | | * is possible to get a term object from the database before applying the |
1315 | | * filters. |
1316 | | * |
1317 | | * $term ID must be part of $taxonomy, to get from the database. Failure, might |
1318 | | * be able to be captured by the hooks. Failure would be the same value as $wpdb |
1319 | | * returns for the get_row method. |
1320 | | * |
1321 | | * There are two hooks, one is specifically for each term, named 'get_term', and |
1322 | | * the second is for the taxonomy name, 'term_$taxonomy'. Both hooks gets the |
1323 | | * term object, and the taxonomy name as parameters. Both hooks are expected to |
1324 | | * return a Term object. |
1325 | | * |
1326 | | * {@see 'get_term'} hook - Takes two parameters the term Object and the taxonomy name. |
1327 | | * Must return term object. Used in get_term() as a catch-all filter for every |
1328 | | * $term. |
1329 | | * |
1330 | | * {@see 'get_$taxonomy'} hook - Takes two parameters the term Object and the taxonomy |
1331 | | * name. Must return term object. $taxonomy will be the taxonomy name, so for |
1332 | | * example, if 'category', it would be 'get_category' as the filter name. Useful |
1333 | | * for custom taxonomies or plugging into default taxonomies. |
1334 | | * |
1335 | | * @todo Better formatting for DocBlock |
1336 | | * |
1337 | | * @since 2.3.0 |
1338 | | * |
1339 | | * @global wpdb $wpdb WordPress database abstraction object. |
1340 | | * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param. |
1341 | | * |
1342 | | * @param int|object $term If integer, will get from database. If object will apply filters and return $term. |
1343 | | * @param string $taxonomy Taxonomy name that $term is part of. |
1344 | | * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N |
1345 | | * @param string $filter Optional, default is raw or no WordPress defined filter will applied. |
1346 | | * @return object|array|null|WP_Error Term Row from database. Will return null if $term is empty. If taxonomy does not |
1347 | | * exist then WP_Error will be returned. |
1348 | | */ |
1349 | | function get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') { |
1350 | | global $wpdb; |
1351 | | |
1352 | | if ( empty( $term ) ) { |
1353 | | return new WP_Error( 'invalid_term', __( 'Empty Term' ) ); |
1354 | | } |
1355 | | |
1356 | | if ( ! taxonomy_exists( $taxonomy ) ) { |
1357 | | return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) ); |
1358 | | } |
1359 | | |
1360 | | if ( is_object($term) && empty($term->filter) ) { |
1361 | | wp_cache_add( $term->term_id, $term, $taxonomy ); |
1362 | | $_term = $term; |
1363 | | } else { |
1364 | | if ( is_object($term) ) |
1365 | | $term = $term->term_id; |
1366 | | if ( !$term = (int) $term ) |
1367 | | return null; |
1368 | | if ( ! $_term = wp_cache_get( $term, $taxonomy ) ) { |
1369 | | $_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 = %d LIMIT 1", $taxonomy, $term) ); |
1370 | | if ( ! $_term ) |
1371 | | return null; |
1372 | | wp_cache_add( $term, $_term, $taxonomy ); |
1373 | | } |
1374 | | } |
1375 | | |
1376 | | /** |
1377 | | * Filter a term. |
1378 | | * |
1379 | | * @since 2.3.0 |
1380 | | * |
1381 | | * @param int|object $_term Term object or ID. |
1382 | | * @param string $taxonomy The taxonomy slug. |
1383 | | */ |
1384 | | $_term = apply_filters( 'get_term', $_term, $taxonomy ); |
1385 | | |
1386 | | /** |
1387 | | * Filter a taxonomy. |
1388 | | * |
1389 | | * The dynamic portion of the filter name, `$taxonomy`, refers |
1390 | | * to the taxonomy slug. |
1391 | | * |
1392 | | * @since 2.3.0 |
1393 | | * |
1394 | | * @param int|object $_term Term object or ID. |
1395 | | * @param string $taxonomy The taxonomy slug. |
1396 | | */ |
1397 | | $_term = apply_filters( "get_$taxonomy", $_term, $taxonomy ); |
1398 | | $_term = sanitize_term($_term, $taxonomy, $filter); |
1399 | | |
1400 | | if ( $output == OBJECT ) { |
1401 | | return $_term; |
1402 | | } elseif ( $output == ARRAY_A ) { |
1403 | | $__term = get_object_vars($_term); |
1404 | | return $__term; |
1405 | | } elseif ( $output == ARRAY_N ) { |
1406 | | $__term = array_values(get_object_vars($_term)); |
1407 | | return $__term; |
1408 | | } else { |
1409 | | return $_term; |
1410 | | } |
1411 | | } |
1412 | | |
1413 | | /** |
1414 | | * Get all Term data from database by Term field and data. |
1415 | | * |
1416 | | * Warning: $value is not escaped for 'name' $field. You must do it yourself, if |
1417 | | * required. |
1418 | | * |
1419 | | * The default $field is 'id', therefore it is possible to also use null for |
1420 | | * field, but not recommended that you do so. |
1421 | | * |
1422 | | * If $value does not exist, the return value will be false. If $taxonomy exists |
1423 | | * and $field and $value combinations exist, the Term will be returned. |
1424 | | * |
1425 | | * @todo Better formatting for DocBlock. |
1426 | | * |
1427 | | * @since 2.3.0 |
1428 | | * |
1429 | | * @global wpdb $wpdb WordPress database abstraction object. |
1430 | | * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param. |
1431 | | * |
1432 | | * @param string $field Either 'slug', 'name', 'id' (term_id), or 'term_taxonomy_id' |
1433 | | * @param string|int $value Search for this term value |
1434 | | * @param string $taxonomy Taxonomy Name |
1435 | | * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N |
1436 | | * @param string $filter Optional, default is raw or no WordPress defined filter will applied. |
1437 | | * @return object|array|null|WP_Error|false Term Row from database. |
1438 | | * Will return false if $taxonomy does not exist or $term was not found. |
1439 | | */ |
1440 | | function get_term_by($field, $value, $taxonomy, $output = OBJECT, $filter = 'raw') { |
1441 | | global $wpdb; |
1442 | | |
1443 | | if ( ! taxonomy_exists($taxonomy) ) |
1444 | | return false; |
1445 | | |
1446 | | if ( 'slug' == $field ) { |
1447 | | $field = 't.slug'; |
1448 | | $value = sanitize_title($value); |
1449 | | if ( empty($value) ) |
1450 | | return false; |
1451 | | } elseif ( 'name' == $field ) { |
1452 | | // Assume already escaped |
1453 | | $value = wp_unslash($value); |
1454 | | $field = 't.name'; |
1455 | | } elseif ( 'term_taxonomy_id' == $field ) { |
1456 | | $value = (int) $value; |
1457 | | $field = 'tt.term_taxonomy_id'; |
1458 | | } else { |
1459 | | $term = get_term( (int) $value, $taxonomy, $output, $filter ); |
1460 | | if ( is_wp_error( $term ) ) |
1461 | | $term = false; |
1462 | | return $term; |
1463 | | } |
1464 | | |
1465 | | $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 ) ); |
1466 | | if ( ! $term ) |
1467 | | return false; |
1468 | | |
1469 | | wp_cache_add( $term->term_id, $term, $taxonomy ); |
1470 | | |
1471 | | /** This filter is documented in wp-includes/taxonomy.php */ |
1472 | | $term = apply_filters( 'get_term', $term, $taxonomy ); |
1473 | | |
1474 | | /** This filter is documented in wp-includes/taxonomy.php */ |
1475 | | $term = apply_filters( "get_$taxonomy", $term, $taxonomy ); |
1476 | | |
1477 | | $term = sanitize_term($term, $taxonomy, $filter); |
1478 | | |
1479 | | if ( $output == OBJECT ) { |
1480 | | return $term; |
1481 | | } elseif ( $output == ARRAY_A ) { |
1482 | | return get_object_vars($term); |
1483 | | } elseif ( $output == ARRAY_N ) { |
1484 | | return array_values(get_object_vars($term)); |
1485 | | } else { |
1486 | | return $term; |
1487 | | } |
1488 | | } |
1489 | | |
1490 | | /** |
1491 | | * Merge all term children into a single array of their IDs. |
1492 | | * |
1493 | | * This recursive function will merge all of the children of $term into the same |
1494 | | * array of term IDs. Only useful for taxonomies which are hierarchical. |
1495 | | * |
1496 | | * Will return an empty array if $term does not exist in $taxonomy. |
1497 | | * |
1498 | | * @since 2.3.0 |
1499 | | * |
1500 | | * @param string $term_id ID of Term to get children. |
1501 | | * @param string $taxonomy Taxonomy Name. |
1502 | | * @return array|WP_Error List of Term IDs. WP_Error returned if `$taxonomy` does not exist. |
1503 | | */ |
1504 | | function get_term_children( $term_id, $taxonomy ) { |
1505 | | if ( ! taxonomy_exists($taxonomy) ) |
1506 | | return new WP_Error('invalid_taxonomy', __('Invalid taxonomy')); |
1507 | | |
1508 | | $term_id = intval( $term_id ); |
1509 | | |
1510 | | $terms = _get_term_hierarchy($taxonomy); |
1511 | | |
1512 | | if ( ! isset($terms[$term_id]) ) |
1513 | | return array(); |
1514 | | |
1515 | | $children = $terms[$term_id]; |
1516 | | |
1517 | | foreach ( (array) $terms[$term_id] as $child ) { |
1518 | | if ( $term_id == $child ) { |
1519 | | continue; |
1520 | | } |
1521 | | |
1522 | | if ( isset($terms[$child]) ) |
1523 | | $children = array_merge($children, get_term_children($child, $taxonomy)); |
1524 | | } |
1525 | | |
1526 | | return $children; |
1527 | | } |
1528 | | |
1529 | | /** |
1530 | | * Get sanitized Term field. |
1531 | | * |
1532 | | * Does checks for $term, based on the $taxonomy. The function is for contextual |
1533 | | * reasons and for simplicity of usage. See sanitize_term_field() for more |
1534 | | * information. |
1535 | | * |
1536 | | * @since 2.3.0 |
1537 | | * |
1538 | | * @param string $field Term field to fetch. |
1539 | | * @param int $term Term ID. |
1540 | | * @param string $taxonomy Taxonomy Name. |
1541 | | * @param string $context Optional, default is display. Look at sanitize_term_field() for available options. |
1542 | | * @return string|int|null|WP_Error Will return an empty string if $term is not an object or if $field is not set in $term. |
1543 | | */ |
1544 | | function get_term_field( $field, $term, $taxonomy, $context = 'display' ) { |
1545 | | $term = (int) $term; |
1546 | | $term = get_term( $term, $taxonomy ); |
1547 | | if ( is_wp_error($term) ) |
1548 | | return $term; |
1549 | | |
1550 | | if ( !is_object($term) ) |
1551 | | return ''; |
1552 | | |
1553 | | if ( !isset($term->$field) ) |
1554 | | return ''; |
1555 | | |
1556 | | return sanitize_term_field($field, $term->$field, $term->term_id, $taxonomy, $context); |
1557 | | } |
1558 | | |
1559 | | /** |
1560 | | * Sanitizes Term for editing. |
1561 | | * |
1562 | | * Return value is sanitize_term() and usage is for sanitizing the term for |
1563 | | * editing. Function is for contextual and simplicity. |
1564 | | * |
1565 | | * @since 2.3.0 |
1566 | | * |
1567 | | * @param int|object $id Term ID or object. |
1568 | | * @param string $taxonomy Taxonomy name. |
1569 | | * @return string|int|null|WP_Error Will return empty string if $term is not an object. |
1570 | | */ |
1571 | | function get_term_to_edit( $id, $taxonomy ) { |
1572 | | $term = get_term( $id, $taxonomy ); |
1573 | | |
1574 | | if ( is_wp_error($term) ) |
1575 | | return $term; |
1576 | | |
1577 | | if ( !is_object($term) ) |
1578 | | return ''; |
1579 | | |
1580 | | return sanitize_term($term, $taxonomy, 'edit'); |
1581 | | } |
1582 | | |
1583 | | /** |
1584 | | * Retrieve the terms in a given taxonomy or list of taxonomies. |
1585 | | * |
1586 | | * You can fully inject any customizations to the query before it is sent, as |
1587 | | * well as control the output with a filter. |
1588 | | * |
1589 | | * The {@see 'get_terms'} filter will be called when the cache has the term and will |
1590 | | * pass the found term along with the array of $taxonomies and array of $args. |
1591 | | * This filter is also called before the array of terms is passed and will pass |
1592 | | * the array of terms, along with the $taxonomies and $args. |
1593 | | * |
1594 | | * The {@see 'list_terms_exclusions'} filter passes the compiled exclusions along with |
1595 | | * the $args. |
1596 | | * |
1597 | | * The {@see 'get_terms_orderby'} filter passes the `ORDER BY` clause for the query |
1598 | | * along with the $args array. |
1599 | | * |
1600 | | * @since 2.3.0 |
1601 | | * @since 4.2.0 Introduced 'name' and 'childless' parameters. |
1602 | | * |
1603 | | * @global wpdb $wpdb WordPress database abstraction object. |
1604 | | * @global array $wp_filter |
1605 | | * |
1606 | | * @param string|array $taxonomies Taxonomy name or list of Taxonomy names. |
1607 | | * @param array|string $args { |
1608 | | * Optional. Array or string of arguments to get terms. |
1609 | | * |
1610 | | * @type string $orderby Field(s) to order terms by. Accepts term fields ('name', 'slug', |
1611 | | * 'term_group', 'term_id', 'id', 'description'), 'count' for term |
1612 | | * taxonomy count, 'include' to match the 'order' of the $include param, |
1613 | | * or 'none' to skip ORDER BY. Defaults to 'name'. |
1614 | | * @type string $order Whether to order terms in ascending or descending order. |
1615 | | * Accepts 'ASC' (ascending) or 'DESC' (descending). |
1616 | | * Default 'ASC'. |
1617 | | * @type bool|int $hide_empty Whether to hide terms not assigned to any posts. Accepts |
1618 | | * 1|true or 0|false. Default 1|true. |
1619 | | * @type array|string $include Array or comma/space-separated string of term ids to include. |
1620 | | * Default empty array. |
1621 | | * @type array|string $exclude Array or comma/space-separated string of term ids to exclude. |
1622 | | * If $include is non-empty, $exclude is ignored. |
1623 | | * Default empty array. |
1624 | | * @type array|string $exclude_tree Array or comma/space-separated string of term ids to exclude |
1625 | | * along with all of their descendant terms. If $include is |
1626 | | * non-empty, $exclude_tree is ignored. Default empty array. |
1627 | | * @type int|string $number Maximum number of terms to return. Accepts ''|0 (all) or any |
1628 | | * positive number. Default ''|0 (all). |
1629 | | * @type int $offset The number by which to offset the terms query. Default empty. |
1630 | | * @type string $fields Term fields to query for. Accepts 'all' (returns an array of complete |
1631 | | * term objects), 'ids' (returns an array of ids), 'id=>parent' (returns |
1632 | | * an associative array with ids as keys, parent term IDs as values), |
1633 | | * 'names' (returns an array of term names), 'count' (returns the number |
1634 | | * of matching terms), 'id=>name' (returns an associative array with ids |
1635 | | * as keys, term names as values), or 'id=>slug' (returns an associative |
1636 | | * array with ids as keys, term slugs as values). Default 'all'. |
1637 | | * @type string|array $name Optional. Name or array of names to return term(s) for. Default empty. |
1638 | | * @type string|array $slug Optional. Slug or array of slugs to return term(s) for. Default empty. |
1639 | | * @type bool $hierarchical Whether to include terms that have non-empty descendants (even |
1640 | | * if $hide_empty is set to true). Default true. |
1641 | | * @type string $search Search criteria to match terms. Will be SQL-formatted with |
1642 | | * wildcards before and after. Default empty. |
1643 | | * @type string $name__like Retrieve terms with criteria by which a term is LIKE $name__like. |
1644 | | * Default empty. |
1645 | | * @type string $description__like Retrieve terms where the description is LIKE $description__like. |
1646 | | * Default empty. |
1647 | | * @type bool $pad_counts Whether to pad the quantity of a term's children in the quantity |
1648 | | * of each term's "count" object variable. Default false. |
1649 | | * @type string $get Whether to return terms regardless of ancestry or whether the terms |
1650 | | * are empty. Accepts 'all' or empty (disabled). Default empty. |
1651 | | * @type int $child_of Term ID to retrieve child terms of. If multiple taxonomies |
1652 | | * are passed, $child_of is ignored. Default 0. |
1653 | | * @type int|string $parent Parent term ID to retrieve direct-child terms of. Default empty. |
1654 | | * @type bool $childless True to limit results to terms that have no children. This parameter has |
1655 | | * no effect on non-hierarchical taxonomies. Default false. |
1656 | | * @type string $cache_domain Unique cache key to be produced when this query is stored in an |
1657 | | * object cache. Default is 'core'. |
1658 | | * } |
1659 | | * @return array|int|WP_Error List of Term Objects and their children. Will return WP_Error, if any of $taxonomies |
1660 | | * do not exist. |
1661 | | */ |
1662 | | function get_terms( $taxonomies, $args = '' ) { |
1663 | | global $wpdb; |
1664 | | $empty_array = array(); |
1665 | | |
1666 | | $single_taxonomy = ! is_array( $taxonomies ) || 1 === count( $taxonomies ); |
1667 | | if ( ! is_array( $taxonomies ) ) { |
1668 | | $taxonomies = array( $taxonomies ); |
1669 | | } |
1670 | | |
1671 | | foreach ( $taxonomies as $taxonomy ) { |
1672 | | if ( ! taxonomy_exists($taxonomy) ) { |
1673 | | return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) ); |
1674 | | } |
1675 | | } |
1676 | | |
1677 | | $defaults = array('orderby' => 'name', 'order' => 'ASC', |
1678 | | 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), |
1679 | | 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, |
1680 | | 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', |
1681 | | 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core' ); |
1682 | | $args = wp_parse_args( $args, $defaults ); |
1683 | | $args['number'] = absint( $args['number'] ); |
1684 | | $args['offset'] = absint( $args['offset'] ); |
1685 | | |
1686 | | // Save queries by not crawling the tree in the case of multiple taxes or a flat tax. |
1687 | | $has_hierarchical_tax = false; |
1688 | | foreach ( $taxonomies as $_tax ) { |
1689 | | if ( is_taxonomy_hierarchical( $_tax ) ) { |
1690 | | $has_hierarchical_tax = true; |
1691 | | } |
1692 | | } |
1693 | | |
1694 | | if ( ! $has_hierarchical_tax ) { |
1695 | | $args['hierarchical'] = false; |
1696 | | $args['pad_counts'] = false; |
1697 | | } |
1698 | | |
1699 | | // 'parent' overrides 'child_of'. |
1700 | | if ( 0 < intval( $args['parent'] ) ) { |
1701 | | $args['child_of'] = false; |
1702 | | } |
1703 | | |
1704 | | if ( 'all' == $args['get'] ) { |
1705 | | $args['childless'] = false; |
1706 | | $args['child_of'] = 0; |
1707 | | $args['hide_empty'] = 0; |
1708 | | $args['hierarchical'] = false; |
1709 | | $args['pad_counts'] = false; |
1710 | | } |
1711 | | |
1712 | | /** |
1713 | | * Filter the terms query arguments. |
1714 | | * |
1715 | | * @since 3.1.0 |
1716 | | * |
1717 | | * @param array $args An array of get_term() arguments. |
1718 | | * @param array $taxonomies An array of taxonomies. |
1719 | | */ |
1720 | | $args = apply_filters( 'get_terms_args', $args, $taxonomies ); |
1721 | | |
1722 | | // Avoid the query if the queried parent/child_of term has no descendants. |
1723 | | $child_of = $args['child_of']; |
1724 | | $parent = $args['parent']; |
1725 | | |
1726 | | if ( $child_of ) { |
1727 | | $_parent = $child_of; |
1728 | | } elseif ( $parent ) { |
1729 | | $_parent = $parent; |
1730 | | } else { |
1731 | | $_parent = false; |
1732 | | } |
1733 | | |
1734 | | if ( $_parent ) { |
1735 | | $in_hierarchy = false; |
1736 | | foreach ( $taxonomies as $_tax ) { |
1737 | | $hierarchy = _get_term_hierarchy( $_tax ); |
1738 | | |
1739 | | if ( isset( $hierarchy[ $_parent ] ) ) { |
1740 | | $in_hierarchy = true; |
1741 | | } |
1742 | | } |
1743 | | |
1744 | | if ( ! $in_hierarchy ) { |
1745 | | return $empty_array; |
1746 | | } |
1747 | | } |
1748 | | |
1749 | | // $args can be whatever, only use the args defined in defaults to compute the key. |
1750 | | $filter_key = ( has_filter('list_terms_exclusions') ) ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : ''; |
1751 | | $key = md5( serialize( wp_array_slice_assoc( $args, array_keys( $defaults ) ) ) . serialize( $taxonomies ) . $filter_key ); |
1752 | | $last_changed = wp_cache_get( 'last_changed', 'terms' ); |
1753 | | if ( ! $last_changed ) { |
1754 | | $last_changed = microtime(); |
1755 | | wp_cache_set( 'last_changed', $last_changed, 'terms' ); |
1756 | | } |
1757 | | $cache_key = "get_terms:$key:$last_changed"; |
1758 | | $cache = wp_cache_get( $cache_key, 'terms' ); |
1759 | | if ( false !== $cache ) { |
1760 | | |
1761 | | /** |
1762 | | * Filter the given taxonomy's terms cache. |
1763 | | * |
1764 | | * @since 2.3.0 |
1765 | | * |
1766 | | * @param array $cache Cached array of terms for the given taxonomy. |
1767 | | * @param array $taxonomies An array of taxonomies. |
1768 | | * @param array $args An array of get_terms() arguments. |
1769 | | */ |
1770 | | return apply_filters( 'get_terms', $cache, $taxonomies, $args ); |
1771 | | } |
1772 | | |
1773 | | $_orderby = strtolower( $args['orderby'] ); |
1774 | | if ( 'count' == $_orderby ) { |
1775 | | $orderby = 'tt.count'; |
1776 | | } elseif ( 'name' == $_orderby ) { |
1777 | | $orderby = 't.name'; |
1778 | | } elseif ( 'slug' == $_orderby ) { |
1779 | | $orderby = 't.slug'; |
1780 | | } elseif ( 'include' == $_orderby && ! empty( $args['include'] ) ) { |
1781 | | $include = implode( ',', array_map( 'absint', $args['include'] ) ); |
1782 | | $orderby = "FIELD( t.term_id, $include )"; |
1783 | | } elseif ( 'term_group' == $_orderby ) { |
1784 | | $orderby = 't.term_group'; |
1785 | | } elseif ( 'description' == $_orderby ) { |
1786 | | $orderby = 'tt.description'; |
1787 | | } elseif ( 'none' == $_orderby ) { |
1788 | | $orderby = ''; |
1789 | | } elseif ( empty($_orderby) || 'id' == $_orderby ) { |
1790 | | $orderby = 't.term_id'; |
1791 | | } else { |
1792 | | $orderby = 't.name'; |
1793 | | } |
1794 | | |
1795 | | /** |
1796 | | * Filter the ORDERBY clause of the terms query. |
1797 | | * |
1798 | | * @since 2.8.0 |
1799 | | * |
1800 | | * @param string $orderby `ORDERBY` clause of the terms query. |
1801 | | * @param array $args An array of terms query arguments. |
1802 | | * @param array $taxonomies An array of taxonomies. |
1803 | | */ |
1804 | | $orderby = apply_filters( 'get_terms_orderby', $orderby, $args, $taxonomies ); |
1805 | | |
1806 | | $order = strtoupper( $args['order'] ); |
1807 | | if ( ! empty( $orderby ) ) { |
1808 | | $orderby = "ORDER BY $orderby"; |
1809 | | } else { |
1810 | | $order = ''; |
1811 | | } |
1812 | | |
1813 | | if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ) ) ) { |
1814 | | $order = 'ASC'; |
1815 | | } |
1816 | | |
1817 | | $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')"; |
1818 | | |
1819 | | $exclude = $args['exclude']; |
1820 | | $exclude_tree = $args['exclude_tree']; |
1821 | | $include = $args['include']; |
1822 | | |
1823 | | $inclusions = ''; |
1824 | | if ( ! empty( $include ) ) { |
1825 | | $exclude = ''; |
1826 | | $exclude_tree = ''; |
1827 | | $inclusions = implode( ',', wp_parse_id_list( $include ) ); |
1828 | | } |
1829 | | |
1830 | | if ( ! empty( $inclusions ) ) { |
1831 | | $inclusions = ' AND t.term_id IN ( ' . $inclusions . ' )'; |
1832 | | $where .= $inclusions; |
1833 | | } |
1834 | | |
1835 | | $exclusions = array(); |
1836 | | if ( ! empty( $exclude_tree ) ) { |
1837 | | $exclude_tree = wp_parse_id_list( $exclude_tree ); |
1838 | | $excluded_children = $exclude_tree; |
1839 | | foreach ( $exclude_tree as $extrunk ) { |
1840 | | $excluded_children = array_merge( |
1841 | | $excluded_children, |
1842 | | (array) get_terms( $taxonomies[0], array( 'child_of' => intval( $extrunk ), 'fields' => 'ids', 'hide_empty' => 0 ) ) |
1843 | | ); |
1844 | | } |
1845 | | $exclusions = array_merge( $excluded_children, $exclusions ); |
1846 | | } |
1847 | | |
1848 | | if ( ! empty( $exclude ) ) { |
1849 | | $exclusions = array_merge( wp_parse_id_list( $exclude ), $exclusions ); |
1850 | | } |
1851 | | |
1852 | | // 'childless' terms are those without an entry in the flattened term hierarchy. |
1853 | | $childless = (bool) $args['childless']; |
1854 | | if ( $childless ) { |
1855 | | foreach ( $taxonomies as $_tax ) { |
1856 | | $term_hierarchy = _get_term_hierarchy( $_tax ); |
1857 | | $exclusions = array_merge( array_keys( $term_hierarchy ), $exclusions ); |
1858 | | } |
1859 | | } |
1860 | | |
1861 | | if ( ! empty( $exclusions ) ) { |
1862 | | $exclusions = ' AND t.term_id NOT IN (' . implode( ',', array_map( 'intval', $exclusions ) ) . ')'; |
1863 | | } else { |
1864 | | $exclusions = ''; |
1865 | | } |
1866 | | |
1867 | | /** |
1868 | | * Filter the terms to exclude from the terms query. |
1869 | | * |
1870 | | * @since 2.3.0 |
1871 | | * |
1872 | | * @param string $exclusions `NOT IN` clause of the terms query. |
1873 | | * @param array $args An array of terms query arguments. |
1874 | | * @param array $taxonomies An array of taxonomies. |
1875 | | */ |
1876 | | $exclusions = apply_filters( 'list_terms_exclusions', $exclusions, $args, $taxonomies ); |
1877 | | |
1878 | | if ( ! empty( $exclusions ) ) { |
1879 | | $where .= $exclusions; |
1880 | | } |
1881 | | |
1882 | | if ( ! empty( $args['name'] ) ) { |
1883 | | $names = (array) $args['name']; |
1884 | | foreach ( $names as &$_name ) { |
1885 | | $_name = sanitize_term_field( 'name', $_name, 0, reset( $taxonomies ), 'db' ); |
1886 | | } |
1887 | | |
1888 | | $where .= " AND t.name IN ('" . implode( "', '", array_map( 'esc_sql', $names ) ) . "')"; |
1889 | | } |
1890 | | |
1891 | | if ( ! empty( $args['slug'] ) ) { |
1892 | | if ( is_array( $args['slug'] ) ) { |
1893 | | $slug = array_map( 'sanitize_title', $args['slug'] ); |
1894 | | $where .= " AND t.slug IN ('" . implode( "', '", $slug ) . "')"; |
1895 | | } else { |
1896 | | $slug = sanitize_title( $args['slug'] ); |
1897 | | $where .= " AND t.slug = '$slug'"; |
1898 | | } |
1899 | | } |
1900 | | |
1901 | | if ( ! empty( $args['name__like'] ) ) { |
1902 | | $where .= $wpdb->prepare( " AND t.name LIKE %s", '%' . $wpdb->esc_like( $args['name__like'] ) . '%' ); |
1903 | | } |
1904 | | |
1905 | | if ( ! empty( $args['description__like'] ) ) { |
1906 | | $where .= $wpdb->prepare( " AND tt.description LIKE %s", '%' . $wpdb->esc_like( $args['description__like'] ) . '%' ); |
1907 | | } |
1908 | | |
1909 | | if ( '' !== $parent ) { |
1910 | | $parent = (int) $parent; |
1911 | | $where .= " AND tt.parent = '$parent'"; |
1912 | | } |
1913 | | |
1914 | | $hierarchical = $args['hierarchical']; |
1915 | | if ( 'count' == $args['fields'] ) { |
1916 | | $hierarchical = false; |
1917 | | } |
1918 | | if ( $args['hide_empty'] && !$hierarchical ) { |
1919 | | $where .= ' AND tt.count > 0'; |
1920 | | } |
1921 | | |
1922 | | $number = $args['number']; |
1923 | | $offset = $args['offset']; |
1924 | | |
1925 | | // Don't limit the query results when we have to descend the family tree. |
1926 | | if ( $number && ! $hierarchical && ! $child_of && '' === $parent ) { |
1927 | | if ( $offset ) { |
1928 | | $limits = 'LIMIT ' . $offset . ',' . $number; |
1929 | | } else { |
1930 | | $limits = 'LIMIT ' . $number; |
1931 | | } |
1932 | | } else { |
1933 | | $limits = ''; |
1934 | | } |
1935 | | |
1936 | | if ( ! empty( $args['search'] ) ) { |
1937 | | $like = '%' . $wpdb->esc_like( $args['search'] ) . '%'; |
1938 | | $where .= $wpdb->prepare( ' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', $like, $like ); |
1939 | | } |
1940 | | |
1941 | | $selects = array(); |
1942 | | switch ( $args['fields'] ) { |
1943 | | case 'all': |
1944 | | $selects = array( 't.*', 'tt.*' ); |
1945 | | break; |
1946 | | case 'ids': |
1947 | | case 'id=>parent': |
1948 | | $selects = array( 't.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy' ); |
1949 | | break; |
1950 | | case 'names': |
1951 | | $selects = array( 't.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy' ); |
1952 | | break; |
1953 | | case 'count': |
1954 | | $orderby = ''; |
1955 | | $order = ''; |
1956 | | $selects = array( 'COUNT(*)' ); |
1957 | | break; |
1958 | | case 'id=>name': |
1959 | | $selects = array( 't.term_id', 't.name', 'tt.count', 'tt.taxonomy' ); |
1960 | | break; |
1961 | | case 'id=>slug': |
1962 | | $selects = array( 't.term_id', 't.slug', 'tt.count', 'tt.taxonomy' ); |
1963 | | break; |
1964 | | } |
1965 | | |
1966 | | $_fields = $args['fields']; |
1967 | | |
1968 | | /** |
1969 | | * Filter the fields to select in the terms query. |
1970 | | * |
1971 | | * Field lists modified using this filter will only modify the term fields returned |
1972 | | * by the function when the `$fields` parameter set to 'count' or 'all'. In all other |
1973 | | * cases, the term fields in the results array will be determined by the `$fields` |
1974 | | * parameter alone. |
1975 | | * |
1976 | | * Use of this filter can result in unpredictable behavior, and is not recommended. |
1977 | | * |
1978 | | * @since 2.8.0 |
1979 | | * |
1980 | | * @param array $selects An array of fields to select for the terms query. |
1981 | | * @param array $args An array of term query arguments. |
1982 | | * @param array $taxonomies An array of taxonomies. |
1983 | | */ |
1984 | | $fields = implode( ', ', apply_filters( 'get_terms_fields', $selects, $args, $taxonomies ) ); |
1985 | | |
1986 | | $join = "INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id"; |
1987 | | |
1988 | | $pieces = array( 'fields', 'join', 'where', 'orderby', 'order', 'limits' ); |
1989 | | |
1990 | | /** |
1991 | | * Filter the terms query SQL clauses. |
1992 | | * |
1993 | | * @since 3.1.0 |
1994 | | * |
1995 | | * @param array $pieces Terms query SQL clauses. |
1996 | | * @param array $taxonomies An array of taxonomies. |
1997 | | * @param array $args An array of terms query arguments. |
1998 | | */ |
1999 | | $clauses = apply_filters( 'terms_clauses', compact( $pieces ), $taxonomies, $args ); |
2000 | | |
2001 | | $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : ''; |
2002 | | $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : ''; |
2003 | | $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : ''; |
2004 | | $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : ''; |
2005 | | $order = isset( $clauses[ 'order' ] ) ? $clauses[ 'order' ] : ''; |
2006 | | $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : ''; |
2007 | | |
2008 | | $query = "SELECT $fields FROM $wpdb->terms AS t $join WHERE $where $orderby $order $limits"; |
2009 | | |
2010 | | if ( 'count' == $_fields ) { |
2011 | | return $wpdb->get_var( $query ); |
2012 | | } |
2013 | | |
2014 | | $terms = $wpdb->get_results($query); |
2015 | | if ( 'all' == $_fields ) { |
2016 | | update_term_cache( $terms ); |
2017 | | } |
2018 | | |
2019 | | if ( empty($terms) ) { |
2020 | | wp_cache_add( $cache_key, array(), 'terms', DAY_IN_SECONDS ); |
2021 | | |
2022 | | /** This filter is documented in wp-includes/taxonomy.php */ |
2023 | | return apply_filters( 'get_terms', array(), $taxonomies, $args ); |
2024 | | } |
2025 | | |
2026 | | if ( $child_of ) { |
2027 | | foreach ( $taxonomies as $_tax ) { |
2028 | | $children = _get_term_hierarchy( $_tax ); |
2029 | | if ( ! empty( $children ) ) { |
2030 | | $terms = _get_term_children( $child_of, $terms, $_tax ); |
2031 | | } |
2032 | | } |
2033 | | } |
2034 | | |
2035 | | // Update term counts to include children. |
2036 | | if ( $args['pad_counts'] && 'all' == $_fields ) { |
2037 | | foreach ( $taxonomies as $_tax ) { |
2038 | | _pad_term_counts( $terms, $_tax ); |
2039 | | } |
2040 | | } |
2041 | | |
2042 | | // Make sure we show empty categories that have children. |
2043 | | if ( $hierarchical && $args['hide_empty'] && is_array( $terms ) ) { |
2044 | | foreach ( $terms as $k => $term ) { |
2045 | | if ( ! $term->count ) { |
2046 | | $children = get_term_children( $term->term_id, $term->taxonomy ); |
2047 | | if ( is_array( $children ) ) { |
2048 | | foreach ( $children as $child_id ) { |
2049 | | $child = get_term( $child_id, $term->taxonomy ); |
2050 | | if ( $child->count ) { |
2051 | | continue 2; |
2052 | | } |
2053 | | } |
2054 | | } |
2055 | | |
2056 | | // It really is empty. |
2057 | | unset($terms[$k]); |
2058 | | } |
2059 | | } |
2060 | | } |
2061 | | |
2062 | | $_terms = array(); |
2063 | | if ( 'id=>parent' == $_fields ) { |
2064 | | foreach ( $terms as $term ) { |
2065 | | $_terms[ $term->term_id ] = $term->parent; |
2066 | | } |
2067 | | } elseif ( 'ids' == $_fields ) { |
2068 | | foreach ( $terms as $term ) { |
2069 | | $_terms[] = $term->term_id; |
2070 | | } |
2071 | | } elseif ( 'names' == $_fields ) { |
2072 | | foreach ( $terms as $term ) { |
2073 | | $_terms[] = $term->name; |
2074 | | } |
2075 | | } elseif ( 'id=>name' == $_fields ) { |
2076 | | foreach ( $terms as $term ) { |
2077 | | $_terms[ $term->term_id ] = $term->name; |
2078 | | } |
2079 | | } elseif ( 'id=>slug' == $_fields ) { |
2080 | | foreach ( $terms as $term ) { |
2081 | | $_terms[ $term->term_id ] = $term->slug; |
2082 | | } |
2083 | | } |
2084 | | |
2085 | | if ( ! empty( $_terms ) ) { |
2086 | | $terms = $_terms; |
2087 | | } |
2088 | | |
2089 | | if ( $number && is_array( $terms ) && count( $terms ) > $number ) { |
2090 | | $terms = array_slice( $terms, $offset, $number ); |
2091 | | } |
2092 | | |
2093 | | wp_cache_add( $cache_key, $terms, 'terms', DAY_IN_SECONDS ); |
2094 | | |
2095 | | /** This filter is documented in wp-includes/taxonomy */ |
2096 | | return apply_filters( 'get_terms', $terms, $taxonomies, $args ); |
2097 | | } |
2098 | | |
2099 | | /** |
2100 | | * Check if Term exists. |
2101 | | * |
2102 | | * Formerly is_term(), introduced in 2.3.0. |
2103 | | * |
2104 | | * @since 3.0.0 |
2105 | | * |
2106 | | * @global wpdb $wpdb WordPress database abstraction object. |
2107 | | * |
2108 | | * @param int|string $term The term to check |
2109 | | * @param string $taxonomy The taxonomy name to use |
2110 | | * @param int $parent Optional. ID of parent term under which to confine the exists search. |
2111 | | * @return mixed Returns null if the term does not exist. Returns the term ID |
2112 | | * if no taxonomy is specified and the term ID exists. Returns |
2113 | | * an array of the term ID and the term taxonomy ID the taxonomy |
2114 | | * is specified and the pairing exists. |
2115 | | */ |
2116 | | function term_exists( $term, $taxonomy = '', $parent = null ) { |
2117 | | global $wpdb; |
2118 | | |
2119 | | $select = "SELECT term_id FROM $wpdb->terms as t WHERE "; |
2120 | | $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 "; |
2121 | | |
2122 | | if ( is_int($term) ) { |
2123 | | if ( 0 == $term ) |
2124 | | return 0; |
2125 | | $where = 't.term_id = %d'; |
2126 | | if ( !empty($taxonomy) ) |
2127 | | return $wpdb->get_row( $wpdb->prepare( $tax_select . $where . " AND tt.taxonomy = %s", $term, $taxonomy ), ARRAY_A ); |
2128 | | else |
2129 | | return $wpdb->get_var( $wpdb->prepare( $select . $where, $term ) ); |
2130 | | } |
2131 | | |
2132 | | $term = trim( wp_unslash( $term ) ); |
2133 | | $slug = sanitize_title( $term ); |
2134 | | |
2135 | | $where = 't.slug = %s'; |
2136 | | $else_where = 't.name = %s'; |
2137 | | $where_fields = array($slug); |
2138 | | $else_where_fields = array($term); |
2139 | | $orderby = 'ORDER BY t.term_id ASC'; |
2140 | | $limit = 'LIMIT 1'; |
2141 | | if ( !empty($taxonomy) ) { |
2142 | | if ( is_numeric( $parent ) ) { |
2143 | | $parent = (int) $parent; |
2144 | | $where_fields[] = $parent; |
2145 | | $else_where_fields[] = $parent; |
2146 | | $where .= ' AND tt.parent = %d'; |
2147 | | $else_where .= ' AND tt.parent = %d'; |
2148 | | } |
2149 | | |
2150 | | $where_fields[] = $taxonomy; |
2151 | | $else_where_fields[] = $taxonomy; |
2152 | | |
2153 | | 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 $orderby $limit", $where_fields), ARRAY_A) ) |
2154 | | return $result; |
2155 | | |
2156 | | 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 $orderby $limit", $else_where_fields), ARRAY_A); |
2157 | | } |
2158 | | |
2159 | | if ( $result = $wpdb->get_var( $wpdb->prepare("SELECT term_id FROM $wpdb->terms as t WHERE $where $orderby $limit", $where_fields) ) ) |
2160 | | return $result; |
2161 | | |
2162 | | return $wpdb->get_var( $wpdb->prepare("SELECT term_id FROM $wpdb->terms as t WHERE $else_where $orderby $limit", $else_where_fields) ); |
2163 | | } |
2164 | | |
2165 | | /** |
2166 | | * Check if a term is an ancestor of another term. |
2167 | | * |
2168 | | * You can use either an id or the term object for both parameters. |
2169 | | * |
2170 | | * @since 3.4.0 |
2171 | | * |
2172 | | * @param int|object $term1 ID or object to check if this is the parent term. |
2173 | | * @param int|object $term2 The child term. |
2174 | | * @param string $taxonomy Taxonomy name that $term1 and `$term2` belong to. |
2175 | | * @return bool Whether `$term2` is a child of `$term1`. |
2176 | | */ |
2177 | | function term_is_ancestor_of( $term1, $term2, $taxonomy ) { |
2178 | | if ( ! isset( $term1->term_id ) ) |
2179 | | $term1 = get_term( $term1, $taxonomy ); |
2180 | | if ( ! isset( $term2->parent ) ) |
2181 | | $term2 = get_term( $term2, $taxonomy ); |
2182 | | |
2183 | | if ( empty( $term1->term_id ) || empty( $term2->parent ) ) |
2184 | | return false; |
2185 | | if ( $term2->parent == $term1->term_id ) |
2186 | | return true; |
2187 | | |
2188 | | return term_is_ancestor_of( $term1, get_term( $term2->parent, $taxonomy ), $taxonomy ); |
2189 | | } |
2190 | | |
2191 | | /** |
2192 | | * Sanitize Term all fields. |
2193 | | * |
2194 | | * Relies on sanitize_term_field() to sanitize the term. The difference is that |
2195 | | * this function will sanitize <strong>all</strong> fields. The context is based |
2196 | | * on sanitize_term_field(). |
2197 | | * |
2198 | | * The $term is expected to be either an array or an object. |
2199 | | * |
2200 | | * @since 2.3.0 |
2201 | | * |
2202 | | * @param array|object $term The term to check. |
2203 | | * @param string $taxonomy The taxonomy name to use. |
2204 | | * @param string $context Optional. Context in which to sanitize the term. Accepts 'edit', 'db', |
2205 | | * 'display', 'attribute', or 'js'. Default 'display'. |
2206 | | * @return array|object Term with all fields sanitized. |
2207 | | */ |
2208 | | function sanitize_term($term, $taxonomy, $context = 'display') { |
2209 | | $fields = array( 'term_id', 'name', 'description', 'slug', 'count', 'parent', 'term_group', 'term_taxonomy_id', 'object_id' ); |
2210 | | |
2211 | | $do_object = is_object( $term ); |
2212 | | |
2213 | | $term_id = $do_object ? $term->term_id : (isset($term['term_id']) ? $term['term_id'] : 0); |
2214 | | |
2215 | | foreach ( (array) $fields as $field ) { |
2216 | | if ( $do_object ) { |
2217 | | if ( isset($term->$field) ) |
2218 | | $term->$field = sanitize_term_field($field, $term->$field, $term_id, $taxonomy, $context); |
2219 | | } else { |
2220 | | if ( isset($term[$field]) ) |
2221 | | $term[$field] = sanitize_term_field($field, $term[$field], $term_id, $taxonomy, $context); |
2222 | | } |
2223 | | } |
2224 | | |
2225 | | if ( $do_object ) |
2226 | | $term->filter = $context; |
2227 | | else |
2228 | | $term['filter'] = $context; |
2229 | | |
2230 | | return $term; |
2231 | | } |
2232 | | |
2233 | | /** |
2234 | | * Cleanse the field value in the term based on the context. |
2235 | | * |
2236 | | * Passing a term field value through the function should be assumed to have |
2237 | | * cleansed the value for whatever context the term field is going to be used. |
2238 | | * |
2239 | | * If no context or an unsupported context is given, then default filters will |
2240 | | * be applied. |
2241 | | * |
2242 | | * There are enough filters for each context to support a custom filtering |
2243 | | * without creating your own filter function. Simply create a function that |
2244 | | * hooks into the filter you need. |
2245 | | * |
2246 | | * @since 2.3.0 |
2247 | | * |
2248 | | * @param string $field Term field to sanitize. |
2249 | | * @param string $value Search for this term value. |
2250 | | * @param int $term_id Term ID. |
2251 | | * @param string $taxonomy Taxonomy Name. |
2252 | | * @param string $context Context in which to sanitize the term field. Accepts 'edit', 'db', 'display', |
2253 | | * 'attribute', or 'js'. |
2254 | | * @return mixed Sanitized field. |
2255 | | */ |
2256 | | function sanitize_term_field($field, $value, $term_id, $taxonomy, $context) { |
2257 | | $int_fields = array( 'parent', 'term_id', 'count', 'term_group', 'term_taxonomy_id', 'object_id' ); |
2258 | | if ( in_array( $field, $int_fields ) ) { |
2259 | | $value = (int) $value; |
2260 | | if ( $value < 0 ) |
2261 | | $value = 0; |
2262 | | } |
2263 | | |
2264 | | if ( 'raw' == $context ) |
2265 | | return $value; |
2266 | | |
2267 | | if ( 'edit' == $context ) { |
2268 | | |
2269 | | /** |
2270 | | * Filter a term field to edit before it is sanitized. |
2271 | | * |
2272 | | * The dynamic portion of the filter name, `$field`, refers to the term field. |
2273 | | * |
2274 | | * @since 2.3.0 |
2275 | | * |
2276 | | * @param mixed $value Value of the term field. |
2277 | | * @param int $term_id Term ID. |
2278 | | * @param string $taxonomy Taxonomy slug. |
2279 | | */ |
2280 | | $value = apply_filters( "edit_term_{$field}", $value, $term_id, $taxonomy ); |
2281 | | |
2282 | | /** |
2283 | | * Filter the taxonomy field to edit before it is sanitized. |
2284 | | * |
2285 | | * The dynamic portions of the filter name, `$taxonomy` and `$field`, refer |
2286 | | * to the taxonomy slug and taxonomy field, respectively. |
2287 | | * |
2288 | | * @since 2.3.0 |
2289 | | * |
2290 | | * @param mixed $value Value of the taxonomy field to edit. |
2291 | | * @param int $term_id Term ID. |
2292 | | */ |
2293 | | $value = apply_filters( "edit_{$taxonomy}_{$field}", $value, $term_id ); |
2294 | | |
2295 | | if ( 'description' == $field ) |
2296 | | $value = esc_html($value); // textarea_escaped |
2297 | | else |
2298 | | $value = esc_attr($value); |
2299 | | } elseif ( 'db' == $context ) { |
2300 | | |
2301 | | /** |
2302 | | * Filter a term field value before it is sanitized. |
2303 | | * |
2304 | | * The dynamic portion of the filter name, `$field`, refers to the term field. |
2305 | | * |
2306 | | * @since 2.3.0 |
2307 | | * |
2308 | | * @param mixed $value Value of the term field. |
2309 | | * @param string $taxonomy Taxonomy slug. |
2310 | | */ |
2311 | | $value = apply_filters( "pre_term_{$field}", $value, $taxonomy ); |
2312 | | |
2313 | | /** |
2314 | | * Filter a taxonomy field before it is sanitized. |
2315 | | * |
2316 | | * The dynamic portions of the filter name, `$taxonomy` and `$field`, refer |
2317 | | * to the taxonomy slug and field name, respectively. |
2318 | | * |
2319 | | * @since 2.3.0 |
2320 | | * |
2321 | | * @param mixed $value Value of the taxonomy field. |
2322 | | */ |
2323 | | $value = apply_filters( "pre_{$taxonomy}_{$field}", $value ); |
2324 | | |
2325 | | // Back compat filters |
2326 | | if ( 'slug' == $field ) { |
2327 | | /** |
2328 | | * Filter the category nicename before it is sanitized. |
2329 | | * |
2330 | | * Use the pre_{$taxonomy}_{$field} hook instead. |
2331 | | * |
2332 | | * @since 2.0.3 |
2333 | | * |
2334 | | * @param string $value The category nicename. |
2335 | | */ |
2336 | | $value = apply_filters( 'pre_category_nicename', $value ); |
2337 | | } |
2338 | | |
2339 | | } elseif ( 'rss' == $context ) { |
2340 | | |
2341 | | /** |
2342 | | * Filter the term field for use in RSS. |
2343 | | * |
2344 | | * The dynamic portion of the filter name, `$field`, refers to the term field. |
2345 | | * |
2346 | | * @since 2.3.0 |
2347 | | * |
2348 | | * @param mixed $value Value of the term field. |
2349 | | * @param string $taxonomy Taxonomy slug. |
2350 | | */ |
2351 | | $value = apply_filters( "term_{$field}_rss", $value, $taxonomy ); |
2352 | | |
2353 | | /** |
2354 | | * Filter the taxonomy field for use in RSS. |
2355 | | * |
2356 | | * The dynamic portions of the hook name, `$taxonomy`, and `$field`, refer |
2357 | | * to the taxonomy slug and field name, respectively. |
2358 | | * |
2359 | | * @since 2.3.0 |
2360 | | * |
2361 | | * @param mixed $value Value of the taxonomy field. |
2362 | | */ |
2363 | | $value = apply_filters( "{$taxonomy}_{$field}_rss", $value ); |
2364 | | } else { |
2365 | | // Use display filters by default. |
2366 | | |
2367 | | /** |
2368 | | * Filter the term field sanitized for display. |
2369 | | * |
2370 | | * The dynamic portion of the filter name, `$field`, refers to the term field name. |
2371 | | * |
2372 | | * @since 2.3.0 |
2373 | | * |
2374 | | * @param mixed $value Value of the term field. |
2375 | | * @param int $term_id Term ID. |
2376 | | * @param string $taxonomy Taxonomy slug. |
2377 | | * @param string $context Context to retrieve the term field value. |
2378 | | */ |
2379 | | $value = apply_filters( "term_{$field}", $value, $term_id, $taxonomy, $context ); |
2380 | | |
2381 | | /** |
2382 | | * Filter the taxonomy field sanitized for display. |
2383 | | * |
2384 | | * The dynamic portions of the filter name, `$taxonomy`, and `$field`, refer |
2385 | | * to the taxonomy slug and taxonomy field, respectively. |
2386 | | * |
2387 | | * @since 2.3.0 |
2388 | | * |
2389 | | * @param mixed $value Value of the taxonomy field. |
2390 | | * @param int $term_id Term ID. |
2391 | | * @param string $context Context to retrieve the taxonomy field value. |
2392 | | */ |
2393 | | $value = apply_filters( "{$taxonomy}_{$field}", $value, $term_id, $context ); |
2394 | | } |
2395 | | |
2396 | | if ( 'attribute' == $context ) { |
2397 | | $value = esc_attr($value); |
2398 | | } elseif ( 'js' == $context ) { |
2399 | | $value = esc_js($value); |
2400 | | } |
2401 | | return $value; |
2402 | | } |
2403 | | |
2404 | | /** |
2405 | | * Count how many terms are in Taxonomy. |
2406 | | * |
2407 | | * Default $args is 'hide_empty' which can be 'hide_empty=true' or array('hide_empty' => true). |
2408 | | * |
2409 | | * @todo Document $args as a hash notation. |
2410 | | * |
2411 | | * @since 2.3.0 |
2412 | | * |
2413 | | * @param string $taxonomy Taxonomy name |
2414 | | * @param array|string $args Overwrite defaults. See get_terms() |
2415 | | * @return array|int|WP_Error How many terms are in $taxonomy. WP_Error if $taxonomy does not exist. |
2416 | | */ |
2417 | | function wp_count_terms( $taxonomy, $args = array() ) { |
2418 | | $defaults = array('hide_empty' => false); |
2419 | | $args = wp_parse_args($args, $defaults); |
2420 | | |
2421 | | // backwards compatibility |
2422 | | if ( isset($args['ignore_empty']) ) { |
2423 | | $args['hide_empty'] = $args['ignore_empty']; |
2424 | | unset($args['ignore_empty']); |
2425 | | } |
2426 | | |
2427 | | $args['fields'] = 'count'; |
2428 | | |
2429 | | return get_terms($taxonomy, $args); |
2430 | | } |
2431 | | |
2432 | | /** |
2433 | | * Will unlink the object from the taxonomy or taxonomies. |
2434 | | * |
2435 | | * Will remove all relationships between the object and any terms in |
2436 | | * a particular taxonomy or taxonomies. Does not remove the term or |
2437 | | * taxonomy itself. |
2438 | | * |
2439 | | * @since 2.3.0 |
2440 | | * |
2441 | | * @param int $object_id The term Object Id that refers to the term. |
2442 | | * @param string|array $taxonomies List of Taxonomy Names or single Taxonomy name. |
2443 | | */ |
2444 | | function wp_delete_object_term_relationships( $object_id, $taxonomies ) { |
2445 | | $object_id = (int) $object_id; |
2446 | | |
2447 | | if ( !is_array($taxonomies) ) |
2448 | | $taxonomies = array($taxonomies); |
2449 | | |
2450 | | foreach ( (array) $taxonomies as $taxonomy ) { |
2451 | | $term_ids = wp_get_object_terms( $object_id, $taxonomy, array( 'fields' => 'ids' ) ); |
2452 | | $term_ids = array_map( 'intval', $term_ids ); |
2453 | | wp_remove_object_terms( $object_id, $term_ids, $taxonomy ); |
2454 | | } |
2455 | | } |
2456 | | |
2457 | | /** |
2458 | | * Removes a term from the database. |
2459 | | * |
2460 | | * If the term is a parent of other terms, then the children will be updated to |
2461 | | * that term's parent. |
2462 | | * |
2463 | | * The `$args` 'default' will only override the terms found, if there is only one |
2464 | | * term found. Any other and the found terms are used. |
2465 | | * |
2466 | | * The $args 'force_default' will force the term supplied as default to be |
2467 | | * assigned even if the object was not going to be termless |
2468 | | * |
2469 | | * @todo Document $args as a hash notation. |
2470 | | * |
2471 | | * @since 2.3.0 |
2472 | | * |
2473 | | * @global wpdb $wpdb WordPress database abstraction object. |
2474 | | * |
2475 | | * @param int $term Term ID. |
2476 | | * @param string $taxonomy Taxonomy Name. |
2477 | | * @param array|string $args Optional. Change 'default' term id and override found term ids. |
2478 | | * @return bool|int|WP_Error Returns false if not term; true if completes delete action. |
2479 | | */ |
2480 | | function wp_delete_term( $term, $taxonomy, $args = array() ) { |
2481 | | global $wpdb; |
2482 | | |
2483 | | $term = (int) $term; |
2484 | | |
2485 | | if ( ! $ids = term_exists($term, $taxonomy) ) |
2486 | | return false; |
2487 | | if ( is_wp_error( $ids ) ) |
2488 | | return $ids; |
2489 | | |
2490 | | $tt_id = $ids['term_taxonomy_id']; |
2491 | | |
2492 | | $defaults = array(); |
2493 | | |
2494 | | if ( 'category' == $taxonomy ) { |
2495 | | $defaults['default'] = get_option( 'default_category' ); |
2496 | | if ( $defaults['default'] == $term ) |
2497 | | return 0; // Don't delete the default category |
2498 | | } |
2499 | | |
2500 | | $args = wp_parse_args($args, $defaults); |
2501 | | |
2502 | | if ( isset( $args['default'] ) ) { |
2503 | | $default = (int) $args['default']; |
2504 | | if ( ! term_exists( $default, $taxonomy ) ) { |
2505 | | unset( $default ); |
2506 | | } |
2507 | | } |
2508 | | |
2509 | | if ( isset( $args['force_default'] ) ) { |
2510 | | $force_default = $args['force_default']; |
2511 | | } |
2512 | | |
2513 | | /** |
2514 | | * Fires when deleting a term, before any modifications are made to posts or terms. |
2515 | | * |
2516 | | * @since 4.1.0 |
2517 | | * |
2518 | | * @param int $term Term ID. |
2519 | | * @param string $taxonomy Taxonomy Name. |
2520 | | */ |
2521 | | do_action( 'pre_delete_term', $term, $taxonomy ); |
2522 | | |
2523 | | // Update children to point to new parent |
2524 | | if ( is_taxonomy_hierarchical($taxonomy) ) { |
2525 | | $term_obj = get_term($term, $taxonomy); |
2526 | | if ( is_wp_error( $term_obj ) ) |
2527 | | return $term_obj; |
2528 | | $parent = $term_obj->parent; |
2529 | | |
2530 | | $edit_ids = $wpdb->get_results( "SELECT term_id, term_taxonomy_id FROM $wpdb->term_taxonomy WHERE `parent` = " . (int)$term_obj->term_id ); |
2531 | | $edit_tt_ids = wp_list_pluck( $edit_ids, 'term_taxonomy_id' ); |
2532 | | |
2533 | | /** |
2534 | | * Fires immediately before a term to delete's children are reassigned a parent. |
2535 | | * |
2536 | | * @since 2.9.0 |
2537 | | * |
2538 | | * @param array $edit_tt_ids An array of term taxonomy IDs for the given term. |
2539 | | */ |
2540 | | do_action( 'edit_term_taxonomies', $edit_tt_ids ); |
2541 | | |
2542 | | $wpdb->update( $wpdb->term_taxonomy, compact( 'parent' ), array( 'parent' => $term_obj->term_id) + compact( 'taxonomy' ) ); |
2543 | | |
2544 | | // Clean the cache for all child terms. |
2545 | | $edit_term_ids = wp_list_pluck( $edit_ids, 'term_id' ); |
2546 | | clean_term_cache( $edit_term_ids, $taxonomy ); |
2547 | | |
2548 | | /** |
2549 | | * Fires immediately after a term to delete's children are reassigned a parent. |
2550 | | * |
2551 | | * @since 2.9.0 |
2552 | | * |
2553 | | * @param array $edit_tt_ids An array of term taxonomy IDs for the given term. |
2554 | | */ |
2555 | | do_action( 'edited_term_taxonomies', $edit_tt_ids ); |
2556 | | } |
2557 | | |
2558 | | // Get the term before deleting it or its term relationships so we can pass to actions below. |
2559 | | $deleted_term = get_term( $term, $taxonomy ); |
2560 | | |
2561 | | $objects = $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $tt_id ) ); |
2562 | | |
2563 | | foreach ( (array) $objects as $object ) { |
2564 | | $terms = wp_get_object_terms($object, $taxonomy, array('fields' => 'ids', 'orderby' => 'none')); |
2565 | | if ( 1 == count($terms) && isset($default) ) { |
2566 | | $terms = array($default); |
2567 | | } else { |
2568 | | $terms = array_diff($terms, array($term)); |
2569 | | if (isset($default) && isset($force_default) && $force_default) |
2570 | | $terms = array_merge($terms, array($default)); |
2571 | | } |
2572 | | $terms = array_map('intval', $terms); |
2573 | | wp_set_object_terms($object, $terms, $taxonomy); |
2574 | | } |
2575 | | |
2576 | | // Clean the relationship caches for all object types using this term. |
2577 | | $tax_object = get_taxonomy( $taxonomy ); |
2578 | | foreach ( $tax_object->object_type as $object_type ) |
2579 | | clean_object_term_cache( $objects, $object_type ); |
2580 | | |
2581 | | /** |
2582 | | * Fires immediately before a term taxonomy ID is deleted. |
2583 | | * |
2584 | | * @since 2.9.0 |
2585 | | * |
2586 | | * @param int $tt_id Term taxonomy ID. |
2587 | | */ |
2588 | | do_action( 'delete_term_taxonomy', $tt_id ); |
2589 | | $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) ); |
2590 | | |
2591 | | /** |
2592 | | * Fires immediately after a term taxonomy ID is deleted. |
2593 | | * |
2594 | | * @since 2.9.0 |
2595 | | * |
2596 | | * @param int $tt_id Term taxonomy ID. |
2597 | | */ |
2598 | | do_action( 'deleted_term_taxonomy', $tt_id ); |
2599 | | |
2600 | | // Delete the term if no taxonomies use it. |
2601 | | if ( !$wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy WHERE term_id = %d", $term) ) ) |
2602 | | $wpdb->delete( $wpdb->terms, array( 'term_id' => $term ) ); |
2603 | | |
2604 | | clean_term_cache($term, $taxonomy); |
2605 | | |
2606 | | /** |
2607 | | * Fires after a term is deleted from the database and the cache is cleaned. |
2608 | | * |
2609 | | * @since 2.5.0 |
2610 | | * |
2611 | | * @param int $term Term ID. |
2612 | | * @param int $tt_id Term taxonomy ID. |
2613 | | * @param string $taxonomy Taxonomy slug. |
2614 | | * @param mixed $deleted_term Copy of the already-deleted term, in the form specified |
2615 | | * by the parent function. WP_Error otherwise. |
2616 | | */ |
2617 | | do_action( 'delete_term', $term, $tt_id, $taxonomy, $deleted_term ); |
2618 | | |
2619 | | /** |
2620 | | * Fires after a term in a specific taxonomy is deleted. |
2621 | | * |
2622 | | * The dynamic portion of the hook name, `$taxonomy`, refers to the specific |
2623 | | * taxonomy the term belonged to. |
2624 | | * |
2625 | | * @since 2.3.0 |
2626 | | * |
2627 | | * @param int $term Term ID. |
2628 | | * @param int $tt_id Term taxonomy ID. |
2629 | | * @param mixed $deleted_term Copy of the already-deleted term, in the form specified |
2630 | | * by the parent function. WP_Error otherwise. |
2631 | | */ |
2632 | | do_action( "delete_$taxonomy", $term, $tt_id, $deleted_term ); |
2633 | | |
2634 | | return true; |
2635 | | } |
2636 | | |
2637 | | /** |
2638 | | * Deletes one existing category. |
2639 | | * |
2640 | | * @since 2.0.0 |
2641 | | * |
2642 | | * @param int $cat_ID |
2643 | | * @return bool|int|WP_Error Returns true if completes delete action; false if term doesn't exist; |
2644 | | * Zero on attempted deletion of default Category; WP_Error object is also a possibility. |
2645 | | */ |
2646 | | function wp_delete_category( $cat_ID ) { |
2647 | | return wp_delete_term( $cat_ID, 'category' ); |
2648 | | } |
2649 | | |
2650 | | /** |
2651 | | * Retrieves the terms associated with the given object(s), in the supplied taxonomies. |
2652 | | * |
2653 | | * @since 2.3.0 |
2654 | | * @since 4.2.0 Added support for 'taxonomy', 'parent', and 'term_taxonomy_id' values of `$orderby`. |
2655 | | * Introduced `$parent` argument. |
2656 | | * |
2657 | | * @global wpdb $wpdb WordPress database abstraction object. |
2658 | | * |
2659 | | * @param int|array $object_ids The ID(s) of the object(s) to retrieve. |
2660 | | * @param string|array $taxonomies The taxonomies to retrieve terms from. |
2661 | | * @param array|string $args { |
2662 | | * Array of arguments. |
2663 | | * @type string $orderby Field by which results should be sorted. Accepts 'name', 'count', 'slug', 'term_group', |
2664 | | * 'term_order', 'taxonomy', 'parent', or 'term_taxonomy_id'. Default 'name'. |
2665 | | * @type string $order Sort order. Accepts 'ASC' or 'DESC'. Default 'ASC'. |
2666 | | * @type string $fields Fields to return for matched terms. Accepts 'all', 'ids', 'names', and |
2667 | | * 'all_with_object_id'. Note that 'all' or 'all_with_object_id' will result in an array of |
2668 | | * term objects being returned, 'ids' will return an array of integers, and 'names' an array |
2669 | | * of strings. |
2670 | | * @type int $parent Optional. Limit results to the direct children of a given term ID. |
2671 | | * } |
2672 | | * @return array|WP_Error The requested term data or empty array if no terms found. |
2673 | | * WP_Error if any of the $taxonomies don't exist. |
2674 | | */ |
2675 | | function wp_get_object_terms($object_ids, $taxonomies, $args = array()) { |
2676 | | global $wpdb; |
2677 | | |
2678 | | if ( empty( $object_ids ) || empty( $taxonomies ) ) |
2679 | | return array(); |
2680 | | |
2681 | | if ( !is_array($taxonomies) ) |
2682 | | $taxonomies = array($taxonomies); |
2683 | | |
2684 | | foreach ( $taxonomies as $taxonomy ) { |
2685 | | if ( ! taxonomy_exists($taxonomy) ) |
2686 | | return new WP_Error('invalid_taxonomy', __('Invalid taxonomy')); |
2687 | | } |
2688 | | |
2689 | | if ( !is_array($object_ids) ) |
2690 | | $object_ids = array($object_ids); |
2691 | | $object_ids = array_map('intval', $object_ids); |
2692 | | |
2693 | | $defaults = array( |
2694 | | 'orderby' => 'name', |
2695 | | 'order' => 'ASC', |
2696 | | 'fields' => 'all', |
2697 | | 'parent' => '', |
2698 | | ); |
2699 | | $args = wp_parse_args( $args, $defaults ); |
2700 | | |
2701 | | $terms = array(); |
2702 | | if ( count($taxonomies) > 1 ) { |
2703 | | foreach ( $taxonomies as $index => $taxonomy ) { |
2704 | | $t = get_taxonomy($taxonomy); |
2705 | | if ( isset($t->args) && is_array($t->args) && $args != array_merge($args, $t->args) ) { |
2706 | | unset($taxonomies[$index]); |
2707 | | $terms = array_merge($terms, wp_get_object_terms($object_ids, $taxonomy, array_merge($args, $t->args))); |
2708 | | } |
2709 | | } |
2710 | | } else { |
2711 | | $t = get_taxonomy($taxonomies[0]); |
2712 | | if ( isset($t->args) && is_array($t->args) ) |
2713 | | $args = array_merge($args, $t->args); |
2714 | | } |
2715 | | |
2716 | | $orderby = $args['orderby']; |
2717 | | $order = $args['order']; |
2718 | | $fields = $args['fields']; |
2719 | | |
2720 | | if ( in_array( $orderby, array( 'term_id', 'name', 'slug', 'term_group' ) ) ) { |
2721 | | $orderby = "t.$orderby"; |
2722 | | } elseif ( in_array( $orderby, array( 'count', 'parent', 'taxonomy', 'term_taxonomy_id' ) ) ) { |
2723 | | $orderby = "tt.$orderby"; |
2724 | | } elseif ( 'term_order' === $orderby ) { |
2725 | | $orderby = 'tr.term_order'; |
2726 | | } elseif ( 'none' === $orderby ) { |
2727 | | $orderby = ''; |
2728 | | $order = ''; |
2729 | | } else { |
2730 | | $orderby = 't.term_id'; |
2731 | | } |
2732 | | |
2733 | | // tt_ids queries can only be none or tr.term_taxonomy_id |
2734 | | if ( ('tt_ids' == $fields) && !empty($orderby) ) |
2735 | | $orderby = 'tr.term_taxonomy_id'; |
2736 | | |
2737 | | if ( !empty($orderby) ) |
2738 | | $orderby = "ORDER BY $orderby"; |
2739 | | |
2740 | | $order = strtoupper( $order ); |
2741 | | if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ) ) ) |
2742 | | $order = 'ASC'; |
2743 | | |
2744 | | $taxonomy_array = $taxonomies; |
2745 | | $object_id_array = $object_ids; |
2746 | | $taxonomies = "'" . implode("', '", $taxonomies) . "'"; |
2747 | | $object_ids = implode(', ', $object_ids); |
2748 | | |
2749 | | $select_this = ''; |
2750 | | if ( 'all' == $fields ) { |
2751 | | $select_this = 't.*, tt.*'; |
2752 | | } elseif ( 'ids' == $fields ) { |
2753 | | $select_this = 't.term_id'; |
2754 | | } elseif ( 'names' == $fields ) { |
2755 | | $select_this = 't.name'; |
2756 | | } elseif ( 'slugs' == $fields ) { |
2757 | | $select_this = 't.slug'; |
2758 | | } elseif ( 'all_with_object_id' == $fields ) { |
2759 | | $select_this = 't.*, tt.*, tr.object_id'; |
2760 | | } |
2761 | | |
2762 | | $where = array( |
2763 | | "tt.taxonomy IN ($taxonomies)", |
2764 | | "tr.object_id IN ($object_ids)", |
2765 | | ); |
2766 | | |
2767 | | if ( '' !== $args['parent'] ) { |
2768 | | $where[] = $wpdb->prepare( 'tt.parent = %d', $args['parent'] ); |
2769 | | } |
2770 | | |
2771 | | $where = implode( ' AND ', $where ); |
2772 | | |
2773 | | $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 $where $orderby $order"; |
2774 | | |
2775 | | $objects = false; |
2776 | | if ( 'all' == $fields || 'all_with_object_id' == $fields ) { |
2777 | | $_terms = $wpdb->get_results( $query ); |
2778 | | foreach ( $_terms as $key => $term ) { |
2779 | | $_terms[$key] = sanitize_term( $term, $taxonomy, 'raw' ); |
2780 | | } |
2781 | | $terms = array_merge( $terms, $_terms ); |
2782 | | update_term_cache( $terms ); |
2783 | | $objects = true; |
2784 | | } elseif ( 'ids' == $fields || 'names' == $fields || 'slugs' == $fields ) { |
2785 | | $_terms = $wpdb->get_col( $query ); |
2786 | | $_field = ( 'ids' == $fields ) ? 'term_id' : 'name'; |
2787 | | foreach ( $_terms as $key => $term ) { |
2788 | | $_terms[$key] = sanitize_term_field( $_field, $term, $term, $taxonomy, 'raw' ); |
2789 | | } |
2790 | | $terms = array_merge( $terms, $_terms ); |
2791 | | } elseif ( 'tt_ids' == $fields ) { |
2792 | | $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) $orderby $order"); |
2793 | | foreach ( $terms as $key => $tt_id ) { |
2794 | | $terms[$key] = sanitize_term_field( 'term_taxonomy_id', $tt_id, 0, $taxonomy, 'raw' ); // 0 should be the term id, however is not needed when using raw context. |
2795 | | } |
2796 | | } |
2797 | | |
2798 | | if ( ! $terms ) { |
2799 | | $terms = array(); |
2800 | | } elseif ( $objects && 'all_with_object_id' !== $fields ) { |
2801 | | $_tt_ids = array(); |
2802 | | $_terms = array(); |
2803 | | foreach ( $terms as $term ) { |
2804 | | if ( in_array( $term->term_taxonomy_id, $_tt_ids ) ) { |
2805 | | continue; |
2806 | | } |
2807 | | |
2808 | | $_tt_ids[] = $term->term_taxonomy_id; |
2809 | | $_terms[] = $term; |
2810 | | } |
2811 | | $terms = $_terms; |
2812 | | } elseif ( ! $objects ) { |
2813 | | $terms = array_values( array_unique( $terms ) ); |
2814 | | } |
2815 | | |
2816 | | /** |
2817 | | * Filter the terms for a given object or objects. |
2818 | | * |
2819 | | * @since 4.2.0 |
2820 | | * |
2821 | | * @param array $terms An array of terms for the given object or objects. |
2822 | | * @param array $object_id_array Array of object IDs for which `$terms` were retrieved. |
2823 | | * @param array $taxonomy_array Array of taxonomies from which `$terms` were retrieved. |
2824 | | * @param array $args An array of arguments for retrieving terms for the given |
2825 | | * object(s). See wp_get_object_terms() for details. |
2826 | | */ |
2827 | | $terms = apply_filters( 'get_object_terms', $terms, $object_id_array, $taxonomy_array, $args ); |
2828 | | |
2829 | | /** |
2830 | | * Filter the terms for a given object or objects. |
2831 | | * |
2832 | | * The `$taxonomies` parameter passed to this filter is formatted as a SQL fragment. The |
2833 | | * {@see 'get_object_terms'} filter is recommended as an alternative. |
2834 | | * |
2835 | | * @since 2.8.0 |
2836 | | * |
2837 | | * @param array $terms An array of terms for the given object or objects. |
2838 | | * @param int|array $object_ids Object ID or array of IDs. |
2839 | | * @param string $taxonomies SQL-formatted (comma-separated and quoted) list of taxonomy names. |
2840 | | * @param array $args An array of arguments for retrieving terms for the given object(s). |
2841 | | * See {@see wp_get_object_terms()} for details. |
2842 | | */ |
2843 | | return apply_filters( 'wp_get_object_terms', $terms, $object_ids, $taxonomies, $args ); |
2844 | | } |
2845 | | |
2846 | | /** |
2847 | | * Add a new term to the database. |
2848 | | * |
2849 | | * A non-existent term is inserted in the following sequence: |
2850 | | * 1. The term is added to the term table, then related to the taxonomy. |
2851 | | * 2. If everything is correct, several actions are fired. |
2852 | | * 3. The 'term_id_filter' is evaluated. |
2853 | | * 4. The term cache is cleaned. |
2854 | | * 5. Several more actions are fired. |
2855 | | * 6. An array is returned containing the term_id and term_taxonomy_id. |
2856 | | * |
2857 | | * If the 'slug' argument is not empty, then it is checked to see if the term |
2858 | | * is invalid. If it is not a valid, existing term, it is added and the term_id |
2859 | | * is given. |
2860 | | * |
2861 | | * If the taxonomy is hierarchical, and the 'parent' argument is not empty, |
2862 | | * the term is inserted and the term_id will be given. |
2863 | | |
2864 | | * Error handling: |
2865 | | * If $taxonomy does not exist or $term is empty, |
2866 | | * a WP_Error object will be returned. |
2867 | | * |
2868 | | * If the term already exists on the same hierarchical level, |
2869 | | * or the term slug and name are not unique, a WP_Error object will be returned. |
2870 | | * |
2871 | | * @global wpdb $wpdb WordPress database abstraction object. |
2872 | | |
2873 | | * @since 2.3.0 |
2874 | | * |
2875 | | * @param string $term The term to add or update. |
2876 | | * @param string $taxonomy The taxonomy to which to add the term. |
2877 | | * @param array|string $args { |
2878 | | * Optional. Array or string of arguments for inserting a term. |
2879 | | * |
2880 | | * @type string $alias_of Slug of the term to make this term an alias of. |
2881 | | * Default empty string. Accepts a term slug. |
2882 | | * @type string $description The term description. Default empty string. |
2883 | | * @type int $parent The id of the parent term. Default 0. |
2884 | | * @type string $slug The term slug to use. Default empty string. |
2885 | | * } |
2886 | | * @return array|WP_Error An array containing the `term_id` and `term_taxonomy_id`, |
2887 | | * {@see WP_Error} otherwise. |
2888 | | */ |
2889 | | function wp_insert_term( $term, $taxonomy, $args = array() ) { |
2890 | | global $wpdb; |
2891 | | |
2892 | | if ( ! taxonomy_exists($taxonomy) ) { |
2893 | | return new WP_Error('invalid_taxonomy', __('Invalid taxonomy')); |
2894 | | } |
2895 | | /** |
2896 | | * Filter a term before it is sanitized and inserted into the database. |
2897 | | * |
2898 | | * @since 3.0.0 |
2899 | | * |
2900 | | * @param string $term The term to add or update. |
2901 | | * @param string $taxonomy Taxonomy slug. |
2902 | | */ |
2903 | | $term = apply_filters( 'pre_insert_term', $term, $taxonomy ); |
2904 | | if ( is_wp_error( $term ) ) { |
2905 | | return $term; |
2906 | | } |
2907 | | if ( is_int($term) && 0 == $term ) { |
2908 | | return new WP_Error('invalid_term_id', __('Invalid term ID')); |
2909 | | } |
2910 | | if ( '' == trim($term) ) { |
2911 | | return new WP_Error('empty_term_name', __('A name is required for this term')); |
2912 | | } |
2913 | | $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => ''); |
2914 | | $args = wp_parse_args( $args, $defaults ); |
2915 | | |
2916 | | if ( $args['parent'] > 0 && ! term_exists( (int) $args['parent'] ) ) { |
2917 | | return new WP_Error( 'missing_parent', __( 'Parent term does not exist.' ) ); |
2918 | | } |
2919 | | $args['name'] = $term; |
2920 | | $args['taxonomy'] = $taxonomy; |
2921 | | $args = sanitize_term($args, $taxonomy, 'db'); |
2922 | | |
2923 | | // expected_slashed ($name) |
2924 | | $name = wp_unslash( $args['name'] ); |
2925 | | $description = wp_unslash( $args['description'] ); |
2926 | | $parent = (int) $args['parent']; |
2927 | | |
2928 | | $slug_provided = ! empty( $args['slug'] ); |
2929 | | if ( ! $slug_provided ) { |
2930 | | $slug = sanitize_title( $name ); |
2931 | | } else { |
2932 | | $slug = $args['slug']; |
2933 | | } |
2934 | | |
2935 | | $term_group = 0; |
2936 | | if ( $args['alias_of'] ) { |
2937 | | $alias = get_term_by( 'slug', $args['alias_of'], $taxonomy ); |
2938 | | if ( ! empty( $alias->term_group ) ) { |
2939 | | // The alias we want is already in a group, so let's use that one. |
2940 | | $term_group = $alias->term_group; |
2941 | | } elseif ( ! empty( $alias->term_id ) ) { |
2942 | | /* |
2943 | | * The alias is not in a group, so we create a new one |
2944 | | * and add the alias to it. |
2945 | | */ |
2946 | | $term_group = $wpdb->get_var("SELECT MAX(term_group) FROM $wpdb->terms") + 1; |
2947 | | |
2948 | | wp_update_term( $alias->term_id, $taxonomy, array( |
2949 | | 'term_group' => $term_group, |
2950 | | ) ); |
2951 | | } |
2952 | | } |
2953 | | |
2954 | | /* |
2955 | | * Prevent the creation of terms with duplicate names at the same level of a taxonomy hierarchy, |
2956 | | * unless a unique slug has been explicitly provided. |
2957 | | */ |
2958 | | if ( $name_match = get_term_by( 'name', $name, $taxonomy ) ) { |
2959 | | $slug_match = get_term_by( 'slug', $slug, $taxonomy ); |
2960 | | if ( ! $slug_provided || $name_match->slug === $slug || $slug_match ) { |
2961 | | if ( is_taxonomy_hierarchical( $taxonomy ) ) { |
2962 | | $siblings = get_terms( $taxonomy, array( 'get' => 'all', 'parent' => $parent ) ); |
2963 | | |
2964 | | $existing_term = null; |
2965 | | if ( $name_match->slug === $slug && in_array( $name, wp_list_pluck( $siblings, 'name' ) ) ) { |
2966 | | $existing_term = $name_match; |
2967 | | } elseif ( $slug_match && in_array( $slug, wp_list_pluck( $siblings, 'slug' ) ) ) { |
2968 | | $existing_term = $slug_match; |
2969 | | } |
2970 | | |
2971 | | if ( $existing_term ) { |
2972 | | return new WP_Error( 'term_exists', __( 'A term with the name provided already exists with this parent.' ), $existing_term->term_id ); |
2973 | | } |
2974 | | } else { |
2975 | | return new WP_Error( 'term_exists', __( 'A term with the name provided already exists in this taxonomy.' ), $name_match->term_id ); |
2976 | | } |
2977 | | } |
2978 | | } |
2979 | | |
2980 | | $slug = wp_unique_term_slug( $slug, (object) $args ); |
2981 | | |
2982 | | if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) { |
2983 | | return new WP_Error( 'db_insert_error', __( 'Could not insert term into the database' ), $wpdb->last_error ); |
2984 | | } |
2985 | | |
2986 | | $term_id = (int) $wpdb->insert_id; |
2987 | | |
2988 | | // Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string. |
2989 | | if ( empty($slug) ) { |
2990 | | $slug = sanitize_title($slug, $term_id); |
2991 | | |
2992 | | /** This action is documented in wp-includes/taxonomy.php */ |
2993 | | do_action( 'edit_terms', $term_id, $taxonomy ); |
2994 | | $wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) ); |
2995 | | |
2996 | | /** This action is documented in wp-includes/taxonomy.php */ |
2997 | | do_action( 'edited_terms', $term_id, $taxonomy ); |
2998 | | } |
2999 | | |
3000 | | $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 ) ); |
3001 | | |
3002 | | if ( !empty($tt_id) ) { |
3003 | | return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); |
3004 | | } |
3005 | | $wpdb->insert( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) ); |
3006 | | $tt_id = (int) $wpdb->insert_id; |
3007 | | |
3008 | | /* |
3009 | | * Sanity check: if we just created a term with the same parent + taxonomy + slug but a higher term_id than |
3010 | | * an existing term, then we have unwittingly created a duplicate term. Delete the dupe, and use the term_id |
3011 | | * and term_taxonomy_id of the older term instead. Then return out of the function so that the "create" hooks |
3012 | | * are not fired. |
3013 | | */ |
3014 | | $duplicate_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.term_id, tt.term_taxonomy_id FROM $wpdb->terms t INNER JOIN $wpdb->term_taxonomy tt ON ( tt.term_id = t.term_id ) WHERE t.slug = %s AND tt.parent = %d AND tt.taxonomy = %s AND t.term_id < %d AND tt.term_taxonomy_id != %d", $slug, $parent, $taxonomy, $term_id, $tt_id ) ); |
3015 | | if ( $duplicate_term ) { |
3016 | | $wpdb->delete( $wpdb->terms, array( 'term_id' => $term_id ) ); |
3017 | | $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) ); |
3018 | | |
3019 | | $term_id = (int) $duplicate_term->term_id; |
3020 | | $tt_id = (int) $duplicate_term->term_taxonomy_id; |
3021 | | |
3022 | | clean_term_cache( $term_id, $taxonomy ); |
3023 | | return array( 'term_id' => $term_id, 'term_taxonomy_id' => $tt_id ); |
3024 | | } |
3025 | | |
3026 | | /** |
3027 | | * Fires immediately after a new term is created, before the term cache is cleaned. |
3028 | | * |
3029 | | * @since 2.3.0 |
3030 | | * |
3031 | | * @param int $term_id Term ID. |
3032 | | * @param int $tt_id Term taxonomy ID. |
3033 | | * @param string $taxonomy Taxonomy slug. |
3034 | | */ |
3035 | | do_action( "create_term", $term_id, $tt_id, $taxonomy ); |
3036 | | |
3037 | | /** |
3038 | | * Fires after a new term is created for a specific taxonomy. |
3039 | | * |
3040 | | * The dynamic portion of the hook name, `$taxonomy`, refers |
3041 | | * to the slug of the taxonomy the term was created for. |
3042 | | * |
3043 | | * @since 2.3.0 |
3044 | | * |
3045 | | * @param int $term_id Term ID. |
3046 | | * @param int $tt_id Term taxonomy ID. |
3047 | | */ |
3048 | | do_action( "create_$taxonomy", $term_id, $tt_id ); |
3049 | | |
3050 | | /** |
3051 | | * Filter the term ID after a new term is created. |
3052 | | * |
3053 | | * @since 2.3.0 |
3054 | | * |
3055 | | * @param int $term_id Term ID. |
3056 | | * @param int $tt_id Taxonomy term ID. |
3057 | | */ |
3058 | | $term_id = apply_filters( 'term_id_filter', $term_id, $tt_id ); |
3059 | | |
3060 | | clean_term_cache($term_id, $taxonomy); |
3061 | | |
3062 | | /** |
3063 | | * Fires after a new term is created, and after the term cache has been cleaned. |
3064 | | * |
3065 | | * @since 2.3.0 |
3066 | | * |
3067 | | * @param int $term_id Term ID. |
3068 | | * @param int $tt_id Term taxonomy ID. |
3069 | | * @param string $taxonomy Taxonomy slug. |
3070 | | */ |
3071 | | do_action( 'created_term', $term_id, $tt_id, $taxonomy ); |
3072 | | |
3073 | | /** |
3074 | | * Fires after a new term in a specific taxonomy is created, and after the term |
3075 | | * cache has been cleaned. |
3076 | | * |
3077 | | * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug. |
3078 | | * |
3079 | | * @since 2.3.0 |
3080 | | * |
3081 | | * @param int $term_id Term ID. |
3082 | | * @param int $tt_id Term taxonomy ID. |
3083 | | */ |
3084 | | do_action( "created_$taxonomy", $term_id, $tt_id ); |
3085 | | |
3086 | | return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); |
3087 | | } |
3088 | | |
3089 | | /** |
3090 | | * Create Term and Taxonomy Relationships. |
3091 | | * |
3092 | | * Relates an object (post, link etc) to a term and taxonomy type. Creates the |
3093 | | * term and taxonomy relationship if it doesn't already exist. Creates a term if |
3094 | | * it doesn't exist (using the slug). |
3095 | | * |
3096 | | * A relationship means that the term is grouped in or belongs to the taxonomy. |
3097 | | * A term has no meaning until it is given context by defining which taxonomy it |
3098 | | * exists under. |
3099 | | * |
3100 | | * @since 2.3.0 |
3101 | | * |
3102 | | * @global wpdb $wpdb The WordPress database abstraction object. |
3103 | | * |
3104 | | * @param int $object_id The object to relate to. |
3105 | | * @param array|int|string $terms A single term slug, single term id, or array of either term slugs or ids. |
3106 | | * Will replace all existing related terms in this taxonomy. |
3107 | | * @param string $taxonomy The context in which to relate the term to the object. |
3108 | | * @param bool $append Optional. If false will delete difference of terms. Default false. |
3109 | | * @return array|WP_Error Affected Term IDs. |
3110 | | */ |
3111 | | function wp_set_object_terms( $object_id, $terms, $taxonomy, $append = false ) { |
3112 | | global $wpdb; |
3113 | | |
3114 | | $object_id = (int) $object_id; |
3115 | | |
3116 | | if ( ! taxonomy_exists($taxonomy) ) |
3117 | | return new WP_Error('invalid_taxonomy', __('Invalid taxonomy')); |
3118 | | |
3119 | | if ( !is_array($terms) ) |
3120 | | $terms = array($terms); |
3121 | | |
3122 | | if ( ! $append ) |
3123 | | $old_tt_ids = wp_get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids', 'orderby' => 'none')); |
3124 | | else |
3125 | | $old_tt_ids = array(); |
3126 | | |
3127 | | $tt_ids = array(); |
3128 | | $term_ids = array(); |
3129 | | $new_tt_ids = array(); |
3130 | | |
3131 | | foreach ( (array) $terms as $term) { |
3132 | | if ( !strlen(trim($term)) ) |
3133 | | continue; |
3134 | | |
3135 | | if ( !$term_info = term_exists($term, $taxonomy) ) { |
3136 | | // Skip if a non-existent term ID is passed. |
3137 | | if ( is_int($term) ) |
3138 | | continue; |
3139 | | $term_info = wp_insert_term($term, $taxonomy); |
3140 | | } |
3141 | | if ( is_wp_error($term_info) ) |
3142 | | return $term_info; |
3143 | | $term_ids[] = $term_info['term_id']; |
3144 | | $tt_id = $term_info['term_taxonomy_id']; |
3145 | | $tt_ids[] = $tt_id; |
3146 | | |
3147 | | 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, $tt_id ) ) ) |
3148 | | continue; |
3149 | | |
3150 | | /** |
3151 | | * Fires immediately before an object-term relationship is added. |
3152 | | * |
3153 | | * @since 2.9.0 |
3154 | | * |
3155 | | * @param int $object_id Object ID. |
3156 | | * @param int $tt_id Term taxonomy ID. |
3157 | | */ |
3158 | | do_action( 'add_term_relationship', $object_id, $tt_id ); |
3159 | | $wpdb->insert( $wpdb->term_relationships, array( 'object_id' => $object_id, 'term_taxonomy_id' => $tt_id ) ); |
3160 | | |
3161 | | /** |
3162 | | * Fires immediately after an object-term relationship is added. |
3163 | | * |
3164 | | * @since 2.9.0 |
3165 | | * |
3166 | | * @param int $object_id Object ID. |
3167 | | * @param int $tt_id Term taxonomy ID. |
3168 | | */ |
3169 | | do_action( 'added_term_relationship', $object_id, $tt_id ); |
3170 | | $new_tt_ids[] = $tt_id; |
3171 | | } |
3172 | | |
3173 | | if ( $new_tt_ids ) |
3174 | | wp_update_term_count( $new_tt_ids, $taxonomy ); |
3175 | | |
3176 | | if ( ! $append ) { |
3177 | | $delete_tt_ids = array_diff( $old_tt_ids, $tt_ids ); |
3178 | | |
3179 | | if ( $delete_tt_ids ) { |
3180 | | $in_delete_tt_ids = "'" . implode( "', '", $delete_tt_ids ) . "'"; |
3181 | | $delete_term_ids = $wpdb->get_col( $wpdb->prepare( "SELECT tt.term_id FROM $wpdb->term_taxonomy AS tt WHERE tt.taxonomy = %s AND tt.term_taxonomy_id IN ($in_delete_tt_ids)", $taxonomy ) ); |
3182 | | $delete_term_ids = array_map( 'intval', $delete_term_ids ); |
3183 | | |
3184 | | $remove = wp_remove_object_terms( $object_id, $delete_term_ids, $taxonomy ); |
3185 | | if ( is_wp_error( $remove ) ) { |
3186 | | return $remove; |
3187 | | } |
3188 | | } |
3189 | | } |
3190 | | |
3191 | | $t = get_taxonomy($taxonomy); |
3192 | | if ( ! $append && isset($t->sort) && $t->sort ) { |
3193 | | $values = array(); |
3194 | | $term_order = 0; |
3195 | | $final_tt_ids = wp_get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids')); |
3196 | | foreach ( $tt_ids as $tt_id ) |
3197 | | if ( in_array($tt_id, $final_tt_ids) ) |
3198 | | $values[] = $wpdb->prepare( "(%d, %d, %d)", $object_id, $tt_id, ++$term_order); |
3199 | | if ( $values ) |
3200 | | if ( false === $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)" ) ) |
3201 | | return new WP_Error( 'db_insert_error', __( 'Could not insert term relationship into the database' ), $wpdb->last_error ); |
3202 | | } |
3203 | | |
3204 | | wp_cache_delete( $object_id, $taxonomy . '_relationships' ); |
3205 | | |
3206 | | /** |
3207 | | * Fires after an object's terms have been set. |
3208 | | * |
3209 | | * @since 2.8.0 |
3210 | | * |
3211 | | * @param int $object_id Object ID. |
3212 | | * @param array $terms An array of object terms. |
3213 | | * @param array $tt_ids An array of term taxonomy IDs. |
3214 | | * @param string $taxonomy Taxonomy slug. |
3215 | | * @param bool $append Whether to append new terms to the old terms. |
3216 | | * @param array $old_tt_ids Old array of term taxonomy IDs. |
3217 | | */ |
3218 | | do_action( 'set_object_terms', $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ); |
3219 | | return $tt_ids; |
3220 | | } |
3221 | | |
3222 | | /** |
3223 | | * Add term(s) associated with a given object. |
3224 | | * |
3225 | | * @since 3.6.0 |
3226 | | * |
3227 | | * @param int $object_id The ID of the object to which the terms will be added. |
3228 | | * @param array|int|string $terms The slug(s) or ID(s) of the term(s) to add. |
3229 | | * @param array|string $taxonomy Taxonomy name. |
3230 | | * @return array|WP_Error Affected Term IDs |
3231 | | */ |
3232 | | function wp_add_object_terms( $object_id, $terms, $taxonomy ) { |
3233 | | return wp_set_object_terms( $object_id, $terms, $taxonomy, true ); |
3234 | | } |
3235 | | |
3236 | | /** |
3237 | | * Remove term(s) associated with a given object. |
3238 | | * |
3239 | | * @since 3.6.0 |
3240 | | * |
3241 | | * @global wpdb $wpdb WordPress database abstraction object. |
3242 | | * |
3243 | | * @param int $object_id The ID of the object from which the terms will be removed. |
3244 | | * @param array|int|string $terms The slug(s) or ID(s) of the term(s) to remove. |
3245 | | * @param array|string $taxonomy Taxonomy name. |
3246 | | * @return bool|WP_Error True on success, false or WP_Error on failure. |
3247 | | */ |
3248 | | function wp_remove_object_terms( $object_id, $terms, $taxonomy ) { |
3249 | | global $wpdb; |
3250 | | |
3251 | | $object_id = (int) $object_id; |
3252 | | |
3253 | | if ( ! taxonomy_exists( $taxonomy ) ) { |
3254 | | return new WP_Error( 'invalid_taxonomy', __( 'Invalid Taxonomy' ) ); |
3255 | | } |
3256 | | |
3257 | | if ( ! is_array( $terms ) ) { |
3258 | | $terms = array( $terms ); |
3259 | | } |
3260 | | |
3261 | | $tt_ids = array(); |
3262 | | |
3263 | | foreach ( (array) $terms as $term ) { |
3264 | | if ( ! strlen( trim( $term ) ) ) { |
3265 | | continue; |
3266 | | } |
3267 | | |
3268 | | if ( ! $term_info = term_exists( $term, $taxonomy ) ) { |
3269 | | // Skip if a non-existent term ID is passed. |
3270 | | if ( is_int( $term ) ) { |
3271 | | continue; |
3272 | | } |
3273 | | } |
3274 | | |
3275 | | if ( is_wp_error( $term_info ) ) { |
3276 | | return $term_info; |
3277 | | } |
3278 | | |
3279 | | $tt_ids[] = $term_info['term_taxonomy_id']; |
3280 | | } |
3281 | | |
3282 | | if ( $tt_ids ) { |
3283 | | $in_tt_ids = "'" . implode( "', '", $tt_ids ) . "'"; |
3284 | | |
3285 | | /** |
3286 | | * Fires immediately before an object-term relationship is deleted. |
3287 | | * |
3288 | | * @since 2.9.0 |
3289 | | * |
3290 | | * @param int $object_id Object ID. |
3291 | | * @param array $tt_ids An array of term taxonomy IDs. |
3292 | | */ |
3293 | | do_action( 'delete_term_relationships', $object_id, $tt_ids ); |
3294 | | $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) ); |
3295 | | |
3296 | | /** |
3297 | | * Fires immediately after an object-term relationship is deleted. |
3298 | | * |
3299 | | * @since 2.9.0 |
3300 | | * |
3301 | | * @param int $object_id Object ID. |
3302 | | * @param array $tt_ids An array of term taxonomy IDs. |
3303 | | */ |
3304 | | do_action( 'deleted_term_relationships', $object_id, $tt_ids ); |
3305 | | |
3306 | | wp_update_term_count( $tt_ids, $taxonomy ); |
3307 | | |
3308 | | return (bool) $deleted; |
3309 | | } |
3310 | | |
3311 | | return false; |
3312 | | } |
3313 | | |
3314 | | /** |
3315 | | * Will make slug unique, if it isn't already. |
3316 | | * |
3317 | | * The `$slug` has to be unique global to every taxonomy, meaning that one |
3318 | | * taxonomy term can't have a matching slug with another taxonomy term. Each |
3319 | | * slug has to be globally unique for every taxonomy. |
3320 | | * |
3321 | | * The way this works is that if the taxonomy that the term belongs to is |
3322 | | * hierarchical and has a parent, it will append that parent to the $slug. |
3323 | | * |
3324 | | * If that still doesn't return an unique slug, then it try to append a number |
3325 | | * until it finds a number that is truly unique. |
3326 | | * |
3327 | | * The only purpose for `$term` is for appending a parent, if one exists. |
3328 | | * |
3329 | | * @since 2.3.0 |
3330 | | * |
3331 | | * @global wpdb $wpdb WordPress database abstraction object. |
3332 | | * |
3333 | | * @param string $slug The string that will be tried for a unique slug. |
3334 | | * @param object $term The term object that the `$slug` will belong to. |
3335 | | * @return string Will return a true unique slug. |
3336 | | */ |
3337 | | function wp_unique_term_slug( $slug, $term ) { |
3338 | | global $wpdb; |
3339 | | |
3340 | | $needs_suffix = true; |
3341 | | $original_slug = $slug; |
3342 | | |
3343 | | // As of 4.1, duplicate slugs are allowed as long as they're in different taxonomies. |
3344 | | if ( ! term_exists( $slug ) || get_option( 'db_version' ) >= 30133 && ! get_term_by( 'slug', $slug, $term->taxonomy ) ) { |
3345 | | $needs_suffix = false; |
3346 | | } |
3347 | | |
3348 | | /* |
3349 | | * If the taxonomy supports hierarchy and the term has a parent, make the slug unique |
3350 | | * by incorporating parent slugs. |
3351 | | */ |
3352 | | $parent_suffix = ''; |
3353 | | if ( $needs_suffix && is_taxonomy_hierarchical( $term->taxonomy ) && ! empty( $term->parent ) ) { |
3354 | | $the_parent = $term->parent; |
3355 | | while ( ! empty($the_parent) ) { |
3356 | | $parent_term = get_term($the_parent, $term->taxonomy); |
3357 | | if ( is_wp_error($parent_term) || empty($parent_term) ) |
3358 | | break; |
3359 | | $parent_suffix .= '-' . $parent_term->slug; |
3360 | | if ( ! term_exists( $slug . $parent_suffix ) ) { |
3361 | | break; |
3362 | | } |
3363 | | |
3364 | | if ( empty($parent_term->parent) ) |
3365 | | break; |
3366 | | $the_parent = $parent_term->parent; |
3367 | | } |
3368 | | } |
3369 | | |
3370 | | // If we didn't get a unique slug, try appending a number to make it unique. |
3371 | | |
3372 | | /** |
3373 | | * Filter whether the proposed unique term slug is bad. |
3374 | | * |
3375 | | * @since 4.3.0 |
3376 | | * |
3377 | | * @param bool $needs_suffix Whether the slug needs to be made unique with a suffix. |
3378 | | * @param string $slug The slug. |
3379 | | * @param object $term Term object. |
3380 | | */ |
3381 | | if ( apply_filters( 'wp_unique_term_slug_is_bad_slug', $needs_suffix, $slug, $term ) ) { |
3382 | | if ( $parent_suffix ) { |
3383 | | $slug .= $parent_suffix; |
3384 | | } else { |
3385 | | if ( ! empty( $term->term_id ) ) |
3386 | | $query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s AND term_id != %d", $slug, $term->term_id ); |
3387 | | else |
3388 | | $query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $slug ); |
3389 | | |
3390 | | if ( $wpdb->get_var( $query ) ) { |
3391 | | $num = 2; |
3392 | | do { |
3393 | | $alt_slug = $slug . "-$num"; |
3394 | | $num++; |
3395 | | $slug_check = $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $alt_slug ) ); |
3396 | | } while ( $slug_check ); |
3397 | | $slug = $alt_slug; |
3398 | | } |
3399 | | } |
3400 | | } |
3401 | | |
3402 | | /** |
3403 | | * Filter the unique term slug. |
3404 | | * |
3405 | | * @since 4.3.0 |
3406 | | * |
3407 | | * @param string $slug Unique term slug. |
3408 | | * @param object $term Term object. |
3409 | | * @param string $original_slug Slug originally passed to the function for testing. |
3410 | | */ |
3411 | | return apply_filters( 'wp_unique_term_slug', $slug, $term, $original_slug ); |
3412 | | } |
3413 | | |
3414 | | /** |
3415 | | * Update term based on arguments provided. |
3416 | | * |
3417 | | * The $args will indiscriminately override all values with the same field name. |
3418 | | * Care must be taken to not override important information need to update or |
3419 | | * update will fail (or perhaps create a new term, neither would be acceptable). |
3420 | | * |
3421 | | * Defaults will set 'alias_of', 'description', 'parent', and 'slug' if not |
3422 | | * defined in $args already. |
3423 | | * |
3424 | | * 'alias_of' will create a term group, if it doesn't already exist, and update |
3425 | | * it for the $term. |
3426 | | * |
3427 | | * If the 'slug' argument in $args is missing, then the 'name' in $args will be |
3428 | | * used. It should also be noted that if you set 'slug' and it isn't unique then |
3429 | | * a WP_Error will be passed back. If you don't pass any slug, then a unique one |
3430 | | * will be created for you. |
3431 | | * |
3432 | | * For what can be overrode in `$args`, check the term scheme can contain and stay |
3433 | | * away from the term keys. |
3434 | | * |
3435 | | * @since 2.3.0 |
3436 | | * |
3437 | | * @global wpdb $wpdb WordPress database abstraction object. |
3438 | | * |
3439 | | * @param int $term_id The ID of the term |
3440 | | * @param string $taxonomy The context in which to relate the term to the object. |
3441 | | * @param array|string $args Optional. Array of get_terms() arguments. Default empty array. |
3442 | | * @return array|WP_Error Returns Term ID and Taxonomy Term ID |
3443 | | */ |
3444 | | function wp_update_term( $term_id, $taxonomy, $args = array() ) { |
3445 | | global $wpdb; |
3446 | | |
3447 | | if ( ! taxonomy_exists( $taxonomy ) ) { |
3448 | | return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) ); |
3449 | | } |
3450 | | |
3451 | | $term_id = (int) $term_id; |
3452 | | |
3453 | | // First, get all of the original args |
3454 | | $term = get_term( $term_id, $taxonomy, ARRAY_A ); |
3455 | | |
3456 | | if ( is_wp_error( $term ) ) { |
3457 | | return $term; |
3458 | | } |
3459 | | |
3460 | | if ( ! $term ) { |
3461 | | return new WP_Error( 'invalid_term', __( 'Empty Term' ) ); |
3462 | | } |
3463 | | |
3464 | | // Escape data pulled from DB. |
3465 | | $term = wp_slash($term); |
3466 | | |
3467 | | // Merge old and new args with new args overwriting old ones. |
3468 | | $args = array_merge($term, $args); |
3469 | | |
3470 | | $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => ''); |
3471 | | $args = wp_parse_args($args, $defaults); |
3472 | | $args = sanitize_term($args, $taxonomy, 'db'); |
3473 | | $parsed_args = $args; |
3474 | | |
3475 | | // expected_slashed ($name) |
3476 | | $name = wp_unslash( $args['name'] ); |
3477 | | $description = wp_unslash( $args['description'] ); |
3478 | | |
3479 | | $parsed_args['name'] = $name; |
3480 | | $parsed_args['description'] = $description; |
3481 | | |
3482 | | if ( '' == trim($name) ) |
3483 | | return new WP_Error('empty_term_name', __('A name is required for this term')); |
3484 | | |
3485 | | if ( $parsed_args['parent'] > 0 && ! term_exists( (int) $parsed_args['parent'] ) ) { |
3486 | | return new WP_Error( 'missing_parent', __( 'Parent term does not exist.' ) ); |
3487 | | } |
3488 | | |
3489 | | $empty_slug = false; |
3490 | | if ( empty( $args['slug'] ) ) { |
3491 | | $empty_slug = true; |
3492 | | $slug = sanitize_title($name); |
3493 | | } else { |
3494 | | $slug = $args['slug']; |
3495 | | } |
3496 | | |
3497 | | $parsed_args['slug'] = $slug; |
3498 | | |
3499 | | $term_group = isset( $parsed_args['term_group'] ) ? $parsed_args['term_group'] : 0; |
3500 | | if ( $args['alias_of'] ) { |
3501 | | $alias = get_term_by( 'slug', $args['alias_of'], $taxonomy ); |
3502 | | if ( ! empty( $alias->term_group ) ) { |
3503 | | // The alias we want is already in a group, so let's use that one. |
3504 | | $term_group = $alias->term_group; |
3505 | | } elseif ( ! empty( $alias->term_id ) ) { |
3506 | | /* |
3507 | | * The alias is not in a group, so we create a new one |
3508 | | * and add the alias to it. |
3509 | | */ |
3510 | | $term_group = $wpdb->get_var("SELECT MAX(term_group) FROM $wpdb->terms") + 1; |
3511 | | |
3512 | | wp_update_term( $alias->term_id, $taxonomy, array( |
3513 | | 'term_group' => $term_group, |
3514 | | ) ); |
3515 | | } |
3516 | | |
3517 | | $parsed_args['term_group'] = $term_group; |
3518 | | } |
3519 | | |
3520 | | /** |
3521 | | * Filter the term parent. |
3522 | | * |
3523 | | * Hook to this filter to see if it will cause a hierarchy loop. |
3524 | | * |
3525 | | * @since 3.1.0 |
3526 | | * |
3527 | | * @param int $parent ID of the parent term. |
3528 | | * @param int $term_id Term ID. |
3529 | | * @param string $taxonomy Taxonomy slug. |
3530 | | * @param array $parsed_args An array of potentially altered update arguments for the given term. |
3531 | | * @param array $args An array of update arguments for the given term. |
3532 | | */ |
3533 | | $parent = apply_filters( 'wp_update_term_parent', $args['parent'], $term_id, $taxonomy, $parsed_args, $args ); |
3534 | | |
3535 | | // Check for duplicate slug |
3536 | | $duplicate = get_term_by( 'slug', $slug, $taxonomy ); |
3537 | | if ( $duplicate && $duplicate->term_id != $term_id ) { |
3538 | | // If an empty slug was passed or the parent changed, reset the slug to something unique. |
3539 | | // Otherwise, bail. |
3540 | | if ( $empty_slug || ( $parent != $term['parent']) ) |
3541 | | $slug = wp_unique_term_slug($slug, (object) $args); |
3542 | | else |
3543 | | return new WP_Error('duplicate_term_slug', sprintf(__('The slug “%s” is already in use by another term'), $slug)); |
3544 | | } |
3545 | | |
3546 | | $tt_id = (int) $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) ); |
3547 | | |
3548 | | // Check whether this is a shared term that needs splitting. |
3549 | | $_term_id = _split_shared_term( $term_id, $tt_id ); |
3550 | | if ( ! is_wp_error( $_term_id ) ) { |
3551 | | $term_id = $_term_id; |
3552 | | } |
3553 | | |
3554 | | /** |
3555 | | * Fires immediately before the given terms are edited. |
3556 | | * |
3557 | | * @since 2.9.0 |
3558 | | * |
3559 | | * @param int $term_id Term ID. |
3560 | | * @param string $taxonomy Taxonomy slug. |
3561 | | */ |
3562 | | do_action( 'edit_terms', $term_id, $taxonomy ); |
3563 | | $wpdb->update($wpdb->terms, compact( 'name', 'slug', 'term_group' ), compact( 'term_id' ) ); |
3564 | | if ( empty($slug) ) { |
3565 | | $slug = sanitize_title($name, $term_id); |
3566 | | $wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) ); |
3567 | | } |
3568 | | |
3569 | | /** |
3570 | | * Fires immediately after the given terms are edited. |
3571 | | * |
3572 | | * @since 2.9.0 |
3573 | | * |
3574 | | * @param int $term_id Term ID |
3575 | | * @param string $taxonomy Taxonomy slug. |
3576 | | */ |
3577 | | do_action( 'edited_terms', $term_id, $taxonomy ); |
3578 | | |
3579 | | /** |
3580 | | * Fires immediate before a term-taxonomy relationship is updated. |
3581 | | * |
3582 | | * @since 2.9.0 |
3583 | | * |
3584 | | * @param int $tt_id Term taxonomy ID. |
3585 | | * @param string $taxonomy Taxonomy slug. |
3586 | | */ |
3587 | | do_action( 'edit_term_taxonomy', $tt_id, $taxonomy ); |
3588 | | |
3589 | | $wpdb->update( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent' ), array( 'term_taxonomy_id' => $tt_id ) ); |
3590 | | |
3591 | | /** |
3592 | | * Fires immediately after a term-taxonomy relationship is updated. |
3593 | | * |
3594 | | * @since 2.9.0 |
3595 | | * |
3596 | | * @param int $tt_id Term taxonomy ID. |
3597 | | * @param string $taxonomy Taxonomy slug. |
3598 | | */ |
3599 | | do_action( 'edited_term_taxonomy', $tt_id, $taxonomy ); |
3600 | | |
3601 | | // Clean the relationship caches for all object types using this term. |
3602 | | $objects = $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $tt_id ) ); |
3603 | | $tax_object = get_taxonomy( $taxonomy ); |
3604 | | foreach ( $tax_object->object_type as $object_type ) { |
3605 | | clean_object_term_cache( $objects, $object_type ); |
3606 | | } |
3607 | | |
3608 | | /** |
3609 | | * Fires after a term has been updated, but before the term cache has been cleaned. |
3610 | | * |
3611 | | * @since 2.3.0 |
3612 | | * |
3613 | | * @param int $term_id Term ID. |
3614 | | * @param int $tt_id Term taxonomy ID. |
3615 | | * @param string $taxonomy Taxonomy slug. |
3616 | | */ |
3617 | | do_action( "edit_term", $term_id, $tt_id, $taxonomy ); |
3618 | | |
3619 | | /** |
3620 | | * Fires after a term in a specific taxonomy has been updated, but before the term |
3621 | | * cache has been cleaned. |
3622 | | * |
3623 | | * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug. |
3624 | | * |
3625 | | * @since 2.3.0 |
3626 | | * |
3627 | | * @param int $term_id Term ID. |
3628 | | * @param int $tt_id Term taxonomy ID. |
3629 | | */ |
3630 | | do_action( "edit_$taxonomy", $term_id, $tt_id ); |
3631 | | |
3632 | | /** This filter is documented in wp-includes/taxonomy.php */ |
3633 | | $term_id = apply_filters( 'term_id_filter', $term_id, $tt_id ); |
3634 | | |
3635 | | clean_term_cache($term_id, $taxonomy); |
3636 | | |
3637 | | /** |
3638 | | * Fires after a term has been updated, and the term cache has been cleaned. |
3639 | | * |
3640 | | * @since 2.3.0 |
3641 | | * |
3642 | | * @param int $term_id Term ID. |
3643 | | * @param int $tt_id Term taxonomy ID. |
3644 | | * @param string $taxonomy Taxonomy slug. |
3645 | | */ |
3646 | | do_action( "edited_term", $term_id, $tt_id, $taxonomy ); |
3647 | | |
3648 | | /** |
3649 | | * Fires after a term for a specific taxonomy has been updated, and the term |
3650 | | * cache has been cleaned. |
3651 | | * |
3652 | | * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug. |
3653 | | * |
3654 | | * @since 2.3.0 |
3655 | | * |
3656 | | * @param int $term_id Term ID. |
3657 | | * @param int $tt_id Term taxonomy ID. |
3658 | | */ |
3659 | | do_action( "edited_$taxonomy", $term_id, $tt_id ); |
3660 | | |
3661 | | return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); |
3662 | | } |
3663 | | |
3664 | | /** |
3665 | | * Enable or disable term counting. |
3666 | | * |
3667 | | * @since 2.5.0 |
3668 | | * |
3669 | | * @staticvar bool $_defer |
3670 | | * |
3671 | | * @param bool $defer Optional. Enable if true, disable if false. |
3672 | | * @return bool Whether term counting is enabled or disabled. |
3673 | | */ |
3674 | | function wp_defer_term_counting($defer=null) { |
3675 | | static $_defer = false; |
3676 | | |
3677 | | if ( is_bool($defer) ) { |
3678 | | $_defer = $defer; |
3679 | | // flush any deferred counts |
3680 | | if ( !$defer ) |
3681 | | wp_update_term_count( null, null, true ); |
3682 | | } |
3683 | | |
3684 | | return $_defer; |
3685 | | } |
3686 | | |
3687 | | /** |
3688 | | * Updates the amount of terms in taxonomy. |
3689 | | * |
3690 | | * If there is a taxonomy callback applied, then it will be called for updating |
3691 | | * the count. |
3692 | | * |
3693 | | * The default action is to count what the amount of terms have the relationship |
3694 | | * of term ID. Once that is done, then update the database. |
3695 | | * |
3696 | | * @since 2.3.0 |
3697 | | * |
3698 | | * @staticvar array $_deferred |
3699 | | * |
3700 | | * @param int|array $terms The term_taxonomy_id of the terms. |
3701 | | * @param string $taxonomy The context of the term. |
3702 | | * @return bool If no terms will return false, and if successful will return true. |
3703 | | */ |
3704 | | function wp_update_term_count( $terms, $taxonomy, $do_deferred=false ) { |
3705 | | static $_deferred = array(); |
3706 | | |
3707 | | if ( $do_deferred ) { |
3708 | | foreach ( (array) array_keys($_deferred) as $tax ) { |
3709 | | wp_update_term_count_now( $_deferred[$tax], $tax ); |
3710 | | unset( $_deferred[$tax] ); |
3711 | | } |
3712 | | } |
3713 | | |
3714 | | if ( empty($terms) ) |
3715 | | return false; |
3716 | | |
3717 | | if ( !is_array($terms) ) |
3718 | | $terms = array($terms); |
3719 | | |
3720 | | if ( wp_defer_term_counting() ) { |
3721 | | if ( !isset($_deferred[$taxonomy]) ) |
3722 | | $_deferred[$taxonomy] = array(); |
3723 | | $_deferred[$taxonomy] = array_unique( array_merge($_deferred[$taxonomy], $terms) ); |
3724 | | return true; |
3725 | | } |
3726 | | |
3727 | | return wp_update_term_count_now( $terms, $taxonomy ); |
3728 | | } |
3729 | | |
3730 | | /** |
3731 | | * Perform term count update immediately. |
3732 | | * |
3733 | | * @since 2.5.0 |
3734 | | * |
3735 | | * @param array $terms The term_taxonomy_id of terms to update. |
3736 | | * @param string $taxonomy The context of the term. |
3737 | | * @return true Always true when complete. |
3738 | | */ |
3739 | | function wp_update_term_count_now( $terms, $taxonomy ) { |
3740 | | $terms = array_map('intval', $terms); |
3741 | | |
3742 | | $taxonomy = get_taxonomy($taxonomy); |
3743 | | if ( !empty($taxonomy->update_count_callback) ) { |
3744 | | call_user_func($taxonomy->update_count_callback, $terms, $taxonomy); |
3745 | | } else { |
3746 | | $object_types = (array) $taxonomy->object_type; |
3747 | | foreach ( $object_types as &$object_type ) { |
3748 | | if ( 0 === strpos( $object_type, 'attachment:' ) ) |
3749 | | list( $object_type ) = explode( ':', $object_type ); |
3750 | | } |
3751 | | |
3752 | | if ( $object_types == array_filter( $object_types, 'post_type_exists' ) ) { |
3753 | | // Only post types are attached to this taxonomy |
3754 | | _update_post_term_count( $terms, $taxonomy ); |
3755 | | } else { |
3756 | | // Default count updater |
3757 | | _update_generic_term_count( $terms, $taxonomy ); |
3758 | | } |
3759 | | } |
3760 | | |
3761 | | clean_term_cache($terms, '', false); |
3762 | | |
3763 | | return true; |
3764 | | } |
3765 | | |
3766 | | // |
3767 | | // Cache |
3768 | | // |
3769 | | |
3770 | | /** |
3771 | | * Removes the taxonomy relationship to terms from the cache. |
3772 | | * |
3773 | | * Will remove the entire taxonomy relationship containing term `$object_id`. The |
3774 | | * term IDs have to exist within the taxonomy `$object_type` for the deletion to |
3775 | | * take place. |
3776 | | * |
3777 | | * @since 2.3.0 |
3778 | | * |
3779 | | * @see get_object_taxonomies() for more on $object_type. |
3780 | | * |
3781 | | * @param int|array $object_ids Single or list of term object ID(s). |
3782 | | * @param array|string $object_type The taxonomy object type. |
3783 | | */ |
3784 | | function clean_object_term_cache($object_ids, $object_type) { |
3785 | | if ( !is_array($object_ids) ) |
3786 | | $object_ids = array($object_ids); |
3787 | | |
3788 | | $taxonomies = get_object_taxonomies( $object_type ); |
3789 | | |
3790 | | foreach ( $object_ids as $id ) { |
3791 | | foreach ( $taxonomies as $taxonomy ) { |
3792 | | wp_cache_delete($id, "{$taxonomy}_relationships"); |
3793 | | } |
3794 | | } |
3795 | | |
3796 | | /** |
3797 | | * Fires after the object term cache has been cleaned. |
3798 | | * |
3799 | | * @since 2.5.0 |
3800 | | * |
3801 | | * @param array $object_ids An array of object IDs. |
3802 | | * @param string $objet_type Object type. |
3803 | | */ |
3804 | | do_action( 'clean_object_term_cache', $object_ids, $object_type ); |
3805 | | } |
3806 | | |
3807 | | /** |
3808 | | * Will remove all of the term ids from the cache. |
3809 | | * |
3810 | | * @since 2.3.0 |
3811 | | * |
3812 | | * @global wpdb $wpdb WordPress database abstraction object. |
3813 | | * @global bool $_wp_suspend_cache_invalidation |
3814 | | * |
3815 | | * @param int|array $ids Single or list of Term IDs. |
3816 | | * @param string $taxonomy Optional. Can be empty and will assume `tt_ids`, else will use for context. |
3817 | | * Default empty. |
3818 | | * @param bool $clean_taxonomy Optional. Whether to clean taxonomy wide caches (true), or just individual |
3819 | | * term object caches (false). Default true. |
3820 | | */ |
3821 | | function clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true) { |
3822 | | global $wpdb, $_wp_suspend_cache_invalidation; |
3823 | | |
3824 | | if ( ! empty( $_wp_suspend_cache_invalidation ) ) { |
3825 | | return; |
3826 | | } |
3827 | | |
3828 | | if ( !is_array($ids) ) |
3829 | | $ids = array($ids); |
3830 | | |
3831 | | $taxonomies = array(); |
3832 | | // If no taxonomy, assume tt_ids. |
3833 | | if ( empty($taxonomy) ) { |
3834 | | $tt_ids = array_map('intval', $ids); |
3835 | | $tt_ids = implode(', ', $tt_ids); |
3836 | | $terms = $wpdb->get_results("SELECT term_id, taxonomy FROM $wpdb->term_taxonomy WHERE term_taxonomy_id IN ($tt_ids)"); |
3837 | | $ids = array(); |
3838 | | foreach ( (array) $terms as $term ) { |
3839 | | $taxonomies[] = $term->taxonomy; |
3840 | | $ids[] = $term->term_id; |
3841 | | wp_cache_delete($term->term_id, $term->taxonomy); |
3842 | | } |
3843 | | $taxonomies = array_unique($taxonomies); |
3844 | | } else { |
3845 | | $taxonomies = array($taxonomy); |
3846 | | foreach ( $taxonomies as $taxonomy ) { |
3847 | | foreach ( $ids as $id ) { |
3848 | | wp_cache_delete($id, $taxonomy); |
3849 | | } |
3850 | | } |
3851 | | } |
3852 | | |
3853 | | foreach ( $taxonomies as $taxonomy ) { |
3854 | | if ( $clean_taxonomy ) { |
3855 | | wp_cache_delete('all_ids', $taxonomy); |
3856 | | wp_cache_delete('get', $taxonomy); |
3857 | | delete_option("{$taxonomy}_children"); |
3858 | | // Regenerate {$taxonomy}_children |
3859 | | _get_term_hierarchy($taxonomy); |
3860 | | } |
3861 | | |
3862 | | /** |
3863 | | * Fires once after each taxonomy's term cache has been cleaned. |
3864 | | * |
3865 | | * @since 2.5.0 |
3866 | | * |
3867 | | * @param array $ids An array of term IDs. |
3868 | | * @param string $taxonomy Taxonomy slug. |
3869 | | */ |
3870 | | do_action( 'clean_term_cache', $ids, $taxonomy ); |
3871 | | } |
3872 | | |
3873 | | wp_cache_set( 'last_changed', microtime(), 'terms' ); |
3874 | | } |
3875 | | |
3876 | | /** |
3877 | | * Retrieves the taxonomy relationship to the term object id. |
3878 | | * |
3879 | | * @since 2.3.0 |
3880 | | * |
3881 | | * @param int $id Term object ID. |
3882 | | * @param string $taxonomy Taxonomy name. |
3883 | | * @return bool|mixed Empty array if $terms found, but not `$taxonomy`. False if nothing is in cache |
3884 | | * for `$taxonomy` and `$id`. |
3885 | | */ |
3886 | | function get_object_term_cache( $id, $taxonomy ) { |
3887 | | return wp_cache_get( $id, "{$taxonomy}_relationships" ); |
3888 | | } |
3889 | | |
3890 | | /** |
3891 | | * Updates the cache for the given term object ID(s). |
3892 | | * |
3893 | | * Note: Due to performance concerns, great care should be taken to only update |
3894 | | * term caches when necessary. Processing time can increase exponentially depending |
3895 | | * on both the number of passed term IDs and the number of taxonomies those terms |
3896 | | * belong to. |
3897 | | * |
3898 | | * Caches will only be updated for terms not already cached. |
3899 | | * |
3900 | | * @since 2.3.0 |
3901 | | * |
3902 | | * @param string|array $object_ids Comma-separated list or array of term object IDs. |
3903 | | * @param array|string $object_type The taxonomy object type. |
3904 | | * @return void|false False if all of the terms in `$object_ids` are already cached. |
3905 | | */ |
3906 | | function update_object_term_cache($object_ids, $object_type) { |
3907 | | if ( empty($object_ids) ) |
3908 | | return; |
3909 | | |
3910 | | if ( !is_array($object_ids) ) |
3911 | | $object_ids = explode(',', $object_ids); |
3912 | | |
3913 | | $object_ids = array_map('intval', $object_ids); |
3914 | | |
3915 | | $taxonomies = get_object_taxonomies($object_type); |
3916 | | |
3917 | | $ids = array(); |
3918 | | foreach ( (array) $object_ids as $id ) { |
3919 | | foreach ( $taxonomies as $taxonomy ) { |
3920 | | if ( false === wp_cache_get($id, "{$taxonomy}_relationships") ) { |
3921 | | $ids[] = $id; |
3922 | | break; |
3923 | | } |
3924 | | } |
3925 | | } |
3926 | | |
3927 | | if ( empty( $ids ) ) |
3928 | | return false; |
3929 | | |
3930 | | $terms = wp_get_object_terms($ids, $taxonomies, array('fields' => 'all_with_object_id')); |
3931 | | |
3932 | | $object_terms = array(); |
3933 | | foreach ( (array) $terms as $term ) |
3934 | | $object_terms[$term->object_id][$term->taxonomy][] = $term; |
3935 | | |
3936 | | foreach ( $ids as $id ) { |
3937 | | foreach ( $taxonomies as $taxonomy ) { |
3938 | | if ( ! isset($object_terms[$id][$taxonomy]) ) { |
3939 | | if ( !isset($object_terms[$id]) ) |
3940 | | $object_terms[$id] = array(); |
3941 | | $object_terms[$id][$taxonomy] = array(); |
3942 | | } |
3943 | | } |
3944 | | } |
3945 | | |
3946 | | foreach ( $object_terms as $id => $value ) { |
3947 | | foreach ( $value as $taxonomy => $terms ) { |
3948 | | wp_cache_add( $id, $terms, "{$taxonomy}_relationships" ); |
3949 | | } |
3950 | | } |
3951 | | } |
3952 | | |
3953 | | /** |
3954 | | * Updates Terms to Taxonomy in cache. |
3955 | | * |
3956 | | * @since 2.3.0 |
3957 | | * |
3958 | | * @param array $terms List of term objects to change. |
3959 | | * @param string $taxonomy Optional. Update Term to this taxonomy in cache. Default empty. |
3960 | | */ |
3961 | | function update_term_cache( $terms, $taxonomy = '' ) { |
3962 | | foreach ( (array) $terms as $term ) { |
3963 | | $term_taxonomy = $taxonomy; |
3964 | | if ( empty($term_taxonomy) ) |
3965 | | $term_taxonomy = $term->taxonomy; |
3966 | | |
3967 | | wp_cache_add( $term->term_id, $term, $term_taxonomy ); |
3968 | | } |
3969 | | } |
3970 | | |
3971 | | // |
3972 | | // Private |
3973 | | // |
3974 | | |
3975 | | /** |
3976 | | * Retrieves children of taxonomy as Term IDs. |
3977 | | * |
3978 | | * @ignore |
3979 | | * @since 2.3.0 |
3980 | | * |
3981 | | * @param string $taxonomy Taxonomy name. |
3982 | | * @return array Empty if $taxonomy isn't hierarchical or returns children as Term IDs. |
3983 | | */ |
3984 | | function _get_term_hierarchy( $taxonomy ) { |
3985 | | if ( !is_taxonomy_hierarchical($taxonomy) ) |
3986 | | return array(); |
3987 | | $children = get_option("{$taxonomy}_children"); |
3988 | | |
3989 | | if ( is_array($children) ) |
3990 | | return $children; |
3991 | | $children = array(); |
3992 | | $terms = get_terms($taxonomy, array('get' => 'all', 'orderby' => 'id', 'fields' => 'id=>parent')); |
3993 | | foreach ( $terms as $term_id => $parent ) { |
3994 | | if ( $parent > 0 ) |
3995 | | $children[$parent][] = $term_id; |
3996 | | } |
3997 | | update_option("{$taxonomy}_children", $children); |
3998 | | |
3999 | | return $children; |
4000 | | } |
4001 | | |
4002 | | /** |
4003 | | * Get the subset of $terms that are descendants of $term_id. |
4004 | | * |
4005 | | * If `$terms` is an array of objects, then _get_term_children() returns an array of objects. |
4006 | | * If `$terms` is an array of IDs, then _get_term_children() returns an array of IDs. |
4007 | | * |
4008 | | * @access private |
4009 | | * @since 2.3.0 |
4010 | | * |
4011 | | * @param int $term_id The ancestor term: all returned terms should be descendants of `$term_id`. |
4012 | | * @param array $terms The set of terms - either an array of term objects or term IDs - from which those that |
4013 | | * are descendants of $term_id will be chosen. |
4014 | | * @param string $taxonomy The taxonomy which determines the hierarchy of the terms. |
4015 | | * @param array $ancestors Optional. Term ancestors that have already been identified. Passed by reference, to keep |
4016 | | * track of found terms when recursing the hierarchy. The array of located ancestors is used |
4017 | | * to prevent infinite recursion loops. For performance, `term_ids` are used as array keys, |
4018 | | * with 1 as value. Default empty array. |
4019 | | * @return array|WP_Error The subset of $terms that are descendants of $term_id. |
4020 | | */ |
4021 | | function _get_term_children( $term_id, $terms, $taxonomy, &$ancestors = array() ) { |
4022 | | $empty_array = array(); |
4023 | | if ( empty($terms) ) |
4024 | | return $empty_array; |
4025 | | |
4026 | | $term_list = array(); |
4027 | | $has_children = _get_term_hierarchy($taxonomy); |
4028 | | |
4029 | | if ( ( 0 != $term_id ) && ! isset($has_children[$term_id]) ) |
4030 | | return $empty_array; |
4031 | | |
4032 | | // Include the term itself in the ancestors array, so we can properly detect when a loop has occurred. |
4033 | | if ( empty( $ancestors ) ) { |
4034 | | $ancestors[ $term_id ] = 1; |
4035 | | } |
4036 | | |
4037 | | foreach ( (array) $terms as $term ) { |
4038 | | $use_id = false; |
4039 | | if ( !is_object($term) ) { |
4040 | | $term = get_term($term, $taxonomy); |
4041 | | if ( is_wp_error( $term ) ) |
4042 | | return $term; |
4043 | | $use_id = true; |
4044 | | } |
4045 | | |
4046 | | // Don't recurse if we've already identified the term as a child - this indicates a loop. |
4047 | | if ( isset( $ancestors[ $term->term_id ] ) ) { |
4048 | | continue; |
4049 | | } |
4050 | | |
4051 | | if ( $term->parent == $term_id ) { |
4052 | | if ( $use_id ) |
4053 | | $term_list[] = $term->term_id; |
4054 | | else |
4055 | | $term_list[] = $term; |
4056 | | |
4057 | | if ( !isset($has_children[$term->term_id]) ) |
4058 | | continue; |
4059 | | |
4060 | | $ancestors[ $term->term_id ] = 1; |
4061 | | |
4062 | | if ( $children = _get_term_children( $term->term_id, $terms, $taxonomy, $ancestors) ) |
4063 | | $term_list = array_merge($term_list, $children); |
4064 | | } |
4065 | | } |
4066 | | |
4067 | | return $term_list; |
4068 | | } |
4069 | | |
4070 | | /** |
4071 | | * Add count of children to parent count. |
4072 | | * |
4073 | | * Recalculates term counts by including items from child terms. Assumes all |
4074 | | * relevant children are already in the $terms argument. |
4075 | | * |
4076 | | * @access private |
4077 | | * @since 2.3.0 |
4078 | | * |
4079 | | * @global wpdb $wpdb WordPress database abstraction object. |
4080 | | * |
4081 | | * @param array $terms List of term IDs, passed by reference. |
4082 | | * @param string $taxonomy Term context. |
4083 | | */ |
4084 | | function _pad_term_counts( &$terms, $taxonomy ) { |
4085 | | global $wpdb; |
4086 | | |
4087 | | // This function only works for hierarchical taxonomies like post categories. |
4088 | | if ( !is_taxonomy_hierarchical( $taxonomy ) ) |
4089 | | return; |
4090 | | |
4091 | | $term_hier = _get_term_hierarchy($taxonomy); |
4092 | | |
4093 | | if ( empty($term_hier) ) |
4094 | | return; |
4095 | | |
4096 | | $term_items = array(); |
4097 | | $terms_by_id = array(); |
4098 | | $term_ids = array(); |
4099 | | |
4100 | | foreach ( (array) $terms as $key => $term ) { |
4101 | | $terms_by_id[$term->term_id] = & $terms[$key]; |
4102 | | $term_ids[$term->term_taxonomy_id] = $term->term_id; |
4103 | | } |
4104 | | |
4105 | | // Get the object and term ids and stick them in a lookup table. |
4106 | | $tax_obj = get_taxonomy($taxonomy); |
4107 | | $object_types = esc_sql($tax_obj->object_type); |
4108 | | $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 (" . implode(',', array_keys($term_ids)) . ") AND post_type IN ('" . implode("', '", $object_types) . "') AND post_status = 'publish'"); |
4109 | | foreach ( $results as $row ) { |
4110 | | $id = $term_ids[$row->term_taxonomy_id]; |
4111 | | $term_items[$id][$row->object_id] = isset($term_items[$id][$row->object_id]) ? ++$term_items[$id][$row->object_id] : 1; |
4112 | | } |
4113 | | |
4114 | | // Touch every ancestor's lookup row for each post in each term. |
4115 | | foreach ( $term_ids as $term_id ) { |
4116 | | $child = $term_id; |
4117 | | $ancestors = array(); |
4118 | | while ( !empty( $terms_by_id[$child] ) && $parent = $terms_by_id[$child]->parent ) { |
4119 | | $ancestors[] = $child; |
4120 | | if ( !empty( $term_items[$term_id] ) ) |
4121 | | foreach ( $term_items[$term_id] as $item_id => $touches ) { |
4122 | | $term_items[$parent][$item_id] = isset($term_items[$parent][$item_id]) ? ++$term_items[$parent][$item_id]: 1; |
4123 | | } |
4124 | | $child = $parent; |
4125 | | |
4126 | | if ( in_array( $parent, $ancestors ) ) { |
4127 | | break; |
4128 | | } |
4129 | | } |
4130 | | } |
4131 | | |
4132 | | // Transfer the touched cells. |
4133 | | foreach ( (array) $term_items as $id => $items ) |
4134 | | if ( isset($terms_by_id[$id]) ) |
4135 | | $terms_by_id[$id]->count = count($items); |
4136 | | } |
4137 | | |
4138 | | // |
4139 | | // Default callbacks |
4140 | | // |
4141 | | |
4142 | | /** |
4143 | | * Will update term count based on object types of the current taxonomy. |
4144 | | * |
4145 | | * Private function for the default callback for post_tag and category |
4146 | | * taxonomies. |
4147 | | * |
4148 | | * @access private |
4149 | | * @since 2.3.0 |
4150 | | * |
4151 | | * @global wpdb $wpdb WordPress database abstraction object. |
4152 | | * |
4153 | | * @param array $terms List of Term taxonomy IDs. |
4154 | | * @param object $taxonomy Current taxonomy object of terms. |
4155 | | */ |
4156 | | function _update_post_term_count( $terms, $taxonomy ) { |
4157 | | global $wpdb; |
4158 | | |
4159 | | $object_types = (array) $taxonomy->object_type; |
4160 | | |
4161 | | foreach ( $object_types as &$object_type ) |
4162 | | list( $object_type ) = explode( ':', $object_type ); |
4163 | | |
4164 | | $object_types = array_unique( $object_types ); |
4165 | | |
4166 | | if ( false !== ( $check_attachments = array_search( 'attachment', $object_types ) ) ) { |
4167 | | unset( $object_types[ $check_attachments ] ); |
4168 | | $check_attachments = true; |
4169 | | } |
4170 | | |
4171 | | if ( $object_types ) |
4172 | | $object_types = esc_sql( array_filter( $object_types, 'post_type_exists' ) ); |
4173 | | |
4174 | | foreach ( (array) $terms as $term ) { |
4175 | | $count = 0; |
4176 | | |
4177 | | // Attachments can be 'inherit' status, we need to base count off the parent's status if so. |
4178 | | if ( $check_attachments ) |
4179 | | $count += (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts p1 WHERE p1.ID = $wpdb->term_relationships.object_id AND ( post_status = 'publish' OR ( post_status = 'inherit' AND post_parent > 0 AND ( SELECT post_status FROM $wpdb->posts WHERE ID = p1.post_parent ) = 'publish' ) ) AND post_type = 'attachment' AND term_taxonomy_id = %d", $term ) ); |
4180 | | |
4181 | | if ( $object_types ) |
4182 | | $count += (int) $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 IN ('" . implode("', '", $object_types ) . "') AND term_taxonomy_id = %d", $term ) ); |
4183 | | |
4184 | | /** This action is documented in wp-includes/taxonomy.php */ |
4185 | | do_action( 'edit_term_taxonomy', $term, $taxonomy->name ); |
4186 | | $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) ); |
4187 | | |
4188 | | /** This action is documented in wp-includes/taxonomy.php */ |
4189 | | do_action( 'edited_term_taxonomy', $term, $taxonomy->name ); |
4190 | | } |
4191 | | } |
4192 | | |
4193 | | /** |
4194 | | * Will update term count based on number of objects. |
4195 | | * |
4196 | | * Default callback for the 'link_category' taxonomy. |
4197 | | * |
4198 | | * @since 3.3.0 |
4199 | | * |
4200 | | * @global wpdb $wpdb WordPress database abstraction object. |
4201 | | * |
4202 | | * @param array $terms List of term taxonomy IDs. |
4203 | | * @param object $taxonomy Current taxonomy object of terms. |
4204 | | */ |
4205 | | function _update_generic_term_count( $terms, $taxonomy ) { |
4206 | | global $wpdb; |
4207 | | |
4208 | | foreach ( (array) $terms as $term ) { |
4209 | | $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $term ) ); |
4210 | | |
4211 | | /** This action is documented in wp-includes/taxonomy.php */ |
4212 | | do_action( 'edit_term_taxonomy', $term, $taxonomy->name ); |
4213 | | $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) ); |
4214 | | |
4215 | | /** This action is documented in wp-includes/taxonomy.php */ |
4216 | | do_action( 'edited_term_taxonomy', $term, $taxonomy->name ); |
4217 | | } |
4218 | | } |
4219 | | |
4220 | | /** |
4221 | | * Create a new term for a term_taxonomy item that currently shares its term |
4222 | | * with another term_taxonomy. |
4223 | | * |
4224 | | * @ignore |
4225 | | * @since 4.2.0 |
4226 | | * @since 4.3.0 Introduced `$record` parameter. Also, `$term_id` and |
4227 | | * `$term_taxonomy_id` can now accept objects. |
4228 | | * |
4229 | | * @global wpdb $wpdb |
4230 | | * |
4231 | | * @param int|object $term_id ID of the shared term, or the shared term object. |
4232 | | * @param int|object $term_taxonomy_id ID of the term_taxonomy item to receive a new term, or the term_taxonomy object |
4233 | | * (corresponding to a row from the term_taxonomy table). |
4234 | | * @param bool $record Whether to record data about the split term in the options table. The recording |
4235 | | * process has the potential to be resource-intensive, so during batch operations |
4236 | | * it can be beneficial to skip inline recording and do it just once, after the |
4237 | | * batch is processed. Only set this to `false` if you know what you are doing. |
4238 | | * Default: true. |
4239 | | * @return int|WP_Error When the current term does not need to be split (or cannot be split on the current |
4240 | | * database schema), `$term_id` is returned. When the term is successfully split, the |
4241 | | * new term_id is returned. A WP_Error is returned for miscellaneous errors. |
4242 | | */ |
4243 | | function _split_shared_term( $term_id, $term_taxonomy_id, $record = true ) { |
4244 | | global $wpdb; |
4245 | | |
4246 | | if ( is_object( $term_id ) ) { |
4247 | | $shared_term = $term_id; |
4248 | | $term_id = intval( $shared_term->term_id ); |
4249 | | } |
4250 | | |
4251 | | if ( is_object( $term_taxonomy_id ) ) { |
4252 | | $term_taxonomy = $term_taxonomy_id; |
4253 | | $term_taxonomy_id = intval( $term_taxonomy->term_taxonomy_id ); |
4254 | | } |
4255 | | |
4256 | | // If there are no shared term_taxonomy rows, there's nothing to do here. |
4257 | | $shared_tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy tt WHERE tt.term_id = %d AND tt.term_taxonomy_id != %d", $term_id, $term_taxonomy_id ) ); |
4258 | | |
4259 | | if ( ! $shared_tt_count ) { |
4260 | | return $term_id; |
4261 | | } |
4262 | | |
4263 | | /* |
4264 | | * Verify that the term_taxonomy_id passed to the function is actually associated with the term_id. |
4265 | | * If there's a mismatch, it may mean that the term is already split. Return the actual term_id from the db. |
4266 | | */ |
4267 | | $check_term_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) ); |
4268 | | if ( $check_term_id != $term_id ) { |
4269 | | return $check_term_id; |
4270 | | } |
4271 | | |
4272 | | // Pull up data about the currently shared slug, which we'll use to populate the new one. |
4273 | | if ( empty( $shared_term ) ) { |
4274 | | $shared_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.* FROM $wpdb->terms t WHERE t.term_id = %d", $term_id ) ); |
4275 | | } |
4276 | | |
4277 | | $new_term_data = array( |
4278 | | 'name' => $shared_term->name, |
4279 | | 'slug' => $shared_term->slug, |
4280 | | 'term_group' => $shared_term->term_group, |
4281 | | ); |
4282 | | |
4283 | | if ( false === $wpdb->insert( $wpdb->terms, $new_term_data ) ) { |
4284 | | return new WP_Error( 'db_insert_error', __( 'Could not split shared term.' ), $wpdb->last_error ); |
4285 | | } |
4286 | | |
4287 | | $new_term_id = (int) $wpdb->insert_id; |
4288 | | |
4289 | | // Update the existing term_taxonomy to point to the newly created term. |
4290 | | $wpdb->update( $wpdb->term_taxonomy, |
4291 | | array( 'term_id' => $new_term_id ), |
4292 | | array( 'term_taxonomy_id' => $term_taxonomy_id ) |
4293 | | ); |
4294 | | |
4295 | | // Reassign child terms to the new parent. |
4296 | | if ( empty( $term_taxonomy ) ) { |
4297 | | $term_taxonomy = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) ); |
4298 | | } |
4299 | | |
4300 | | $children_tt_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE parent = %d AND taxonomy = %s", $term_id, $term_taxonomy->taxonomy ) ); |
4301 | | if ( ! empty( $children_tt_ids ) ) { |
4302 | | foreach ( $children_tt_ids as $child_tt_id ) { |
4303 | | $wpdb->update( $wpdb->term_taxonomy, |
4304 | | array( 'parent' => $new_term_id ), |
4305 | | array( 'term_taxonomy_id' => $child_tt_id ) |
4306 | | ); |
4307 | | clean_term_cache( $term_id, $term_taxonomy->taxonomy ); |
4308 | | } |
4309 | | } else { |
4310 | | // If the term has no children, we must force its taxonomy cache to be rebuilt separately. |
4311 | | clean_term_cache( $new_term_id, $term_taxonomy->taxonomy ); |
4312 | | } |
4313 | | |
4314 | | // Clean the cache for term taxonomies formerly shared with the current term. |
4315 | | $shared_term_taxonomies = $wpdb->get_row( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) ); |
4316 | | if ( $shared_term_taxonomies ) { |
4317 | | foreach ( $shared_term_taxonomies as $shared_term_taxonomy ) { |
4318 | | clean_term_cache( $term_id, $shared_term_taxonomy ); |
4319 | | } |
4320 | | } |
4321 | | |
4322 | | // Keep a record of term_ids that have been split, keyed by old term_id. See {@see wp_get_split_term()}. |
4323 | | if ( $record ) { |
4324 | | $split_term_data = get_option( '_split_terms', array() ); |
4325 | | if ( ! isset( $split_term_data[ $term_id ] ) ) { |
4326 | | $split_term_data[ $term_id ] = array(); |
4327 | | } |
4328 | | |
4329 | | $split_term_data[ $term_id ][ $term_taxonomy->taxonomy ] = $new_term_id; |
4330 | | update_option( '_split_terms', $split_term_data ); |
4331 | | } |
4332 | | |
4333 | | /** |
4334 | | * Fires after a previously shared taxonomy term is split into two separate terms. |
4335 | | * |
4336 | | * @since 4.2.0 |
4337 | | * |
4338 | | * @param int $term_id ID of the formerly shared term. |
4339 | | * @param int $new_term_id ID of the new term created for the $term_taxonomy_id. |
4340 | | * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split. |
4341 | | * @param string $taxonomy Taxonomy for the split term. |
4342 | | */ |
4343 | | do_action( 'split_shared_term', $term_id, $new_term_id, $term_taxonomy_id, $term_taxonomy->taxonomy ); |
4344 | | |
4345 | | return $new_term_id; |
4346 | | } |
4347 | | |
4348 | | /** |
4349 | | * Splits a batch of shared taxonomy terms. |
4350 | | * |
4351 | | * @since 4.3.0 |
4352 | | * |
4353 | | * @global wpdb $wpdb WordPress database abstraction object. |
4354 | | */ |
4355 | | function _wp_batch_split_terms() { |
4356 | | global $wpdb; |
4357 | | |
4358 | | $lock_name = 'term_split.lock'; |
4359 | | |
4360 | | // Try to lock. |
4361 | | $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_name, time() ) ); |
4362 | | |
4363 | | if ( ! $lock_result ) { |
4364 | | $lock_result = get_option( $lock_name ); |
4365 | | |
4366 | | // Bail if we were unable to create a lock, or if the existing lock is still valid. |
4367 | | if ( ! $lock_result || ( $lock_result > ( time() - HOUR_IN_SECONDS ) ) ) { |
4368 | | wp_schedule_single_event( time() + ( 5 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' ); |
4369 | | return; |
4370 | | } |
4371 | | } |
4372 | | |
4373 | | // Update the lock, as by this point we've definitely got a lock, just need to fire the actions. |
4374 | | update_option( $lock_name, time() ); |
4375 | | |
4376 | | // Get a list of shared terms (those with more than one associated row in term_taxonomy). |
4377 | | $shared_terms = $wpdb->get_results( |
4378 | | "SELECT tt.term_id, t.*, count(*) as term_tt_count FROM {$wpdb->term_taxonomy} tt |
4379 | | LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id |
4380 | | GROUP BY t.term_id |
4381 | | HAVING term_tt_count > 1 |
4382 | | LIMIT 10" |
4383 | | ); |
4384 | | |
4385 | | // No more terms, we're done here. |
4386 | | if ( ! $shared_terms ) { |
4387 | | update_option( 'finished_splitting_shared_terms', true ); |
4388 | | delete_option( $lock_name ); |
4389 | | return; |
4390 | | } |
4391 | | |
4392 | | // Shared terms found? We'll need to run this script again. |
4393 | | wp_schedule_single_event( time() + ( 2 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' ); |
4394 | | |
4395 | | // Rekey shared term array for faster lookups. |
4396 | | $_shared_terms = array(); |
4397 | | foreach ( $shared_terms as $shared_term ) { |
4398 | | $term_id = intval( $shared_term->term_id ); |
4399 | | $_shared_terms[ $term_id ] = $shared_term; |
4400 | | } |
4401 | | $shared_terms = $_shared_terms; |
4402 | | |
4403 | | // Get term taxonomy data for all shared terms. |
4404 | | $shared_term_ids = implode( ',', array_keys( $shared_terms ) ); |
4405 | | $shared_tts = $wpdb->get_results( "SELECT * FROM {$wpdb->term_taxonomy} WHERE `term_id` IN ({$shared_term_ids})" ); |
4406 | | |
4407 | | // Split term data recording is slow, so we do it just once, outside the loop. |
4408 | | $split_term_data = get_option( '_split_terms', array() ); |
4409 | | $skipped_first_term = $taxonomies = array(); |
4410 | | foreach ( $shared_tts as $shared_tt ) { |
4411 | | $term_id = intval( $shared_tt->term_id ); |
4412 | | |
4413 | | // Don't split the first tt belonging to a given term_id. |
4414 | | if ( ! isset( $skipped_first_term[ $term_id ] ) ) { |
4415 | | $skipped_first_term[ $term_id ] = 1; |
4416 | | continue; |
4417 | | } |
4418 | | |
4419 | | if ( ! isset( $split_term_data[ $term_id ] ) ) { |
4420 | | $split_term_data[ $term_id ] = array(); |
4421 | | } |
4422 | | |
4423 | | // Keep track of taxonomies whose hierarchies need flushing. |
4424 | | if ( ! isset( $taxonomies[ $shared_tt->taxonomy ] ) ) { |
4425 | | $taxonomies[ $shared_tt->taxonomy ] = 1; |
4426 | | } |
4427 | | |
4428 | | // Split the term. |
4429 | | $split_term_data[ $term_id ][ $shared_tt->taxonomy ] = _split_shared_term( $shared_terms[ $term_id ], $shared_tt, false ); |
4430 | | } |
4431 | | |
4432 | | // Rebuild the cached hierarchy for each affected taxonomy. |
4433 | | foreach ( array_keys( $taxonomies ) as $tax ) { |
4434 | | delete_option( "{$tax}_children" ); |
4435 | | _get_term_hierarchy( $tax ); |
4436 | | } |
4437 | | |
4438 | | update_option( '_split_terms', $split_term_data ); |
4439 | | |
4440 | | delete_option( $lock_name ); |
4441 | | } |
4442 | | |
4443 | | /** |
4444 | | * In order to avoid the _wp_batch_split_terms() job being accidentally removed, |
4445 | | * check that it's still scheduled while we haven't finished splitting terms. |
4446 | | * |
4447 | | * @ignore |
4448 | | * @since 4.3.0 |
4449 | | */ |
4450 | | function _wp_check_for_scheduled_split_terms() { |
4451 | | if ( ! get_option( 'finished_splitting_shared_terms' ) && ! wp_next_scheduled( 'wp_split_shared_term_batch' ) ) { |
4452 | | wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'wp_split_shared_term_batch' ); |
4453 | | } |
4454 | | } |
4455 | | |
4456 | | /** |
4457 | | * Check default categories when a term gets split to see if any of them need to be updated. |
4458 | | * |
4459 | | * @ignore |
4460 | | * @since 4.2.0 |
4461 | | * |
4462 | | * @param int $term_id ID of the formerly shared term. |
4463 | | * @param int $new_term_id ID of the new term created for the $term_taxonomy_id. |
4464 | | * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split. |
4465 | | * @param string $taxonomy Taxonomy for the split term. |
4466 | | */ |
4467 | | function _wp_check_split_default_terms( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) { |
4468 | | if ( 'category' != $taxonomy ) { |
4469 | | return; |
4470 | | } |
4471 | | |
4472 | | foreach ( array( 'default_category', 'default_link_category', 'default_email_category' ) as $option ) { |
4473 | | if ( $term_id == get_option( $option, -1 ) ) { |
4474 | | update_option( $option, $new_term_id ); |
4475 | | } |
4476 | | } |
4477 | | } |
4478 | | |
4479 | | /** |
4480 | | * Check menu items when a term gets split to see if any of them need to be updated. |
4481 | | * |
4482 | | * @ignore |
4483 | | * @since 4.2.0 |
4484 | | * |
4485 | | * @global wpdb $wpdb |
4486 | | * |
4487 | | * @param int $term_id ID of the formerly shared term. |
4488 | | * @param int $new_term_id ID of the new term created for the $term_taxonomy_id. |
4489 | | * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split. |
4490 | | * @param string $taxonomy Taxonomy for the split term. |
4491 | | */ |
4492 | | function _wp_check_split_terms_in_menus( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) { |
4493 | | global $wpdb; |
4494 | | $post_ids = $wpdb->get_col( $wpdb->prepare( |
4495 | | "SELECT m1.post_id |
4496 | | FROM {$wpdb->postmeta} AS m1 |
4497 | | INNER JOIN {$wpdb->postmeta} AS m2 ON ( m2.post_id = m1.post_id ) |
4498 | | INNER JOIN {$wpdb->postmeta} AS m3 ON ( m3.post_id = m1.post_id ) |
4499 | | WHERE ( m1.meta_key = '_menu_item_type' AND m1.meta_value = 'taxonomy' ) |
4500 | | AND ( m2.meta_key = '_menu_item_object' AND m2.meta_value = '%s' ) |
4501 | | AND ( m3.meta_key = '_menu_item_object_id' AND m3.meta_value = %d )", |
4502 | | $taxonomy, |
4503 | | $term_id |
4504 | | ) ); |
4505 | | |
4506 | | if ( $post_ids ) { |
4507 | | foreach ( $post_ids as $post_id ) { |
4508 | | update_post_meta( $post_id, '_menu_item_object_id', $new_term_id, $term_id ); |
4509 | | } |
4510 | | } |
4511 | | } |
4512 | | |
4513 | | /** |
4514 | | * If the term being split is a nav_menu, change associations. |
4515 | | * |
4516 | | * @ignore |
4517 | | * @since 4.3.0 |
4518 | | * |
4519 | | * @global wpdb $wpdb |
4520 | | * |
4521 | | * @param int $term_id ID of the formerly shared term. |
4522 | | * @param int $new_term_id ID of the new term created for the $term_taxonomy_id. |
4523 | | * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split. |
4524 | | * @param string $taxonomy Taxonomy for the split term. |
4525 | | */ |
4526 | | function _wp_check_split_nav_menu_terms( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) { |
4527 | | if ( 'nav_menu' !== $taxonomy ) { |
4528 | | return; |
4529 | | } |
4530 | | |
4531 | | // Update menu locations. |
4532 | | $locations = get_nav_menu_locations(); |
4533 | | foreach ( $locations as $location => $menu_id ) { |
4534 | | if ( $term_id == $menu_id ) { |
4535 | | $locations[ $location ] = $new_term_id; |
4536 | | } |
4537 | | } |
4538 | | set_theme_mod( 'nav_menu_locations', $locations ); |
4539 | | } |
4540 | | |
4541 | | /** |
4542 | | * Get data about terms that previously shared a single term_id, but have since been split. |
4543 | | * |
4544 | | * @since 4.2.0 |
4545 | | * |
4546 | | * @param int $old_term_id Term ID. This is the old, pre-split term ID. |
4547 | | * @return array Array of new term IDs, keyed by taxonomy. |
4548 | | */ |
4549 | | function wp_get_split_terms( $old_term_id ) { |
4550 | | $split_terms = get_option( '_split_terms', array() ); |
4551 | | |
4552 | | $terms = array(); |
4553 | | if ( isset( $split_terms[ $old_term_id ] ) ) { |
4554 | | $terms = $split_terms[ $old_term_id ]; |
4555 | | } |
4556 | | |
4557 | | return $terms; |
4558 | | } |
4559 | | |
4560 | | /** |
4561 | | * Get the new term ID corresponding to a previously split term. |
4562 | | * |
4563 | | * @since 4.2.0 |
4564 | | * |
4565 | | * @param int $old_term_id Term ID. This is the old, pre-split term ID. |
4566 | | * @param string $taxonomy Taxonomy that the term belongs to. |
4567 | | * @return int|false If a previously split term is found corresponding to the old term_id and taxonomy, |
4568 | | * the new term_id will be returned. If no previously split term is found matching |
4569 | | * the parameters, returns false. |
4570 | | */ |
4571 | | function wp_get_split_term( $old_term_id, $taxonomy ) { |
4572 | | $split_terms = wp_get_split_terms( $old_term_id ); |
4573 | | |
4574 | | $term_id = false; |
4575 | | if ( isset( $split_terms[ $taxonomy ] ) ) { |
4576 | | $term_id = (int) $split_terms[ $taxonomy ]; |
4577 | | } |
4578 | | |
4579 | | return $term_id; |
4580 | | } |
4581 | | |
4582 | | /** |
4583 | | * Generate a permalink for a taxonomy term archive. |
4584 | | * |
4585 | | * @since 2.5.0 |
4586 | | * |
4587 | | * @global WP_Rewrite $wp_rewrite |
4588 | | * |
4589 | | * @param object|int|string $term The term object, ID, or slug whose link will be retrieved. |
4590 | | * @param string $taxonomy Optional. Taxonomy. Default empty. |
4591 | | * @return string|WP_Error HTML link to taxonomy term archive on success, WP_Error if term does not exist. |
4592 | | */ |
4593 | | function get_term_link( $term, $taxonomy = '' ) { |
4594 | | global $wp_rewrite; |
4595 | | |
4596 | | if ( !is_object($term) ) { |
4597 | | if ( is_int( $term ) ) { |
4598 | | $term = get_term( $term, $taxonomy ); |
4599 | | } else { |
4600 | | $term = get_term_by( 'slug', $term, $taxonomy ); |
4601 | | } |
4602 | | } |
4603 | | |
4604 | | if ( !is_object($term) ) |
4605 | | $term = new WP_Error('invalid_term', __('Empty Term')); |
4606 | | |
4607 | | if ( is_wp_error( $term ) ) |
4608 | | return $term; |
4609 | | |
4610 | | $taxonomy = $term->taxonomy; |
4611 | | |
4612 | | $termlink = $wp_rewrite->get_extra_permastruct($taxonomy); |
4613 | | |
4614 | | $slug = $term->slug; |
4615 | | $t = get_taxonomy($taxonomy); |
4616 | | |
4617 | | if ( empty($termlink) ) { |
4618 | | if ( 'category' == $taxonomy ) |
4619 | | $termlink = '?cat=' . $term->term_id; |
4620 | | elseif ( $t->query_var ) |
4621 | | $termlink = "?$t->query_var=$slug"; |
4622 | | else |
4623 | | $termlink = "?taxonomy=$taxonomy&term=$slug"; |
4624 | | $termlink = home_url($termlink); |
4625 | | } else { |
4626 | | if ( $t->rewrite['hierarchical'] ) { |
4627 | | $hierarchical_slugs = array(); |
4628 | | $ancestors = get_ancestors( $term->term_id, $taxonomy, 'taxonomy' ); |
4629 | | foreach ( (array)$ancestors as $ancestor ) { |
4630 | | $ancestor_term = get_term($ancestor, $taxonomy); |
4631 | | $hierarchical_slugs[] = $ancestor_term->slug; |
4632 | | } |
4633 | | $hierarchical_slugs = array_reverse($hierarchical_slugs); |
4634 | | $hierarchical_slugs[] = $slug; |
4635 | | $termlink = str_replace("%$taxonomy%", implode('/', $hierarchical_slugs), $termlink); |
4636 | | } else { |
4637 | | $termlink = str_replace("%$taxonomy%", $slug, $termlink); |
4638 | | } |
4639 | | $termlink = home_url( user_trailingslashit($termlink, 'category') ); |
4640 | | } |
4641 | | // Back Compat filters. |
4642 | | if ( 'post_tag' == $taxonomy ) { |
4643 | | |
4644 | | /** |
4645 | | * Filter the tag link. |
4646 | | * |
4647 | | * @since 2.3.0 |
4648 | | * @deprecated 2.5.0 Use 'term_link' instead. |
4649 | | * |
4650 | | * @param string $termlink Tag link URL. |
4651 | | * @param int $term_id Term ID. |
4652 | | */ |
4653 | | $termlink = apply_filters( 'tag_link', $termlink, $term->term_id ); |
4654 | | } elseif ( 'category' == $taxonomy ) { |
4655 | | |
4656 | | /** |
4657 | | * Filter the category link. |
4658 | | * |
4659 | | * @since 1.5.0 |
4660 | | * @deprecated 2.5.0 Use 'term_link' instead. |
4661 | | * |
4662 | | * @param string $termlink Category link URL. |
4663 | | * @param int $term_id Term ID. |
4664 | | */ |
4665 | | $termlink = apply_filters( 'category_link', $termlink, $term->term_id ); |
4666 | | } |
4667 | | |
4668 | | /** |
4669 | | * Filter the term link. |
4670 | | * |
4671 | | * @since 2.5.0 |
4672 | | * |
4673 | | * @param string $termlink Term link URL. |
4674 | | * @param object $term Term object. |
4675 | | * @param string $taxonomy Taxonomy slug. |
4676 | | */ |
4677 | | return apply_filters( 'term_link', $termlink, $term, $taxonomy ); |
4678 | | } |
4679 | | |
4680 | | /** |
4681 | | * Display the taxonomies of a post with available options. |
4682 | | * |
4683 | | * This function can be used within the loop to display the taxonomies for a |
4684 | | * post without specifying the Post ID. You can also use it outside the Loop to |
4685 | | * display the taxonomies for a specific post. |
4686 | | * |
4687 | | * @since 2.5.0 |
4688 | | * |
4689 | | * @param array $args { |
4690 | | * Arguments about which post to use and how to format the output. Shares all of the arguments |
4691 | | * supported by get_the_taxonomies(), in addition to the following. |
4692 | | * |
4693 | | * @type int|WP_Post $post Post ID or object to get taxonomies of. Default current post. |
4694 | | * @type string $before Displays before the taxonomies. Default empty string. |
4695 | | * @type string $sep Separates each taxonomy. Default is a space. |
4696 | | * @type string $after Displays after the taxonomies. Default empty string. |
4697 | | * } |
4698 | | * @param array $args See {@link get_the_taxonomies()} for a description of arguments and their defaults. |
4699 | | */ |
4700 | | function the_taxonomies( $args = array() ) { |
4701 | | $defaults = array( |
4702 | | 'post' => 0, |
4703 | | 'before' => '', |
4704 | | 'sep' => ' ', |
4705 | | 'after' => '', |
4706 | | ); |
4707 | | |
4708 | | $r = wp_parse_args( $args, $defaults ); |
4709 | | |
4710 | | echo $r['before'] . join( $r['sep'], get_the_taxonomies( $r['post'], $r ) ) . $r['after']; |
4711 | | } |
4712 | | |
4713 | | /** |
4714 | | * Retrieve all taxonomies associated with a post. |
4715 | | * |
4716 | | * This function can be used within the loop. It will also return an array of |
4717 | | * the taxonomies with links to the taxonomy and name. |
4718 | | * |
4719 | | * @since 2.5.0 |
4720 | | * |
4721 | | * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. |
4722 | | * @param array $args { |
4723 | | * Optional. Arguments about how to format the list of taxonomies. Default empty array. |
4724 | | * |
4725 | | * @type string $template Template for displaying a taxonomy label and list of terms. |
4726 | | * Default is "Label: Terms." |
4727 | | * @type string $term_template Template for displaying a single term in the list. Default is the term name |
4728 | | * linked to its archive. |
4729 | | * } |
4730 | | * @return array List of taxonomies. |
4731 | | */ |
4732 | | function get_the_taxonomies( $post = 0, $args = array() ) { |
4733 | | $post = get_post( $post ); |
4734 | | |
4735 | | $args = wp_parse_args( $args, array( |
4736 | | /* translators: %s: taxonomy label, %l: list of terms formatted as per $term_template */ |
4737 | | 'template' => __( '%s: %l.' ), |
4738 | | 'term_template' => '<a href="%1$s">%2$s</a>', |
4739 | | ) ); |
4740 | | |
4741 | | $taxonomies = array(); |
4742 | | |
4743 | | if ( ! $post ) { |
4744 | | return $taxonomies; |
4745 | | } |
4746 | | |
4747 | | foreach ( get_object_taxonomies( $post ) as $taxonomy ) { |
4748 | | $t = (array) get_taxonomy( $taxonomy ); |
4749 | | if ( empty( $t['label'] ) ) { |
4750 | | $t['label'] = $taxonomy; |
4751 | | } |
4752 | | if ( empty( $t['args'] ) ) { |
4753 | | $t['args'] = array(); |
4754 | | } |
4755 | | if ( empty( $t['template'] ) ) { |
4756 | | $t['template'] = $args['template']; |
4757 | | } |
4758 | | if ( empty( $t['term_template'] ) ) { |
4759 | | $t['term_template'] = $args['term_template']; |
4760 | | } |
4761 | | |
4762 | | $terms = get_object_term_cache( $post->ID, $taxonomy ); |
4763 | | if ( false === $terms ) { |
4764 | | $terms = wp_get_object_terms( $post->ID, $taxonomy, $t['args'] ); |
4765 | | } |
4766 | | $links = array(); |
4767 | | |
4768 | | foreach ( $terms as $term ) { |
4769 | | $links[] = wp_sprintf( $t['term_template'], esc_attr( get_term_link( $term ) ), $term->name ); |
4770 | | } |
4771 | | if ( $links ) { |
4772 | | $taxonomies[$taxonomy] = wp_sprintf( $t['template'], $t['label'], $links, $terms ); |
4773 | | } |
4774 | | } |
4775 | | return $taxonomies; |
4776 | | } |
4777 | | |
4778 | | /** |
4779 | | * Retrieve all taxonomies of a post with just the names. |
4780 | | * |
4781 | | * @since 2.5.0 |
4782 | | * |
4783 | | * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. |
4784 | | * @return array |
4785 | | */ |
4786 | | function get_post_taxonomies( $post = 0 ) { |
4787 | | $post = get_post( $post ); |
4788 | | |
4789 | | return get_object_taxonomies($post); |
4790 | | } |
4791 | | |
4792 | | /** |
4793 | | * Determine if the given object is associated with any of the given terms. |
4794 | | * |
4795 | | * The given terms are checked against the object's terms' term_ids, names and slugs. |
4796 | | * Terms given as integers will only be checked against the object's terms' term_ids. |
4797 | | * If no terms are given, determines if object is associated with any terms in the given taxonomy. |
4798 | | * |
4799 | | * @since 2.7.0 |
4800 | | * |
4801 | | * @param int $object_id ID of the object (post ID, link ID, ...). |
4802 | | * @param string $taxonomy Single taxonomy name. |
4803 | | * @param int|string|array $terms Optional. Term term_id, name, slug or array of said. Default null. |
4804 | | * @return bool|WP_Error WP_Error on input error. |
4805 | | */ |
4806 | | function is_object_in_term( $object_id, $taxonomy, $terms = null ) { |
4807 | | if ( !$object_id = (int) $object_id ) |
4808 | | return new WP_Error( 'invalid_object', __( 'Invalid object ID' ) ); |
4809 | | |
4810 | | $object_terms = get_object_term_cache( $object_id, $taxonomy ); |
4811 | | if ( false === $object_terms ) |
4812 | | $object_terms = wp_get_object_terms( $object_id, $taxonomy ); |
4813 | | |
4814 | | if ( is_wp_error( $object_terms ) ) |
4815 | | return $object_terms; |
4816 | | if ( empty( $object_terms ) ) |
4817 | | return false; |
4818 | | if ( empty( $terms ) ) |
4819 | | return ( !empty( $object_terms ) ); |
4820 | | |
4821 | | $terms = (array) $terms; |
4822 | | |
4823 | | if ( $ints = array_filter( $terms, 'is_int' ) ) |
4824 | | $strs = array_diff( $terms, $ints ); |
4825 | | else |
4826 | | $strs =& $terms; |
4827 | | |
4828 | | foreach ( $object_terms as $object_term ) { |
4829 | | // If term is an int, check against term_ids only. |
4830 | | if ( $ints && in_array( $object_term->term_id, $ints ) ) { |
4831 | | return true; |
4832 | | } |
4833 | | |
4834 | | if ( $strs ) { |
4835 | | // Only check numeric strings against term_id, to avoid false matches due to type juggling. |
4836 | | $numeric_strs = array_map( 'intval', array_filter( $strs, 'is_numeric' ) ); |
4837 | | if ( in_array( $object_term->term_id, $numeric_strs, true ) ) { |
4838 | | return true; |
4839 | | } |
4840 | | |
4841 | | if ( in_array( $object_term->name, $strs ) ) return true; |
4842 | | if ( in_array( $object_term->slug, $strs ) ) return true; |
4843 | | } |
4844 | | } |
4845 | | |
4846 | | return false; |
4847 | | } |
4848 | | |
4849 | | /** |
4850 | | * Determine if the given object type is associated with the given taxonomy. |
4851 | | * |
4852 | | * @since 3.0.0 |
4853 | | * |
4854 | | * @param string $object_type Object type string. |
4855 | | * @param string $taxonomy Single taxonomy name. |
4856 | | * @return bool True if object is associated with the taxonomy, otherwise false. |
4857 | | */ |
4858 | | function is_object_in_taxonomy( $object_type, $taxonomy ) { |
4859 | | $taxonomies = get_object_taxonomies( $object_type ); |
4860 | | if ( empty( $taxonomies ) ) { |
4861 | | return false; |
4862 | | } |
4863 | | return in_array( $taxonomy, $taxonomies ); |
4864 | | } |
4865 | | |
4866 | | /** |
4867 | | * Get an array of ancestor IDs for a given object. |
4868 | | * |
4869 | | * @since 3.1.0 |
4870 | | * @since 4.1.0 Introduced the `$resource_type` argument. |
4871 | | * |
4872 | | * @param int $object_id Optional. The ID of the object. Default 0. |
4873 | | * @param string $object_type Optional. The type of object for which we'll be retrieving |
4874 | | * ancestors. Accepts a post type or a taxonomy name. Default empty. |
4875 | | * @param string $resource_type Optional. Type of resource $object_type is. Accepts 'post_type' |
4876 | | * or 'taxonomy'. Default empty. |
4877 | | * @return array An array of ancestors from lowest to highest in the hierarchy. |
4878 | | */ |
4879 | | function get_ancestors( $object_id = 0, $object_type = '', $resource_type = '' ) { |
4880 | | $object_id = (int) $object_id; |
4881 | | |
4882 | | $ancestors = array(); |
4883 | | |
4884 | | if ( empty( $object_id ) ) { |
4885 | | |
4886 | | /** This filter is documented in wp-includes/taxonomy.php */ |
4887 | | return apply_filters( 'get_ancestors', $ancestors, $object_id, $object_type, $resource_type ); |
4888 | | } |
4889 | | |
4890 | | if ( ! $resource_type ) { |
4891 | | if ( is_taxonomy_hierarchical( $object_type ) ) { |
4892 | | $resource_type = 'taxonomy'; |
4893 | | } elseif ( post_type_exists( $object_type ) ) { |
4894 | | $resource_type = 'post_type'; |
4895 | | } |
4896 | | } |
4897 | | |
4898 | | if ( 'taxonomy' === $resource_type ) { |
4899 | | $term = get_term($object_id, $object_type); |
4900 | | while ( ! is_wp_error($term) && ! empty( $term->parent ) && ! in_array( $term->parent, $ancestors ) ) { |
4901 | | $ancestors[] = (int) $term->parent; |
4902 | | $term = get_term($term->parent, $object_type); |
4903 | | } |
4904 | | } elseif ( 'post_type' === $resource_type ) { |
4905 | | $ancestors = get_post_ancestors($object_id); |
4906 | | } |
4907 | | |
4908 | | /** |
4909 | | * Filter a given object's ancestors. |
4910 | | * |
4911 | | * @since 3.1.0 |
4912 | | * @since 4.1.1 Introduced the `$resource_type` parameter. |
4913 | | * |
4914 | | * @param array $ancestors An array of object ancestors. |
4915 | | * @param int $object_id Object ID. |
4916 | | * @param string $object_type Type of object. |
4917 | | * @param string $resource_type Type of resource $object_type is. |
4918 | | */ |
4919 | | return apply_filters( 'get_ancestors', $ancestors, $object_id, $object_type, $resource_type ); |
4920 | | } |
4921 | | |
4922 | | /** |
4923 | | * Returns the term's parent's term_ID. |
4924 | | * |
4925 | | * @since 3.1.0 |
4926 | | * |
4927 | | * @param int $term_id Term ID. |
4928 | | * @param string $taxonomy Taxonomy name. |
4929 | | * @return int|false False on error. |
4930 | | */ |
4931 | | function wp_get_term_taxonomy_parent_id( $term_id, $taxonomy ) { |
4932 | | $term = get_term( $term_id, $taxonomy ); |
4933 | | if ( ! $term || is_wp_error( $term ) ) { |
4934 | | return false; |
4935 | | } |
4936 | | return (int) $term->parent; |
4937 | | } |
4938 | | |
4939 | | /** |
4940 | | * Checks the given subset of the term hierarchy for hierarchy loops. |
4941 | | * Prevents loops from forming and breaks those that it finds. |
4942 | | * |
4943 | | * Attached to the {@see 'wp_update_term_parent'} filter. |
4944 | | * |
4945 | | * @since 3.1.0 |
4946 | | * |
4947 | | * @param int $parent `term_id` of the parent for the term we're checking. |
4948 | | * @param int $term_id The term we're checking. |
4949 | | * @param string $taxonomy The taxonomy of the term we're checking. |
4950 | | * |
4951 | | * @return int The new parent for the term. |
4952 | | */ |
4953 | | function wp_check_term_hierarchy_for_loops( $parent, $term_id, $taxonomy ) { |
4954 | | // Nothing fancy here - bail |
4955 | | if ( !$parent ) |
4956 | | return 0; |
4957 | | |
4958 | | // Can't be its own parent. |
4959 | | if ( $parent == $term_id ) |
4960 | | return 0; |
4961 | | |
4962 | | // Now look for larger loops. |
4963 | | if ( !$loop = wp_find_hierarchy_loop( 'wp_get_term_taxonomy_parent_id', $term_id, $parent, array( $taxonomy ) ) ) |
4964 | | return $parent; // No loop |
4965 | | |
4966 | | // Setting $parent to the given value causes a loop. |
4967 | | if ( isset( $loop[$term_id] ) ) |
4968 | | return 0; |
4969 | | |
4970 | | // There's a loop, but it doesn't contain $term_id. Break the loop. |
4971 | | foreach ( array_keys( $loop ) as $loop_member ) |
4972 | | wp_update_term( $loop_member, $taxonomy, array( 'parent' => 0 ) ); |
4973 | | |
4974 | | return $parent; |
4975 | | } |