WordPress.org

Make WordPress Core

Opened 3 months ago

#43271 new enhancement

Issue with term duplicate check in wp_insert_term

Reported by: strategio Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 4.9.4
Component: Taxonomy Keywords:
Focuses: Cc:

Description

In WPML, we assume that a term can be duplicated in the same taxonomy in a secondary language. This means that it will have the same 'slug' as the one in the primary language.

Here's a practical example of 2 term links from the same taxonomy with the same slug:

In wp_insert_term we have several checks to ensure that a term is unique in its taxonomy. All these checks can be overpassed with the standard WP API except the last one which makes a direct call to the database:

/*
 * Sanity check: if we just created a term with the same parent + taxonomy + slug but a higher term_id than
 * an existing term, then we have unwittingly created a duplicate term. Delete the dupe, and use the term_id
 * and term_taxonomy_id of the older term instead. Then return out of the function so that the "create" hooks
 * are not fired.
 */
$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 ) );
if ( $duplicate_term ) {
	$wpdb->delete( $wpdb->terms, array( 'term_id' => $term_id ) );
	$wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) );

	$term_id = (int) $duplicate_term->term_id;
	$tt_id   = (int) $duplicate_term->term_taxonomy_id;

	clean_term_cache( $term_id, $taxonomy );
	return array( 'term_id' => $term_id, 'term_taxonomy_id' => $tt_id );
}

The above data validation is only performed in wp_insert_term and not in wp_update_term which makes the data validation inconsistent between the two function. This also allowed us to demonstrate that two terms with the same slug can live together in the same taxonomy.

As a suggestion, this last check could be either:

  • removed: since we already have several checks before and it's not performed when a term is updated.
  • filterable: by using get_term_by to fetch the potential existing term, or having a specific filter hook (e.g. wp_bypass_term_duplication_check).

Change History (0)

Note: See TracTickets for help on using tickets.