first commit
This commit is contained in:
27
wp-content/plugins/polylang-pro/modules/share-slug/load.php
Normal file
27
wp-content/plugins/polylang-pro/modules/share-slug/load.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang-Pro
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Don't access directly.
|
||||
}
|
||||
|
||||
if ( $polylang->model->has_languages() ) {
|
||||
add_filter(
|
||||
'pll_settings_modules',
|
||||
function ( $modules ) {
|
||||
$k = array_search( 'PLL_Settings_Preview_Share_Slug', $modules );
|
||||
if ( $k ) {
|
||||
$modules[ $k ] = 'PLL_Settings_Share_Slug';
|
||||
}
|
||||
return $modules;
|
||||
},
|
||||
20 // After Polylang.
|
||||
);
|
||||
|
||||
if ( get_option( 'permalink_structure' ) && $polylang->options['force_lang'] ) {
|
||||
$polylang->share_post_slug = new PLL_Share_Post_Slug( $polylang );
|
||||
$polylang->share_term_slug = new PLL_Share_Term_Slug( $polylang );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang-Pro
|
||||
*/
|
||||
|
||||
/**
|
||||
* Settings class to display information for the Share slugs module.
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
class PLL_Settings_Share_Slug extends PLL_Settings_Preview_Share_Slug {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @param PLL_Settings $polylang Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
parent::__construct( $polylang, array( 'active_option' => 'none' ) );
|
||||
|
||||
if ( get_option( 'permalink_structure' ) ) {
|
||||
add_action( 'admin_print_footer_scripts', array( $this, 'print_js' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the module description.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description() {
|
||||
return parent::get_description() . ' ' . __( 'The module is automatically deactivated when using plain permalinks or when the language is set from the content in the URL modifications.', 'polylang-pro' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the module is active.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_active() {
|
||||
return $this->options['force_lang'] && get_option( 'permalink_structure' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the javascript to handle dynamically the change in url modifications
|
||||
* as sharing slugs is not possible when the language is set from the content
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function print_js() {
|
||||
wp_enqueue_script( 'jquery' );
|
||||
|
||||
$activated = sprintf( '<span class="activated">%s</span>', $this->action_links['activated'] );
|
||||
$deactivated = sprintf( '<span class="deactivated">%s</span>', $this->action_links['deactivated'] );
|
||||
|
||||
?>
|
||||
<script>
|
||||
jQuery(
|
||||
function ( $ ) {
|
||||
$( "input[name='force_lang']" ).on( 'change', function () {
|
||||
var value = $( this ).val();
|
||||
if ( value > 0 ) {
|
||||
$( "#pll-module-share-slugs" ).removeClass( "inactive" ).addClass( "active" ).children( "td" ).children( ".row-actions" ).html( '<?php echo $activated; // phpcs:ignore WordPress.Security.EscapeOutput ?>' );
|
||||
}
|
||||
else {
|
||||
$( "#pll-module-share-slugs" ).removeClass( "active" ).addClass( "inactive" ).children( "td" ).children( ".row-actions" ).html( '<?php echo $deactivated; // phpcs:ignore WordPress.Security.EscapeOutput ?>' );
|
||||
}
|
||||
} );
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang-Pro
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class to manage shared slugs for posts
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class PLL_Share_Post_Slug {
|
||||
/**
|
||||
* Stores the plugin options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* @var PLL_Links_Model
|
||||
*/
|
||||
public $links_model;
|
||||
|
||||
/**
|
||||
* The current language.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param object $polylang Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
$this->options = &$polylang->options;
|
||||
$this->model = &$polylang->model;
|
||||
$this->links_model = &$polylang->links_model;
|
||||
$this->curlang = &$polylang->curlang;
|
||||
|
||||
// Get page by pagename and lang.
|
||||
add_action( 'parse_query', array( $this, 'parse_query' ), 0 ); // Before all other functions hooked to 'parse_query'.
|
||||
|
||||
// Get post by name and lang.
|
||||
add_filter( 'posts_join', array( $this, 'posts_join' ), 10, 2 );
|
||||
add_filter( 'posts_where', array( $this, 'posts_where' ), 10, 2 );
|
||||
|
||||
add_filter( 'wp_unique_post_slug', array( $this, 'wp_unique_post_slug' ), 10, 6 );
|
||||
add_action( 'pll_translate_media', array( $this, 'pll_translate_media' ), 20, 2 ); // After PLL_Admin_Sync to avoid reverse sync.
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the query object when a page is queried by slug and language
|
||||
* This must be the first function hooked to 'parse_query' to run so that others get the right queried page
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param WP_Query $query Reference to a WP_Query object.
|
||||
* @return void
|
||||
*/
|
||||
public function parse_query( $query ) {
|
||||
if ( $lang = $this->get_language_for_filter( $query ) ) {
|
||||
$qv = $query->query_vars;
|
||||
|
||||
// For hierarchical custom post types.
|
||||
if ( empty( $qv['pagename'] ) && ! empty( $qv['name'] ) && ! empty( $qv['post_type'] ) && array_intersect( get_post_types( array( 'hierarchical' => true ) ), (array) $qv['post_type'] ) ) {
|
||||
$qv['pagename'] = $qv['name'];
|
||||
}
|
||||
|
||||
if ( ! empty( $qv['pagename'] ) ) {
|
||||
/*
|
||||
* A simpler solution is available at https://github.com/mirsch/polylang-slug/commit/4bf2cb80256fc31347455f6539fac0c20f403c04
|
||||
* But it supposes that pages sharing slug are translations of each other which we don't.
|
||||
*/
|
||||
/** @var WP_Post|null $queried_object */
|
||||
$queried_object = $this->get_page_by_path( $qv['pagename'], $lang->slug, OBJECT, empty( $qv['post_type'] ) ? 'page' : $qv['post_type'] );
|
||||
|
||||
// If we got nothing or an attachment, check if we also have a post with the same slug. See https://core.trac.wordpress.org/ticket/24612
|
||||
if ( empty( $qv['post_type'] ) && ( empty( $queried_object ) || 'attachment' === $queried_object->post_type ) && preg_match( '/^[^%]*%(?:postname)%/', get_option( 'permalink_structure' ) ) ) {
|
||||
/** @var WP_Post|null $post */
|
||||
$post = $this->get_page_by_path( $qv['pagename'], $lang->slug, OBJECT, 'post' );
|
||||
if ( $post ) {
|
||||
$queried_object = $post;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $queried_object ) ) {
|
||||
$query->queried_object = $queried_object;
|
||||
$query->queried_object_id = (int) $queried_object->ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a page given its path.
|
||||
* This is the same function as WP get_page_by_path()
|
||||
* Rewritten to make it language dependent
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $page_path Page path.
|
||||
* @param string $lang Language slug.
|
||||
* @param string $output Optional. Output type. Accepts OBJECT, ARRAY_N, or ARRAY_A. Default OBJECT.
|
||||
* @param string|string[] $post_type Optional. Post type or array of post types. Default 'page'.
|
||||
* @return WP_Post|mixed[]|null WP_Post on success or null on failure.
|
||||
*
|
||||
* @phpstan-param non-empty-string $lang
|
||||
* @phpstan-param 'ARRAY_A'|'ARRAY_N'|'OBJECT' $output
|
||||
* @phpstan-return array<int|string, mixed>|WP_Post|null
|
||||
*/
|
||||
protected function get_page_by_path( $page_path, $lang, $output = OBJECT, $post_type = 'page' ) {
|
||||
global $wpdb;
|
||||
|
||||
$page_path = rawurlencode( urldecode( $page_path ) );
|
||||
$page_path = str_replace( '%2F', '/', $page_path );
|
||||
$page_path = str_replace( '%20', ' ', $page_path );
|
||||
$parts = explode( '/', trim( $page_path, '/' ) );
|
||||
$parts = array_map( 'sanitize_title_for_query', $parts );
|
||||
$escaped_parts = esc_sql( $parts );
|
||||
|
||||
$in_string = "'" . implode( "','", (array) $escaped_parts ) . "'";
|
||||
|
||||
if ( is_array( $post_type ) ) {
|
||||
$post_types = $post_type;
|
||||
} else {
|
||||
$post_types = array( $post_type, 'attachment' );
|
||||
}
|
||||
|
||||
$post_types = esc_sql( $post_types );
|
||||
$post_type_in_string = "'" . implode( "','", $post_types ) . "'";
|
||||
$sql = "SELECT ID, post_name, post_parent, post_type FROM {$wpdb->posts}";
|
||||
$sql .= $this->model->post->join_clause();
|
||||
$sql .= " WHERE post_name IN ( {$in_string} ) AND post_type IN ( {$post_type_in_string} )";
|
||||
$sql .= $this->model->post->where_clause( $lang );
|
||||
|
||||
// PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$pages = $wpdb->get_results( $sql, OBJECT_K );
|
||||
|
||||
$revparts = array_reverse( $parts );
|
||||
|
||||
$foundid = 0;
|
||||
foreach ( (array) $pages as $page ) {
|
||||
if ( $page->post_name === $revparts[0] ) {
|
||||
$count = 0;
|
||||
$p = $page;
|
||||
while ( 0 !== (int) $p->post_parent && isset( $pages[ $p->post_parent ] ) ) {
|
||||
++$count;
|
||||
$parent = $pages[ $p->post_parent ];
|
||||
if ( ! isset( $revparts[ $count ] ) || $parent->post_name !== $revparts[ $count ] ) {
|
||||
break;
|
||||
}
|
||||
$p = $parent;
|
||||
}
|
||||
|
||||
if ( 0 === (int) $p->post_parent && count( $revparts ) === $count + 1 && $p->post_name === $revparts[ $count ] ) {
|
||||
$foundid = $page->ID;
|
||||
if ( $page->post_type === $post_type ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $foundid ) {
|
||||
return get_post( $foundid, $output );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds our join clause to sql query.
|
||||
* Useful when querying a post by name.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $join Original join clause.
|
||||
* @param WP_Query $query The WP_Query object.
|
||||
* @return string Modified join clause.
|
||||
*/
|
||||
public function posts_join( $join, $query ) {
|
||||
if ( $this->get_language_for_filter( $query ) ) {
|
||||
return $join . $this->model->post->join_clause();
|
||||
}
|
||||
return $join;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds our where clause to sql query.
|
||||
* Useful when querying a post by name.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $where Original where clause.
|
||||
* @param WP_Query $query The WP_Query object.
|
||||
* @return string Modified where clause.
|
||||
*/
|
||||
public function posts_where( $where, $query ) {
|
||||
if ( $language = $this->get_language_for_filter( $query ) ) {
|
||||
return $where . $this->model->post->where_clause( $language );
|
||||
}
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the query must be filtered or not
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param WP_Query $query The WP_Query object.
|
||||
* @return PLL_Language|false The language to use for the filter, false if the query should be kept unfiltered.
|
||||
*/
|
||||
protected function get_language_for_filter( $query ) {
|
||||
$qv = $query->query_vars;
|
||||
|
||||
if ( empty( $qv['name'] ) && empty( $qv['pagename'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$post_type = empty( $qv['post_type'] ) ? 'post' : $qv['post_type'];
|
||||
|
||||
if ( ! empty( $qv['attachment'] ) ) {
|
||||
$post_type = 'attachment';
|
||||
}
|
||||
|
||||
if ( ! $this->model->is_translated_post_type( $post_type ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! empty( $qv['lang'] ) ) {
|
||||
return $this->model->get_language( $qv['lang'] );
|
||||
}
|
||||
|
||||
if ( isset( $qv['tax_query'] ) && is_array( $qv['tax_query'] ) ) {
|
||||
foreach ( $qv['tax_query'] as $tax_query ) {
|
||||
if ( isset( $tax_query['taxonomy'] ) && 'language' === $tax_query['taxonomy'] ) {
|
||||
if ( is_array( $tax_query['terms'] ) ) {
|
||||
if ( 1 < count( $tax_query['terms'] ) ) {
|
||||
// Several language terms queried.
|
||||
continue;
|
||||
}
|
||||
|
||||
$term = reset( $tax_query['terms'] );
|
||||
} else {
|
||||
$term = $tax_query['terms'];
|
||||
}
|
||||
|
||||
if ( isset( $tax_query['field'] ) && 'term_taxonomy_id' === $tax_query['field'] ) {
|
||||
$term = "tt:{$term}";
|
||||
}
|
||||
|
||||
$lang = $this->model->get_language( $term );
|
||||
|
||||
if ( $lang ) {
|
||||
return $lang;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $this->curlang ) ) {
|
||||
return $this->curlang;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the slug is unique within language.
|
||||
* Thanks to @AndyDeGroo for https://wordpress.org/support/topic/plugin-polylang-identical-page-names-in-different-languages?replies=8#post-2669927
|
||||
* Thanks to Ulrich Pogson for https://github.com/grappler/polylang-slug/blob/master/polylang-slug.php
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $slug The slug defined by wp_unique_post_slug() in WP.
|
||||
* @param int $post_ID The post id.
|
||||
* @param string $post_status Not used.
|
||||
* @param string $post_type The Post type.
|
||||
* @param int $post_parent The id of the post parent.
|
||||
* @param string $original_slug The original slug before it is modified by wp_unique_post_slug in WP.
|
||||
* @return string Original slug if it is unique in the language or the modified slug otherwise.
|
||||
*/
|
||||
public function wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) {
|
||||
global $wpdb;
|
||||
|
||||
// Return slug if it was not changed.
|
||||
if ( $original_slug === $slug || 0 === $this->options['force_lang'] || ! $this->model->is_translated_post_type( $post_type ) ) {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
$lang = $this->model->post->get_language( $post_ID );
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
if ( 'attachment' === $post_type ) {
|
||||
// Attachment slugs must be unique across all types.
|
||||
$sql = "SELECT post_name FROM {$wpdb->posts}";
|
||||
$sql .= $this->model->post->join_clause();
|
||||
$sql .= $wpdb->prepare( ' WHERE post_name = %s AND ID != %d', $original_slug, $post_ID );
|
||||
$sql .= $this->model->post->where_clause( $lang ) . ' LIMIT 1';
|
||||
|
||||
// PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$post_name_check = $wpdb->get_var( $sql );
|
||||
}
|
||||
|
||||
elseif ( is_post_type_hierarchical( $post_type ) ) {
|
||||
// Page slugs must be unique within their own trees. Pages are in a separate namespace than posts so page slugs are allowed to overlap post slugs.
|
||||
$sql = "SELECT ID FROM {$wpdb->posts}";
|
||||
$sql .= $this->model->post->join_clause();
|
||||
$sql .= $wpdb->prepare( " WHERE post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d AND post_parent = %d", $original_slug, $post_type, $post_ID, $post_parent );
|
||||
$sql .= $this->model->post->where_clause( $lang ) . ' LIMIT 1';
|
||||
|
||||
// PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$post_name_check = $wpdb->get_var( $sql );
|
||||
}
|
||||
|
||||
else {
|
||||
// Post slugs must be unique across all posts.
|
||||
$sql = "SELECT post_name FROM {$wpdb->posts}";
|
||||
$sql .= $this->model->post->join_clause();
|
||||
$sql .= $wpdb->prepare( ' WHERE post_name = %s AND post_type = %s AND ID != %d', $original_slug, $post_type, $post_ID );
|
||||
$sql .= $this->model->post->where_clause( $lang ) . ' LIMIT 1';
|
||||
|
||||
// PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$post_name_check = $wpdb->get_var( $sql );
|
||||
}
|
||||
|
||||
return $post_name_check ? $slug : $original_slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the attachment slug when creating a translation to allow to share slugs
|
||||
* This second step is needed because wp_unique_post_slug is called before the language is set
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $post_id Original attachment id.
|
||||
* @param int $tr_id Translated attachment id.
|
||||
* @return void
|
||||
*/
|
||||
public function pll_translate_media( $post_id, $tr_id ) {
|
||||
$post = get_post( $post_id );
|
||||
if ( $post ) {
|
||||
wp_update_post( array( 'ID' => $tr_id, 'post_name' => $post->post_name ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang-Pro
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class for managing shared slugs for taxonomy terms
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class PLL_Share_Term_Slug {
|
||||
/**
|
||||
* Stores the plugin options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Instance of a child class of PLL_Links_Model.
|
||||
*
|
||||
* @var PLL_Links_Model
|
||||
*/
|
||||
public $links_model;
|
||||
|
||||
/**
|
||||
* Stores the term name before creating a slug if needed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $pre_term_name = '';
|
||||
|
||||
/**
|
||||
* Used to trick WordPress by setting
|
||||
* a transitory unique term slug.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TERM_SLUG_SEPARATOR = '___';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param PLL_Base $polylang Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
$this->options = &$polylang->options;
|
||||
$this->model = &$polylang->model;
|
||||
$this->links_model = &$polylang->links_model;
|
||||
|
||||
add_action( 'created_term', array( $this, 'save_term' ), 1, 3 );
|
||||
add_action( 'edited_term', array( $this, 'save_term' ), 1, 3 );
|
||||
add_filter( 'pre_term_name', array( $this, 'set_pre_term_name' ) );
|
||||
add_filter( 'pre_term_slug', array( $this, 'set_pre_term_slug' ), 10, 2 );
|
||||
|
||||
// Remove Polylang filter to avoid conflicts when filtering slugs.
|
||||
remove_filter( 'pre_term_name', array( $polylang->terms, 'set_pre_term_name' ), 10 );
|
||||
remove_filter( 'pre_term_slug', array( $polylang->terms, 'set_pre_term_slug' ), 10 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Will make slug unique per language and taxonomy
|
||||
* Mostly taken from wp_unique_term_slug
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $slug The string that will be tried for a unique slug.
|
||||
* @param string $lang Language slug.
|
||||
* @param WP_Term $term The term object that the $slug will belong too.
|
||||
* @return string Will return a true unique slug.
|
||||
*/
|
||||
protected function unique_term_slug( $slug, $lang, $term ) {
|
||||
global $wpdb;
|
||||
|
||||
$original_slug = $slug; // Save this for the filter at the end.
|
||||
|
||||
// Quick check.
|
||||
if ( ! $this->model->term_exists_by_slug( $slug, $lang, $term->taxonomy ) ) {
|
||||
/** This filter is documented in /wordpress/wp-includes/taxonomy.php */
|
||||
return apply_filters( 'wp_unique_term_slug', $slug, $term, $original_slug );
|
||||
}
|
||||
|
||||
/*
|
||||
* As done by WP in term_exists except that we use our own term_exist.
|
||||
* If the taxonomy supports hierarchy and the term has a parent,
|
||||
* make the slug unique by incorporating parent slugs.
|
||||
*/
|
||||
if ( is_taxonomy_hierarchical( $term->taxonomy ) && ! empty( $term->parent ) ) {
|
||||
$the_parent = $term->parent;
|
||||
while ( $the_parent > 0 ) {
|
||||
$parent_term = get_term( $the_parent, $term->taxonomy );
|
||||
if ( ! $parent_term instanceof WP_Term ) {
|
||||
break;
|
||||
}
|
||||
$slug .= '-' . $parent_term->slug;
|
||||
if ( ! $this->model->term_exists_by_slug( $slug, $lang ) ) { // Calls our own term_exists.
|
||||
/** This filter is documented in /wordpress/wp-includes/taxonomy.php */
|
||||
return apply_filters( 'wp_unique_term_slug', $slug, $term, $original_slug );
|
||||
}
|
||||
|
||||
$the_parent = $parent_term->parent;
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't get a unique slug, try appending a number to make it unique.
|
||||
if ( ! empty( $term->term_id ) ) {
|
||||
$query = $wpdb->prepare( "SELECT slug FROM {$wpdb->terms} WHERE slug = %s AND term_id != %d", $slug, $term->term_id );
|
||||
}
|
||||
else {
|
||||
$query = $wpdb->prepare( "SELECT slug FROM {$wpdb->terms} WHERE slug = %s", $slug );
|
||||
}
|
||||
|
||||
// PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
if ( $wpdb->get_var( $query ) ) {
|
||||
$num = 2;
|
||||
do {
|
||||
$alt_slug = $slug . "-$num";
|
||||
++$num;
|
||||
$slug_check = $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM {$wpdb->terms} WHERE slug = %s", $alt_slug ) );
|
||||
} while ( $slug_check );
|
||||
$slug = $alt_slug;
|
||||
}
|
||||
|
||||
/** This filter is documented in /wordpress/wp-includes/taxonomy.php */
|
||||
return apply_filters( 'wp_unique_term_slug', $slug, $term, $original_slug );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ugly hack to enable the same slug in several languages
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $term_id The term id of a saved term.
|
||||
* @param int $tt_id The term taxononomy id.
|
||||
* @param string $taxonomy The term taxonomy.
|
||||
* @return void
|
||||
*/
|
||||
public function save_term( $term_id, $tt_id, $taxonomy ) {
|
||||
global $wpdb;
|
||||
|
||||
// Does nothing except on taxonomies which are filterable.
|
||||
if ( ! $this->model->is_translated_taxonomy( $taxonomy ) || 0 === $this->options['force_lang'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$term = get_term( $term_id, $taxonomy );
|
||||
|
||||
if ( ! ( $term instanceof WP_Term ) || false === ( $pos = strpos( $term->slug, self::TERM_SLUG_SEPARATOR ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$slug = substr( $term->slug, 0, $pos );
|
||||
$lang = substr( $term->slug, $pos + 3 );
|
||||
|
||||
// Need to check for unique slug as we tricked wp_unique_term_slug from WP.
|
||||
$slug = $this->unique_term_slug( $slug, $lang, (object) $term );
|
||||
$wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) );
|
||||
clean_term_cache( $term_id, $taxonomy );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the term name to use in 'pre_term_slug'.
|
||||
*
|
||||
* @since 3.3
|
||||
*
|
||||
* @param string $name Term name.
|
||||
* @return string Unmodified term name.
|
||||
*/
|
||||
public function set_pre_term_name( $name ) {
|
||||
return $this->pre_term_name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends language slug to the term slug if needed.
|
||||
*
|
||||
* @since 3.3
|
||||
*
|
||||
* @param string $slug Term slug.
|
||||
* @param string $taxonomy Term taxonomy.
|
||||
* @return string Slug with a language suffix if found.
|
||||
*/
|
||||
public function set_pre_term_slug( $slug, $taxonomy ) {
|
||||
if ( ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
if ( ! $slug ) {
|
||||
$slug = sanitize_title( $this->pre_term_name );
|
||||
}
|
||||
|
||||
if ( ! term_exists( $slug, $taxonomy ) ) {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
/** This filter is documented in polylang/include/crud-terms.php */
|
||||
$lang = apply_filters( 'pll_inserted_term_language', null, $taxonomy, $slug );
|
||||
|
||||
if ( ! $lang instanceof PLL_Language ) {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
$parent = 0;
|
||||
|
||||
if ( is_taxonomy_hierarchical( $taxonomy ) ) {
|
||||
/** This filter is documented in polylang/include/crud-terms.php */
|
||||
$parent = apply_filters( 'pll_inserted_term_parent', 0, $taxonomy, $slug );
|
||||
|
||||
$slug .= $this->maybe_get_parent_suffix( $parent, $taxonomy, $slug );
|
||||
}
|
||||
|
||||
$term_id = (int) $this->model->term_exists_by_slug( $slug, $lang, $taxonomy, $parent );
|
||||
|
||||
/**
|
||||
* If no term exists in the given language with that slug, it can be created.
|
||||
* Or if we are editing the existing term, trick WordPress to allow shared slugs.
|
||||
*/
|
||||
if ( ! $term_id || ( ! empty( $_POST['tag_ID'] ) && (int) $_POST['tag_ID'] === $term_id ) || ( ! empty( $_POST['tax_ID'] ) && (int) $_POST['tax_ID'] === $term_id ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$slug .= self::TERM_SLUG_SEPARATOR . $lang->slug;
|
||||
}
|
||||
|
||||
return $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent suffix for the slug only if parent slug is the same as the given one.
|
||||
* Recursively appends the parents slugs like WordPress does.
|
||||
*
|
||||
* @since 3.3
|
||||
*
|
||||
* @param int $parent Parent term ID.
|
||||
* @param string $taxonomy Parent taxonomy.
|
||||
* @param string $slug Child term slug.
|
||||
* @return string Parents slugs if they are the same as the child slug, empty string otherwise.
|
||||
*/
|
||||
private function maybe_get_parent_suffix( $parent, $taxonomy, $slug ) {
|
||||
$parent_suffix = '';
|
||||
$the_parent = get_term( $parent, $taxonomy );
|
||||
|
||||
if ( ! $the_parent instanceof WP_Term || $the_parent->slug !== $slug ) {
|
||||
return $parent_suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mostly copied from {@see wp_unique_term_slug()}.
|
||||
*/
|
||||
while ( ! empty( $the_parent ) ) {
|
||||
$parent_term = get_term( $the_parent, $taxonomy );
|
||||
if ( ! $parent_term instanceof WP_Term ) {
|
||||
break;
|
||||
}
|
||||
$parent_suffix .= '-' . $parent_term->slug;
|
||||
if ( ! term_exists( $slug . $parent_suffix ) ) {
|
||||
break;
|
||||
}
|
||||
$the_parent = $parent_term->parent;
|
||||
}
|
||||
|
||||
return $parent_suffix;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user