";
} else {
// Auto-update settings
$auto_update_def_val = $permalink_manager_options["general"]["auto_update_uris"];
if ( $auto_update_def_val == 1 ) {
$auto_update_def_label = __( "Auto-update \"Custom permalink\"", "permalink-manager" );
} else if ( $auto_update_def_val == 2 ) {
$auto_update_def_label = __( "Don't save/generate custom permalinks", "permalink-manager" );
} else {
$auto_update_def_label = __( "Don't auto-update \"Custom permalink\"", "permalink-manager" );
}
$auto_update_choices = array(
/* translators: The global value of the "Auto-update" mode setting */
0 => array( "label" => sprintf( __( "Use global settings [%s]", "permalink-manager" ), $auto_update_def_label ), "atts" => "data-readonly=\"{$auto_update_def_val}\"" ),
10 => '---',
- 1 => array( "label" => __( "Don't auto-update \"Custom permalink\"", "permalink-manager" ), "atts" => "data-readonly=\"0\"" ),
- 2 => array( "label" => __( "Don't auto-update \"Custom permalink\" and exclude from the \"Regenerate/reset\" tool", "permalink-manager" ), "atts" => "data-readonly=\"0\"" ),
1 => array( "label" => __( "Auto-update \"Custom permalink\"", "permalink-manager" ), "atts" => "data-readonly=\"1\"" ),
11 => '---',
2 => array( "label" => __( "Disable custom permalink (disallow further changes)", "permalink-manager" ), "atts" => "data-readonly=\"2\"" ),
);
// Decode default URI
$default_uri = rawurldecode( $default_uri );
// Start HTML output
// 1. Button
if ( ! $gutenberg ) {
$html = sprintf( "
";
// 2. The heading
$html .= "
" . __( "Close: ", "permalink-manager" ) . __( "Permalink Manager", "permalink-manager" ) . " ";
$html .= sprintf( "
%s ", __( "Permalink Manager", "permalink-manager" ) );
// 3. The fields container [start]
$html .= "
";
} else {
$html = "
";
}
// 4. Custom URI
if ( ! empty( $is_front_page ) ) {
$custom_uri_field = self::generate_option_field( "custom_uri", array( "type" => "hidden", "extra_atts" => "data-default=\"{$default_uri}\" data-element-id=\"{$element_id}\"", "input_class" => "widefat custom_uri", "value" => rawurldecode( $uri ) ) );
$custom_uri_field .= __( "The custom URI cannot be edited on frontpage.", "permalink-manager" );
} else {
$custom_uri_field = self::generate_option_field( "custom_uri", array( "extra_atts" => "data-default=\"{$default_uri}\" data-element-id=\"{$element_id}\"", "input_class" => "widefat custom_uri", "value" => rawurldecode( $uri ) ) );
$custom_uri_field .= sprintf( '
%s %s
', '
', __( 'The URL above is displayed in read-only mode. To enable editing, change the "
Permalink update " setting to
Don\'t auto-update "Custom permalink" .', 'permalink-manager' ) );
}
$html .= sprintf( "
", __( "Custom permalink", "permalink-manager" ), $custom_uri_field );
// 5. Auto-update URI
if ( empty( $is_front_page ) ) {
if ( ! empty( $auto_update_choices ) ) {
$html .= sprintf( "
", __( "Permalink update", "permalink-manager" ), self::help_tooltip( __( "If 'auto-update mode' is turned on, the 'Custom permalink' field will be automatically changed to 'Default custom permalink' (displayed below) after the post is saved or updated.", "permalink-manager" ) ), self::generate_option_field( "auto_update_uri", array( "type" => "select", "input_class" => "widefat auto_update", "value" => $auto_update_val, "choices" => $auto_update_choices ) ) );
}
}
// 6. Native slug
if ( ! empty( $element->ID ) && ! empty( $permalink_manager_options["general"]["show_native_slug_field"] ) ) {
$native_slug_field = self::generate_option_field( "native_slug", array( "extra_atts" => "data-default=\"{$native_slug}\" data-element-id=\"{$element_id}\"", "input_class" => "widefat native_slug", "value" => rawurldecode( $native_slug ) ) );
$html .= sprintf( "
", __( "Native slug", "permalink-manager" ), self::help_tooltip( __( "The native slug is by default automatically used in native permalinks (when Permalink Manager is disabled).", "permalink-manager" ) ), $native_slug_field );
}
if ( empty( $is_front_page ) ) {
// 7. Default custom permalink
$html .= "
";
$html .= sprintf( "
%s: %s", __( "Default custom permalink", "permalink-manager" ), esc_html( $default_uri ) );
$html .= sprintf( "
%s", __( "Use \"Default custom permalink\"", "permalink-manager" ) );
// $html .= sprintf( "
%s", __( "Go to \"Permastructures\"", "permalink-manager" ) );
$html .= "
";
// 8. Native permalink info
if ( ! empty( $permalink_manager_options['general']['redirect'] ) && ! ( ! empty( $element->post_status ) && in_array( $element->post_status, array( 'auto-draft', 'trash', 'draft' ) ) ) ) {
$native_permalink = trim( Permalink_Manager_Permastructure_Functions::get_permalink_base( $element ), "/" ) . "/";
$native_permalink .= $native_uri;
$native_permalink_label = ( $native_uri === $uri ) ? __( "Original WordPress permalink:", "permalink-manager" ) : __( "Original WordPress permalink (redirected):", "permalink-manager" );
$html .= sprintf( "
", $native_permalink_label, $native_permalink, rawurldecode( $native_uri ) );
}
}
// 9. Custom redirects
$html .= ( $element->ID ) ? self::display_redirect_panel( $id ) : self::display_redirect_panel( "tax-{$id}" );
// 10. Extra save button for Gutenberg
if ( $gutenberg ) {
$html .= sprintf( "
", __( "Save permalink", "permalink-manager" ) );
} else {
$html .= "
";
}
$html .= "
";
}
// 11. Append nonce field, element ID & native slug
$html .= self::generate_option_field( "permalink-manager-edit-uri-element-slug", array( "type" => "hidden", "value" => $native_slug ) );
$html .= self::generate_option_field( "permalink-manager-edit-uri-element-id", array( "type" => "hidden", "value" => $element_id ) );
$html .= wp_nonce_field( 'permalink-manager-edit-uri-box', 'permalink-manager-nonce', true, false );
return $html;
}
/**
* Get the HTML output of the redirect panel
*
* @param string|int $element_id
*
* @return string
*/
public static function display_redirect_panel( $element_id ) {
// Heading
$html = "
";
$html .= sprintf( "
", __( "Manage redirects", "permalink-manager" ) );
$html .= "
";
if ( class_exists( 'Permalink_Manager_Pro_Addons' ) ) {
$html .= Permalink_Manager_Pro_Addons::display_redirect_form( $element_id );
} else {
$html .= self::pro_text( true );
}
$html .= "
";
$html .= "
";
return $html;
}
/**
* Hide "Custom URI" column
*
* @param array $hidden
*
* @return array
*/
function quick_edit_hide_column( $hidden ) {
$hidden[] = 'permalink-manager-col';
return $hidden;
}
/**
* Display the simplified URI Editor in "Quick Edit" mode
*
* @param string $column_name
* @param string $post_type
* @param string $taxonomy
*/
public static function quick_edit_column_form( $column_name, $post_type, $taxonomy = '' ) {
// Check the user capabilities
if ( Permalink_Manager_Admin_Functions::current_user_can_edit_uris() === false || $column_name !== 'permalink-manager-col' ) {
return;
}
$html = Permalink_Manager_UI_Elements::generate_option_field( 'permalink-manager-quick-edit', array( 'value' => true, 'type' => 'hidden' ) );
$html .= '
';
$html .= sprintf( "%s ", __( "Permalink Manager", "permalink-manager" ) );
$html .= '';
$html .= sprintf( "%s %s ", __( "Custom permalink", "permalink-manager" ), Permalink_Manager_UI_Elements::generate_option_field( "custom_uri", array( "input_class" => "custom_uri", "value" => '' ) ) );
$html .= "
";
$html .= " ";
// Append nonce field & element ID
$html .= Permalink_Manager_UI_Elements::generate_option_field( "permalink-manager-edit-uri-element-id", array( "type" => "hidden", "input_class" => "permalink-manager-edit-uri-element-id", "value" => "" ) );
$html .= wp_nonce_field( 'permalink-manager-edit-uri-box', 'permalink-manager-nonce', true, false );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $html;
}
/**
* Get the HTML output of error/info message
*
* @param string $alert_content
* @param string $alert_type
* @param bool $dismissible
* @param bool $id
*
* @return string
*/
public static function get_alert_message( $alert_content = "", $alert_type = "", $dismissible = true, $id = false ) {
// Ignore empty messages (just in case)
if ( empty( $alert_content ) || empty( $alert_type ) ) {
return "";
}
$class = ( $dismissible ) ? "is-dismissible" : "";
$alert_id = ( $id ) ? " data-alert_id=\"{$id}\"" : "";
return sprintf( "
%s
", wpautop( $alert_content ) );
}
/**
* Get the HTML output of help tooltip
*
* @param string $text
*
* @return string
*/
static function help_tooltip( $text = '' ) {
return "
";
}
/**
* Display the license expiration date (in Pro version) or information about the premium functionality
*
* @param string $text_only
*
* @return string
*/
static function pro_text( $text_only = false ) {
if ( class_exists( 'Permalink_Manager_Pro_License' ) ) {
$text = Permalink_Manager_Pro_License::get_expiration_date( false, true );
} else {
/* translators: Permalink Manager Pro website */
$text = sprintf( __( 'This functionality is available only in
Permalink Manager Pro .', 'permalink-manager' ), PERMALINK_MANAGER_PROMO );
}
return ( $text_only ) ? $text : sprintf( "
%s
", wpautop( $text, 'alert' ) );
}
} views/permalink-manager-permastructures.php 0000644 00000032116 15220204736 0015256 0 ustar 00 __( 'Permastructures', 'permalink-manager' ),
'function' => array( 'class' => 'Permalink_Manager_Permastructs', 'method' => 'output' )
);
return $admin_sections;
}
/**
* Return an array of fields that will be used to adjust the permastructure settings
*
* @return array
*/
public function get_fields() {
$post_types = Permalink_Manager_Helper_Functions::get_post_types_array( 'full' );
$taxonomies = Permalink_Manager_Helper_Functions::get_taxonomies_array( 'full' );
// Display additional information in Permalink Manager Lite
if ( ! Permalink_Manager_Admin_Functions::is_pro_active() ) {
/* translators: %s: Permalink Manager Pro website */
$pro_text = sprintf( __( 'To edit taxonomy permalinks,
Permalink Manager Pro is required.', 'permalink-manager' ), PERMALINK_MANAGER_PROMO );
$pro_text = sprintf( '
%s
', $pro_text );
}
// 1. Get fields
$fields = array(
'post_types' => array(
'section_name' => __( 'Post types', 'permalink-manager' ),
'container' => 'row',
'fields' => array()
),
'taxonomies' => array(
'section_name' => __( 'Taxonomies', 'permalink-manager' ),
'container' => 'row',
'fields' => array(),
'append_content' => ( ! empty( $pro_text ) ) ? $pro_text : ''
)
);
// 2. Add a separate section for WooCommerce content types
if ( class_exists( 'WooCommerce' ) ) {
$fields['woocommerce'] = array(
'section_name' => "
" . __( 'WooCommerce', 'permalink-manager' ),
'container' => 'row',
'fields' => array(),
'append_content' => ( ! empty( $pro_text ) ) ? $pro_text : ''
);
}
// 3A. Add permastructure fields for post types
foreach ( $post_types as $post_type ) {
if ( $post_type['name'] == 'shop_coupon' ) {
continue;
}
$fields["post_types"]["fields"][ $post_type['name'] ] = self::get_single_permastructure_field( $post_type, false, false );
}
// 3B. Add permastructure fields for taxonomies
foreach ( $taxonomies as $taxonomy ) {
$taxonomy_name = $taxonomy['name'];
// Check if taxonomy exists
if ( ! taxonomy_exists( $taxonomy_name ) ) {
continue;
}
$fields["taxonomies"]["fields"][ $taxonomy_name ] = self::get_single_permastructure_field( $taxonomy, true, isset( $pro_text ) );
}
return apply_filters( 'permalink_manager_permastructs_fields', $fields );
}
/**
* Get the row of the permastructure field for single content type
*
* @param $content_type
* @param bool $is_tax
* @param bool $pro_alert
*
* @return string
*/
function get_single_permastructure_field( $content_type, $is_tax = false, $pro_alert = false ) {
global $permalink_manager_permastructs;
if ( empty( $content_type['name'] ) ) {
return '';
}
$content_group = ( $is_tax ) ? "taxonomies" : "post_types";
$content_type_name = $content_type['name'];
$content_type_label = $content_type['label'];
$siteurl = Permalink_Manager_Permastructure_Functions::get_permalink_base();
$tags_container_id = sprintf( 'permastruct-tags-%s-%s', $content_group, $content_type_name );
$available_tags = self::get_all_structure_tags( $content_type_name, $is_tax );
// Get permastructures
$permastructures = ( ! empty( $permalink_manager_permastructs[ $content_group ] ) ) ? $permalink_manager_permastructs[ $content_group ] : array();
$default_permastruct = trim( Permalink_Manager_Permastructure_Functions::get_default_permastruct( $content_type_name ), "/" );
$current_permastruct = isset( $permastructures[ $content_type_name ] ) ? $permastructures[ $content_type_name ] : $default_permastruct;
// Append extra attributes
$field_atts = array(
'value' => $current_permastruct,
'input_class' => 'permastruct-field',
'disabled' => $pro_alert,
'placeholder' => $default_permastruct,
'extra_atts' => " data-default=\"{$default_permastruct}\""
);
$field_name = sprintf( '%s[%s]', $content_group, $content_type_name );
$permastruct_field = sprintf( "
%s/%s
", $siteurl, Permalink_Manager_UI_Elements::generate_option_field( $field_name, $field_atts ) );
$buttons = sprintf( "
%s
%s
", __( "Extra settings", "permalink-manager" ), $tags_container_id, __( "Available tags", "permalink-manager" ) );
$language_fields = '';
$languages = Permalink_Manager_Language_Plugins::get_all_languages( true );
if ( $languages ) {
$language_fields = sprintf( "
%s %s
", __( "Permastructure translations", "permalink-manager" ), __( "If you would like to translate the permastructures and set-up different permalink structure per language, please fill in the fields below. Otherwise the permastructure set for default language (see field above) will be applied.", "permalink-manager" ) );
foreach ( $languages as $lang => $name ) {
$current_lang_permastruct = isset( $permastructures["{$content_type_name}_{$lang}"] ) ? $permastructures["{$content_type_name}_{$lang}"] : '';
$lang_field_atts = array_merge( $field_atts, array( 'value' => $current_lang_permastruct, 'extra_atts' => 'data-default=""', 'placeholder' => $current_permastruct ) );
$lang_field_name = str_replace( "]", "_{$lang}]", $field_name );
$language_fields .= sprintf( "
%s %s/%s
", $name, Permalink_Manager_Language_Plugins::prepend_lang_prefix( $siteurl, '', $lang ), Permalink_Manager_UI_Elements::generate_option_field( $lang_field_name, $lang_field_atts ) );
}
}
$default_permastruct_row = sprintf( "
%s: %s
%s
", __( "Default permastructure", "permalink-manager" ), esc_html( $default_permastruct ), __( "Restore default permastructure", "permalink-manager" ) );
$permastructure_settings = sprintf( "
%s %s
", __( "Permastructure settings", "permalink-manager" ), Permalink_Manager_UI_Elements::generate_option_field( "permastructure-settings[do_not_append_slug][$content_group][{$content_type_name}]", array( 'type' => 'single_checkbox', 'default' => 1, 'checkbox_label' => __( "Do not automatically append the slug", "permalink-manager" ) ) ) );
// Combine all HTML chunks
$html = sprintf( "
%s%s
", $permastruct_field, $buttons );
$html .= sprintf( "
%s%s%s
", $language_fields, $default_permastruct_row, $permastructure_settings );
$html .= sprintf( '
%s
', $tags_container_id, $available_tags );
$label_tag = sprintf( "
%s ", $field_name, $content_type_label );
return sprintf( "
%s%s ", esc_attr( $content_group ), esc_attr( $content_type_name ), esc_attr( $field_name ), 'field-container permastruct-row', $label_tag, $html );
}
/**
* Get the array with settings and render the HTML output
*/
public function output() {
$sidebar = "
";
$sidebar .= __( 'The current permastructures settings will be automatically applied only to the new posts & terms .', 'permalink-manager' );
$sidebar .= ' ';
/* translators: %s: Regenerate/reset admin URL */
$sidebar .= sprintf( __( 'To apply the new format to existing posts and terms , please use "Regenerate/reset " tool after you update the permastructure settings below.', 'permalink-manager' ), admin_url( 'tools.php?page=permalink-manager§ion=tools&subsection=regenerate_slugs' ) );
$sidebar .= "
";
return Permalink_Manager_UI_Elements::get_the_form( self::get_fields(), '', array( 'text' => __( 'Save permastructures', 'permalink-manager' ), 'class' => 'primary margin-top' ), $sidebar, array( 'action' => 'permalink-manager', 'name' => 'permalink_manager_permastructs' ) );
}
/**
* Get a list of all structure tags
*
* @param string $content_type
* @param bool $is_taxonomy
*
* @return string
*/
static function get_all_structure_tags( $content_type = '', $is_taxonomy = false ) {
if ( empty( $content_type ) ) {
return '';
}
$html = '';
$tags_groups = array(
'slug' => array(
'heading' => __( 'Native slug & title', 'permalink-manager' ),
'description' => __( 'The native slug is generated from the initial title and will not update automatically if the title is changed later. You can use the %native_title% tag to replace native slugs with actual titles, even if another content item has the same title.', 'permalink-manager' )
),
'meta' => array(
'heading' => __( 'Meta data', 'permalink-manager' ),
'description' => __( 'Using meta tags, you may add post-specific information like item IDs or author names to permalinks. This might be beneficial to news, events, and time-sensitive content where you can use date-based tags.', 'permalink-manager' )
),
'taxonomies' => array(
'heading' => __( 'Taxonomies', 'permalink-manager' ),
'description' => __( 'Custom permalinks can include taxonomy-based slugs such as categories, tags, and custom taxonomy terms. If a post belongs to multiple terms, the lowest-level one is used unless a specific term is selected as primary.', 'permalink-manager' )
),
'custom_fields' => array(
'heading' => __( 'Custom fields', 'permalink-manager' ),
'description' => __( 'Permalinks can be modified with custom fields to dynamically include extra data. For example, you can append product SKUs to WooCommerce URLs or include geolocation details in your custom post types\' permalinks.', 'permalink-manager' ),
'pro' => true
)
);
if ( ! $is_taxonomy ) {
$post_type_tag = Permalink_Manager_Permastructure_Functions::get_post_tag( $content_type );
$post_type_taxonomies = get_taxonomies( array( 'object_type' => array( $content_type ) ), 'objects' );
$tags_groups['slug']['tags'] = array(
$post_type_tag,
( $content_type !== 'post' ) ? '%postname%' : '',
'%native_title%'
);
$tags_groups['meta']['tags'] = array(
'%post_id%',
'%author%',
'%year%',
'%monthnum%',
'%monthname%',
'%day%',
'%hour%',
'%minute%',
'%second%',
'%post_type%'
);
if ( ! empty( $post_type_taxonomies ) ) {
$tags_groups['taxonomies']['tags'] = array();
foreach ( $post_type_taxonomies as $post_type_taxonomy ) {
$tags_groups['taxonomies']['tags'][] = sprintf( '%%%s%%', $post_type_taxonomy->name );
}
}
} else {
$taxonomy_tag = sprintf( '%%%s%%', $content_type );
$tags_groups['slug']['tags'] = array(
$taxonomy_tag,
'%term_name%',
'%native_title%'
);
$tags_groups['meta']['tags'] = array(
'%term_id%',
'%taxonomy%'
);
}
$tags_groups['custom_fields']['tags'] = array(
'%__custom_field_name%'
);
foreach ( $tags_groups as $tags_group ) {
if ( empty( $tags_group['tags'] ) ) {
continue;
}
$html .= sprintf( '
%s ', $tags_group['heading'] );
if ( ! empty( $tags_group['pro'] ) ) {
$pro_text = Permalink_Manager_UI_Elements::pro_text( true );
$html .= ( ! empty( $pro_text ) ) ? sprintf( '
%s ', $pro_text ) : '';
}
$html .= '
';
$html .= sprintf( '
', $tags_group['description'] );
$html .= '
';
foreach ( $tags_group['tags'] as $tag ) {
$html .= ( ! empty( $tag ) ) ? sprintf( '%1$s ', $tag ) : '';
}
$html .= '
';
$html .= '
';
}
return sprintf( "
%s
", $html );
}
}
views/permalink-manager-uri-editor.php 0000644 00000012552 15220204736 0014073 0 ustar 00 this_section ] = array(
'name' => __( 'URI Editor', 'permalink-manager' )
);
// Display separate section for each post type
$post_types = Permalink_Manager_Helper_Functions::get_post_types_array( 'full' );
foreach ( $post_types as $post_type_name => $post_type ) {
// Check if post type exists
if ( ! post_type_exists( $post_type_name ) ) {
continue;
}
$icon = ( class_exists( 'WooCommerce' ) && $post_type_name == 'product' ) ? "
" : "";
$admin_sections[ $this->this_section ]['subsections'][ $post_type_name ] = array(
'name' => "{$icon} {$post_type['label']}",
'function' => array( 'class' => 'Permalink_Manager_URI_Editor_Post', 'method' => 'display_admin_section' )
);
}
// Permalink Manager Pro: Display separate section for each taxonomy
$taxonomies = Permalink_Manager_Helper_Functions::get_taxonomies_array( 'full' );
foreach ( $taxonomies as $taxonomy_name => $taxonomy ) {
// Check if taxonomy exists
if ( ! taxonomy_exists( $taxonomy_name ) ) {
continue;
}
// Get the icon
$icon = ( class_exists( 'WooCommerce' ) && in_array( $taxonomy_name, array( 'product_tag', 'product_cat' ) ) ) ? "
" : "
";
$admin_sections[ $this->this_section ]['subsections']["tax_{$taxonomy_name}"] = array(
'name' => "{$icon} {$taxonomy['label']}",
'html' => Permalink_Manager_UI_Elements::pro_text(),
'pro' => true
);
}
// A little dirty hack to move wooCommerce product & taxonomies to the end of array
if ( class_exists( 'WooCommerce' ) ) {
foreach ( array( 'product', 'tax_product_tag', 'tax_product_cat' ) as $section_name ) {
if ( empty( $admin_sections[ $this->this_section ]['subsections'][ $section_name ] ) ) {
continue;
}
$section = $admin_sections[ $this->this_section ]['subsections'][ $section_name ];
unset( $admin_sections[ $this->this_section ]['subsections'][ $section_name ] );
$admin_sections[ $this->this_section ]['subsections'][ $section_name ] = $section;
}
}
return $admin_sections;
}
/**
* Display "Screen options"
*
* @param string $html
* @param string $screen
*
* @return string
*/
public function screen_options( $html, $screen ) {
global $active_section;
// Display the screen options only in "Permalink Editor"
if ( $active_section != $this->this_section ) {
return $html;
}
$button = get_submit_button( __( 'Apply', 'permalink-manager' ), 'primary', 'screen-options-apply', false );
$html = "
";
$screen_options = array(
'per_page' => array(
'type' => 'number',
'label' => __( 'Per page', 'permalink-manager' ),
'input_class' => 'settings-select'
),
'post_statuses' => array(
'type' => 'checkbox',
'label' => __( 'Post statuses', 'permalink-manager' ),
'choices' => Permalink_Manager_Helper_Functions::get_post_statuses(),
'select_all' => '',
'unselect_all' => '',
)
);
foreach ( $screen_options as $field_name => $field_args ) {
$field_args['container'] = 'screen-options';
$html .= Permalink_Manager_UI_Elements::generate_option_field( "screen-options[{$field_name}]", $field_args );
}
$html .= sprintf( " %s", $button );
return $html;
}
/**
* Trigger SQL query to get all items
*
* @param $sql_parts
* @param $current_page
* @param $is_taxonomy
*
* @return array
*/
public static function prepare_sql_query( $sql_parts, $current_page, $is_taxonomy = false ) {
global $permalink_manager_options, $wpdb;
$per_page = is_numeric( $permalink_manager_options['screen-options']['per_page'] ) ? $permalink_manager_options['screen-options']['per_page'] : 10;
$offset = ( $current_page - 1 ) * $per_page;
// Prepare the count SQL query
$count_query_parts = $sql_parts;
$count_query_parts['start'] = preg_replace( '/(SELECT.*FROM)/', 'SELECT COUNT(*) FROM', $count_query_parts['start'] );
$count_query = apply_filters( 'permalink_manager_filter_uri_editor_query', implode( "", $count_query_parts ), $count_query_parts, $is_taxonomy );
$total_items_raw = $wpdb->get_var( $count_query );
// Pagination support
$sql_query = implode( "", $sql_parts );
$sql_query .= sprintf( " LIMIT %d, %d", $offset, $per_page );
// Get items
$sql_query = apply_filters( 'permalink_manager_filter_uri_editor_query', $sql_query, $sql_parts, $is_taxonomy );
$all_items_raw = $wpdb->get_results( $sql_query, ARRAY_A );
$total_items = ( is_numeric( $total_items_raw ) ) ? (int) $total_items_raw : 0;
$all_items = ( ! empty( $all_items_raw ) ) ? $all_items_raw : array();
return array( $all_items, $total_items, $per_page );
}
}
views/permalink-manager-debug.php 0000644 00000014361 15220204736 0013076 0 ustar 00 __( 'Debug', 'permalink-manager' ),
'function' => array( 'class' => 'Permalink_Manager_Debug', 'method' => 'output' )
);
return $admin_sections;
}
/**
* Get a URL pointing to the "Debug" tab in Permalink Manager UI
*
* @param string $field
*
* @return string
*/
public function get_remove_settings_url( $field = '' ) {
return add_query_arg( array(
'section' => 'debug',
'remove-permalink-manager-settings' => $field,
'permalink-manager-nonce' => wp_create_nonce( 'permalink-manager' )
), Permalink_Manager_Admin_Functions::get_admin_url() );
}
/**
* Define and display HTML output of a new section with the "Debug" data
*
* @return string
*/
public function output() {
global $permalink_manager_options, $permalink_manager_permastructs, $permalink_manager_redirects, $permalink_manager_external_redirects;
// Get the permalinks array & count permalinks and calculate the size of array
$custom_permalinks = Permalink_Manager_URI_Functions::get_all_uris();
list ( $custom_permalinks_size, $custom_permalinks_count ) = Permalink_Manager_URI_Functions::get_all_uris( true );
if ( $custom_permalinks_count >= 1 ) {
$custom_permalinks_size = ( is_numeric( ( $custom_permalinks_size ) ) ) ? round( $custom_permalinks_size / 1024, 2 ) . __( 'kb', 'permalink-manager' ) : '-';
/* translators: 1: Custom permalinks count, 2: Custom permalinks array size */
$custom_permalinks_stats = sprintf( __( 'Custom permalinks count:
%1$s | Custom permalinks array size in DB:
%2$s ', 'permalink-manager' ), esc_html( $custom_permalinks_count ), esc_html( $custom_permalinks_size ) );
} else {
$custom_permalinks_stats = __( 'The custom permalinks array has not been stored in the database yet.', 'permalink-manager' );
}
$sections_and_fields = apply_filters( 'permalink_manager_debug_fields', array(
'debug-data' => array(
'section_name' => __( 'Debug data', 'permalink-manager' ),
'fields' => array(
'uris' => array(
'type' => 'textarea',
'description' => sprintf( '%s
%s
%s ', __( 'List of the URIs generated by this plugin.', 'permalink-manager' ), $custom_permalinks_stats, $this->get_remove_settings_url( 'uris' ), __( 'Remove all custom permalinks', 'permalink-manager' ) ),
'label' => __( 'Array with custom permalinks', 'permalink-manager' ),
'input_class' => 'short-textarea widefat',
'value' => ( $custom_permalinks ) ? print_r( $custom_permalinks, true ) : ''
),
'custom-redirects' => array(
'type' => 'textarea',
'description' => sprintf( '%s
%s ', __( 'List of custom redirects set-up by this plugin.', 'permalink-manager' ), $this->get_remove_settings_url( 'redirects' ), __( 'Remove all custom redirects', 'permalink-manager' ) ),
'label' => __( 'Array with redirects', 'permalink-manager' ),
'input_class' => 'short-textarea widefat',
'value' => ( $permalink_manager_redirects ) ? print_r( $permalink_manager_redirects, true ) : ''
),
'external-redirects' => array(
'type' => 'textarea',
'description' => sprintf( '%s
%s ', __( 'List of external redirects set-up by this plugin.', 'permalink-manager' ), $this->get_remove_settings_url( 'external-redirects' ), __( 'Remove all external redirects', 'permalink-manager' ) ),
'label' => __( 'Array with external redirects', 'permalink-manager' ),
'input_class' => 'short-textarea widefat',
'value' => ( $permalink_manager_external_redirects ) ? print_r( array_filter( $permalink_manager_external_redirects ), true ) : ''
),
'permastructs' => array(
'type' => 'textarea',
'description' => sprintf( '%s
%s ', __( 'List of permastructures set-up by this plugin.', 'permalink-manager' ), $this->get_remove_settings_url( 'permastructs' ), __( 'Remove all permastructures settings', 'permalink-manager' ) ),
'label' => __( 'Array with permastructures', 'permalink-manager' ),
'input_class' => 'short-textarea widefat',
'value' => ( $permalink_manager_permastructs ) ? print_r( $permalink_manager_permastructs, true ) : ''
),
'settings' => array(
'type' => 'textarea',
'description' => sprintf( '%s
%s ', __( 'List of plugin settings.', 'permalink-manager' ), $this->get_remove_settings_url( 'settings' ), __( 'Remove all plugin settings', 'permalink-manager' ) ),
'label' => __( 'Array with settings used in this plugin.', 'permalink-manager' ),
'input_class' => 'short-textarea widefat',
'value' => print_r( $permalink_manager_options, true )
)
)
)
) );
// Now get the HTML output
$output = '';
foreach ( $sections_and_fields as $section_id => $section ) {
$output .= ( isset( $section['section_name'] ) ) ? "
{$section['section_name']} " : "";
$output .= ( isset( $section['description'] ) ) ? "
{$section['description']}
" : "";
$output .= "
";
}
return $output;
}
}
views/permalink-manager-tools.php 0000644 00000032406 15220204736 0013150 0 ustar 00 __( 'Tools', 'permalink-manager' ),
'subsections' => array(
'duplicates' => array(
'name' => __( 'Permalink Duplicates', 'permalink-manager' ),
'function' => array( 'class' => 'Permalink_Manager_Tools', 'method' => 'duplicates_output' )
),
'find_and_replace' => array(
'name' => __( 'Find & Replace', 'permalink-manager' ),
'function' => array( 'class' => 'Permalink_Manager_Tools', 'method' => 'find_and_replace_output' )
),
'regenerate_slugs' => array(
'name' => __( 'Regenerate/Reset', 'permalink-manager' ),
'function' => array( 'class' => 'Permalink_Manager_Tools', 'method' => 'regenerate_slugs_output' )
),
'stop_words' => array(
'name' => __( 'Stop Words', 'permalink-manager' ),
'function' => array( 'class' => 'Permalink_Manager_UI_Elements', 'method' => 'pro_text' )
),
'import' => array(
'name' => __( 'Custom Permalinks', 'permalink-manager' ),
'function' => array( 'class' => 'Permalink_Manager_UI_Elements', 'method' => 'pro_text' )
)
)
);
return $admin_sections;
}
/**
* Display a warning message before the user changes the permalinks mode to "Native slugs"
*
* @return string
*/
public function display_instructions() {
return sprintf( '
%s ', __( 'A MySQL backup is highly recommended before using "Native slugs " mode!', 'permalink-manager' ) );
}
/**
* Display a list of all duplicated URIs and redirects
*
* @return string
*/
public function duplicates_output() {
// Get the duplicates & another variables
$all_duplicates = Permalink_Manager_Admin_Functions::get_all_duplicates();
$button_url = add_query_arg( array(
'section' => 'tools',
'subsection' => 'duplicates',
'clear-permalink-manager-uris' => 1,
'permalink-manager-nonce' => wp_create_nonce( 'permalink-manager' )
), Permalink_Manager_Admin_Functions::get_admin_url() );
$html = sprintf( "
%s ", __( "List of duplicated permalinks", "permalink-manager" ) );
$html .= wpautop( sprintf( "
%s ", $button_url, __( 'Fix custom permalinks & redirects', 'permalink-manager' ) ) );
if ( ! empty( $all_duplicates ) ) {
foreach ( $all_duplicates as $uri => $duplicates ) {
$html .= "
";
$html .= sprintf( '
', Permalink_Manager_Core_Functions::control_trailing_slashes( home_url( $uri ) ) );
$html .= "
";
foreach ( $duplicates as $item_id ) {
$html .= "";
// Detect duplicate type
preg_match( "/(redirect-([\d]+)_)?(?:(tax-)?([\d]*))/", $item_id, $parts );
$is_extra_redirect = ( ! empty( $parts[1] ) ) ? true : false;
$duplicate_type = ( $is_extra_redirect ) ? __( 'Extra Redirect', 'permalink-manager' ) : __( 'Custom permalink', 'permalink-manager' );
$detected_id = $parts[4];
// $detected_index = $parts[2];
$detected_term = ( ! empty( $parts[3] ) ) ? true : false;
$remove_link = ( $is_extra_redirect ) ? sprintf( " %s ", admin_url( "tools.php?page=permalink-manager§ion=tools&subsection=duplicates&remove-redirect={$item_id}" ), __( 'Remove Redirect', 'permalink-manager' ) ) : "";
// Get term
if ( $detected_term && ! empty( $detected_id ) ) {
$term = get_term( $detected_id );
if ( ! empty( $term->name ) ) {
$title = $term->name;
$edit_label = " " . __( "Edit term", "permalink-manager" );
$edit_link = get_edit_tag_link( $term->term_id, $term->taxonomy );
} else {
$title = __( "(Removed term)", "permalink-manager" );
$edit_label = " " . __( "Remove broken URI", "permalink-manager" );
$edit_link = admin_url( "tools.php?page=permalink-manager§ion=tools&subsection=duplicates&remove-uri=tax-{$detected_id}" );
}
} // Get post
else if ( ! empty( $detected_id ) ) {
$post = get_post( $detected_id );
if ( ! empty( $post->post_title ) && post_type_exists( $post->post_type ) ) {
$title = $post->post_title;
$edit_label = " " . __( "Edit post", "permalink-manager" );
$edit_link = get_edit_post_link( $post->ID );
} else {
$title = __( "(Removed post)", "permalink-manager" );
$edit_label = " " . __( "Remove broken URI", "permalink-manager" );
$edit_link = admin_url( "tools.php?page=permalink-manager§ion=tools&subsection=duplicates&remove-uri={$detected_id}" );
}
} else {
continue;
}
$html .= sprintf( '%2$s %3$s%4$s %5$s %6$s ', $edit_link, $title, " #{$detected_id} ", $duplicate_type, $edit_label, $remove_link );
$html .= " ";
}
$html .= "
";
$html .= "
";
}
} else {
$html .= sprintf( "
%s
", __( 'Congratulations! No duplicated URIs or Redirects found!', 'permalink-manager' ) );
}
return $html;
}
/**
* Generate a form for "Tools -> Find & replace" tool
*
* @return string
*/
public function find_and_replace_output() {
// Get all registered post types array & statuses
$all_post_statuses_array = Permalink_Manager_Helper_Functions::get_post_statuses();
$all_post_types = Permalink_Manager_Helper_Functions::get_post_types_array();
$all_taxonomies = Permalink_Manager_Helper_Functions::get_taxonomies_array();
$fields = apply_filters( 'permalink_manager_tools_fields', array(
'old_string' => array(
'label' => __( 'Find ...', 'permalink-manager' ),
'type' => 'text',
'container' => 'row',
'input_class' => 'widefat'
),
'new_string' => array(
'label' => __( 'Replace with ...', 'permalink-manager' ),
'type' => 'text',
'container' => 'row',
'input_class' => 'widefat'
),
'mode' => array(
'label' => __( 'Mode', 'permalink-manager' ),
'type' => 'select',
'container' => 'row',
'choices' => array(
'custom_uris' => __( 'Custom permalinks', 'permalink-manager' ),
'slugs' => __( 'Native slugs', 'permalink-manager' )
),
),
'content_type' => array(
'label' => __( 'Select content type', 'permalink-manager' ),
'type' => 'select',
'disabled' => true,
'pro' => true,
'container' => 'row',
'default' => 'post_types',
'choices' => array(
'post_types' => __( 'Post types', 'permalink-manager' ),
'taxonomies' => __( 'Taxonomies', 'permalink-manager' )
),
),
'post_types' => array(
'label' => __( 'Select post types', 'permalink-manager' ),
'type' => 'checkbox',
'container' => 'row',
'default' => array( 'post', 'page' ),
'choices' => $all_post_types,
'select_all' => '',
'unselect_all' => '',
),
'taxonomies' => array(
'label' => __( 'Select taxonomies', 'permalink-manager' ),
'type' => 'checkbox',
'container' => 'row',
'container_class' => 'hidden',
'default' => array( 'category', 'post_tag' ),
'choices' => $all_taxonomies,
'pro' => true,
'select_all' => '',
'unselect_all' => '',
),
'post_statuses' => array(
'label' => __( 'Select post statuses', 'permalink-manager' ),
'type' => 'checkbox',
'container' => 'row',
'default' => array( 'publish' ),
'choices' => $all_post_statuses_array,
'select_all' => '',
'unselect_all' => '',
),
'ids' => array(
'label' => __( 'Select IDs', 'permalink-manager' ),
'type' => 'text',
'container' => 'row',
//'disabled' => true,
'description' => __( 'To narrow the above filters you can type the post IDs (or ranges) here. E.g.
1-8, 10, 25 .', 'permalink-manager' ),
//'pro' => true,
'input_class' => 'widefat'
)
), 'find_and_replace' );
$sidebar = '
' . __( 'Important notices', 'permalink-manager' ) . ' ';
$sidebar .= self::display_instructions();
return Permalink_Manager_UI_Elements::get_the_form( $fields, 'columns-3', array( 'text' => __( 'Find and replace', 'permalink-manager' ), 'class' => 'primary margin-top' ), $sidebar, array( 'action' => 'permalink-manager', 'name' => 'find_and_replace' ), true, 'form-ajax' );
}
/**
* Generate a form for "Tools -> Regenerate/reset" tool
*
* @return string
*/
public function regenerate_slugs_output() {
// Get all registered post types array & statuses
$all_post_statuses_array = Permalink_Manager_Helper_Functions::get_post_statuses();
$all_post_types = Permalink_Manager_Helper_Functions::get_post_types_array();
$all_taxonomies = Permalink_Manager_Helper_Functions::get_taxonomies_array();
$fields = apply_filters( 'permalink_manager_tools_fields', array(
'mode' => array(
'label' => __( 'Mode', 'permalink-manager' ),
'type' => 'select',
'container' => 'row',
'choices' => array(
'custom_uris' => __( 'Regenerate custom permalinks', 'permalink-manager' ),
'slugs' => __( 'Regenerate native slugs', 'permalink-manager' ),
'native' => __( 'Use original URLs as custom permalinks', 'permalink-manager' )
),
),
'content_type' => array(
'label' => __( 'Select content type', 'permalink-manager' ),
'type' => 'select',
'disabled' => true,
'pro' => true,
'container' => 'row',
'default' => 'post_types',
'choices' => array(
'post_types' => __( 'Post types', 'permalink-manager' ),
'taxonomies' => __( 'Taxonomies', 'permalink-manager' )
),
),
'post_types' => array(
'label' => __( 'Select post types', 'permalink-manager' ),
'type' => 'checkbox',
'container' => 'row',
'default' => array( 'post', 'page' ),
'choices' => $all_post_types,
'select_all' => '',
'unselect_all' => '',
),
'taxonomies' => array(
'label' => __( 'Select taxonomies', 'permalink-manager' ),
'type' => 'checkbox',
'container' => 'row',
'container_class' => 'hidden',
'default' => array( 'category', 'post_tag' ),
'choices' => $all_taxonomies,
'pro' => true,
'select_all' => '',
'unselect_all' => '',
),
'post_statuses' => array(
'label' => __( 'Select post statuses', 'permalink-manager' ),
'type' => 'checkbox',
'container' => 'row',
'default' => array( 'publish' ),
'choices' => $all_post_statuses_array,
'select_all' => '',
'unselect_all' => '',
),
'ids' => array(
'label' => __( 'Select IDs', 'permalink-manager' ),
'type' => 'text',
'container' => 'row',
//'disabled' => true,
'description' => __( 'To narrow the above filters you can type the post IDs (or ranges) here. E.g.
1-8, 10, 25 .', 'permalink-manager' ),
//'pro' => true,
'input_class' => 'widefat'
)
), 'regenerate' );
$sidebar = '
' . __( 'Important notices', 'permalink-manager' ) . ' ';
$sidebar .= self::display_instructions();
return Permalink_Manager_UI_Elements::get_the_form( $fields, 'columns-3', array( 'text' => __( 'Regenerate', 'permalink-manager' ), 'class' => 'primary margin-top' ), $sidebar, array( 'action' => 'permalink-manager', 'name' => 'regenerate' ), true, 'form-ajax' );
}
/**
* Add "Preview mode" toggle to the end of options list in "Regenerate/rest" and "Find & replace"
*
* @param $fields
* @param $tool_name
*
* @return array
*/
public function add_preview_mode_toggle( $fields, $tool_name ) {
if ( is_array( $fields ) && in_array( $tool_name, array( 'regenerate', 'find_and_replace' ) ) ) {
$fields['preview_mode'] = array(
'label' => __( 'Preview mode', 'permalink-manager' ),
'type' => 'single_checkbox',
'container' => 'row',
'description' => __( 'Enable this option if you want to review the changes in "read mode" before saving them in the database.', 'permalink-manager' )
);
}
return $fields;
}
}
views/permalink-manager-uri-editor-post.php 0000644 00000032172 15220204736 0015056 0 ustar 00 'slug',
'plural' => 'slugs'
) );
$this->displayed_post_statuses = ( isset( $permalink_manager_options['screen-options']['post_statuses'] ) ) ? Permalink_Manager_Helper_Functions::prepare_array_for_sql_in( $permalink_manager_options['screen-options']['post_statuses'] ) : "'no-post-status'";
$this->displayed_post_types = ( $active_subsection == 'all' ) ? Permalink_Manager_Helper_Functions::prepare_array_for_sql_in( $permalink_manager_options['screen-options']['post_types'] ) : "'{$active_subsection}'";
}
/**
* Get the HTML output with the whole WP_List_Table
*
* @return string
*/
public function display_admin_section() {
$output = "
";
return $output;
}
/**
* Return an array of classes to be used in the HTML table
*
* @return array
*/
function get_table_classes() {
return array( 'widefat', 'striped', $this->_args['plural'] );
}
/**
* Add columns to the table
*
* @return array
*/
public function get_columns() {
return apply_filters( 'permalink_manager_uri_editor_columns', array(
'item_title' => __( 'Post title', 'permalink-manager' ),
'item_uri' => __( 'Custom permalink', 'permalink-manager' )
) );
}
/**
* Define sortable columns
*
* @return array
*/
public function get_sortable_columns() {
return array(
'item_title' => array( 'post_title', false )
);
}
/**
* Data inside the columns
*
* @param array $item
* @param string $column_name
*
* @return string
*/
public function column_default( $item, $column_name ) {
global $permalink_manager_options;
if ( Permalink_Manager_Helper_Functions::is_front_page( $item['ID'] ) ) {
$uri = '';
$permalink = Permalink_Manager_Permastructure_Functions::get_permalink_base( $item['ID'] );
$is_front_page = true;
} else {
$is_draft = ( $item["post_status"] == 'draft' ) ? true : false;
$uri = Permalink_Manager_URI_Functions_Post::get_post_uri( $item['ID'], true, $is_draft );
$uri = ( ! empty( $permalink_manager_options['general']['decode_uris'] ) ) ? urldecode( $uri ) : $uri;
$permalink = get_permalink( $item['ID'] );
$is_front_page = false;
}
$field_args_base = array( 'type' => 'text', 'value' => $uri, 'without_label' => true, 'input_class' => 'custom_uri', 'extra_atts' => "data-element-id=\"{$item['ID']}\"" );
$post_title = sanitize_text_field( $item['post_title'] );
$post_statuses_array = Permalink_Manager_Helper_Functions::get_post_statuses();
$post_statuses_array['inherit'] = __( 'Inherit (Attachment)', 'permalink-manager' );
$output = apply_filters( 'permalink_manager_uri_editor_column_content', '', $column_name, get_post( $item['ID'] ) );
if ( ! empty( $output ) ) {
return $output;
}
switch ( $column_name ) {
case 'item_uri':
// Get auto-update settings
$auto_update_val = get_post_meta( $item['ID'], "auto_update_uri", true );
$auto_update_uri = ( ! empty( $auto_update_val ) ) ? $auto_update_val : $permalink_manager_options["general"]["auto_update_uris"];
if ( $is_front_page ) {
$field_args_base['disabled'] = true;
$field_args_base['append_content'] = sprintf( '
%s %s
', '
', __( 'URI Editor is disabled because a custom permalink cannot be set for a front page.', 'permalink-manager' ) );
} else if ( Permalink_Manager_Helper_Functions::is_draft_excluded( (int) $item['ID'] ) ) {
$field_args_base['disabled'] = true;
$field_args_base['append_content'] = sprintf( '
%s %s
', '
', __( 'URI Editor disabled due to "Exclude drafts & pending posts" setting and the post status.', 'permalink-manager' ) );
} else if ( $auto_update_uri == 1 ) {
$field_args_base['readonly'] = true;
$field_args_base['append_content'] = sprintf( '
%s %s
', '
', __( 'The above permalink will be automatically updated and is locked for editing.', 'permalink-manager' ) );
} else if ( $auto_update_uri == 2 ) {
$field_args_base['disabled'] = true;
$field_args_base['append_content'] = sprintf( '
%s %s
', '
', __( 'URI Editor disabled due to "Permalink update" setting.', 'permalink-manager' ) );
}
$output = '
';
$output .= Permalink_Manager_UI_Elements::generate_option_field( "uri[{$item['ID']}]", $field_args_base );
$output .= "
";
$output .= sprintf( "
%s", $permalink, urldecode( $permalink ) );
$output .= '
';
return $output;
case 'item_title':
$output = esc_html( $post_title );
$output .= '';
$output .= '
';
$output .= sprintf( '
%2$s | ', esc_url( get_edit_post_link( $item['ID'] ) ), __( 'Edit', 'permalink-manager' ) );
$output .= sprintf( '
%2$s | ', esc_attr( $permalink ), __( 'View', 'permalink-manager' ), esc_html( $post_title ) );
$output .= sprintf( '
#%s ', esc_html( $item['ID'] ) );
$output .= '
';
return $output;
default:
return $item[ $column_name ];
}
}
/**
* The button that allows to save updated slugs
*/
function extra_tablenav( $which ) {
global $wpdb, $active_section, $active_subsection;
if ( $which == "top" ) {
$button_text = __( 'Save all the permalinks below', 'permalink-manager' );
$button_name = 'update_all_slugs[top]';
} else {
$button_text = __( 'Save all the permalinks above', 'permalink-manager' );
$button_name = 'update_all_slugs[bottom]';
}
$html = "
";
$html .= get_submit_button( $button_text, 'primary alignleft', $button_name, false, array( 'id' => 'doaction', 'value' => 'update_all_slugs' ) );
$html .= "
";
if ( $which == "top" ) {
// Filter by date
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$months = $wpdb->get_results( "SELECT DISTINCT month(post_date) AS m, year(post_date) AS y FROM {$wpdb->posts} WHERE post_status IN ($this->displayed_post_statuses) AND post_type IN ($this->displayed_post_types) ORDER BY post_date DESC", ARRAY_A );
if ( $months ) {
$choices = array( __( 'All dates', 'permalink-manager' ) );
foreach ( $months as $month ) {
$month_raw = sprintf( "%s-%s", $month['y'], $month['m'] );
$choices[ $month_raw ] = date_i18n( "F Y", strtotime( $month_raw ) );
}
$select_field = Permalink_Manager_UI_Elements::generate_option_field( 'month', array(
'type' => 'select',
'choices' => $choices,
'value' => ( isset( $_REQUEST['month'] ) ) ? sanitize_key( $_REQUEST['month'] ) : ''
) );
$html .= sprintf( '
%s
', $select_field );
}
$extra_fields = apply_filters( 'permalink_manager_uri_editor_extra_fields', '', 'posts' );
if ( $months || $extra_fields ) {
$html .= $extra_fields;
$html .= '
';
$html .= get_submit_button( __( "Filter", "permalink-manager" ), 'button', false, false, array( 'id' => 'filter-button', 'name' => 'filter-button' ) );
$html .= "
";
}
$html .= '
';
$html .= $this->search_box( __( 'Search', 'permalink-manager' ), 'search-input' );
$html .= '
';
}
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $html;
}
/**
* Display the search input field
*
* @return string
*/
public function search_box( $text = '', $input_id = '' ) {
$search_query = ( ! empty( $_REQUEST['s'] ) ) ? esc_attr( sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) ) : "";
$output = "
";
$output .= "{$text}: ";
$output .= Permalink_Manager_UI_Elements::generate_option_field( 's', array( 'value' => $search_query, 'type' => 'search' ) );
$output .= get_submit_button( $text, 'button', false, false, array( 'id' => 'search-submit', 'name' => 'search-submit' ) );
$output .= "
";
return $output;
}
/**
* Prepare the items for the table to process
*/
public function prepare_items() {
global $wpdb;
$columns = $this->get_columns();
$hidden = $this->get_hidden_columns();
$sortable = $this->get_sortable_columns();
$current_page = $this->get_pagenum();
// SQL query parameters
$order = ( isset( $_REQUEST['order'] ) && in_array( $_REQUEST['order'], array( 'asc', 'desc' ) ) ) ? sanitize_sql_orderby( wp_unslash( $_REQUEST['order'] ) ) : 'desc';
$orderby = ( isset( $_REQUEST['orderby'] ) ) ? sanitize_sql_orderby( wp_unslash( $_REQUEST['orderby'] ) ) : 'ID';
$search_query = ( ! empty( $_REQUEST['s'] ) ) ? esc_sql( sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) ) : "";
// Extra filters
$extra_filters = $attachment_support = '';
if ( ! empty( $_GET['month'] ) ) {
$month = gmdate( "n", strtotime( sanitize_key( $_GET['month'] ) ) );
$year = gmdate( "Y", strtotime( sanitize_key( $_GET['month'] ) ) );
$extra_filters .= "AND month(post_date) = {$month} AND year(post_date) = {$year}";
}
// Support for attachments
if ( strpos( $this->displayed_post_types, 'attachment' ) !== false ) {
$attachment_support = " OR (post_type = 'attachment')";
}
// Grab posts from database
$sql_parts['start'] = "SELECT * FROM {$wpdb->posts} AS p ";
if ( $search_query ) {
$sql_parts['where'] = "WHERE (LOWER(post_title) LIKE LOWER('%{$search_query}%') ";
// Search in array with custom URIs
$found = Permalink_Manager_URI_Functions::find_uri( $search_query, false, 'posts' );
if ( $found ) {
$sql_parts['where'] .= sprintf( "OR ID IN (%s)", implode( ',', $found ) );
}
$sql_parts['where'] .= " ) AND ((post_status IN ($this->displayed_post_statuses) AND post_type IN ($this->displayed_post_types)) {$attachment_support}) {$extra_filters} ";
} else {
$sql_parts['where'] = "WHERE ((post_status IN ($this->displayed_post_statuses) AND post_type IN ($this->displayed_post_types)) {$attachment_support}) {$extra_filters} ";
}
// Do not display excluded posts in Bulk URI Editor
$excluded_posts = Permalink_Manager_Helper_Functions::get_excluded_post_ids();
if ( ! empty( $excluded_posts ) && is_array( $excluded_posts ) ) {
$sql_parts['where'] .= sprintf( "AND ID NOT IN (%s) ", Permalink_Manager_Helper_Functions::prepare_array_for_sql_in( $excluded_posts ) );
}
$sql_parts['end'] = "ORDER BY {$orderby} {$order}";
list( $all_items, $total_items, $per_page ) = Permalink_Manager_URI_Editor::prepare_sql_query( $sql_parts, $current_page, false );
$this->set_pagination_args( array(
'total_items' => $total_items,
'per_page' => $per_page
) );
$this->_column_headers = array( $columns, $hidden, $sortable );
$this->items = $all_items;
}
/**
* Define hidden columns
*
* @return array
*/
public function get_hidden_columns() {
return array();
}
/**
* Sort the data
*
* @param mixed $a
* @param mixed $b
*
* @return int
*/
private function sort_data( $a, $b ) {
// Set defaults
$orderby = ( ! empty( $_GET['orderby'] ) ) ? sanitize_sql_orderby( wp_unslash( $_GET['orderby'] ) ) : 'post_title';
$order = ( ! empty( $_GET['order'] ) ) ? sanitize_sql_orderby( wp_unslash( $_GET['order'] ) ) : 'asc';
$result = strnatcasecmp( $a[ $orderby ], $b[ $orderby ] );
return ( $order === 'asc' ) ? $result : - $result;
}
}
views/permalink-manager-settings.php 0000644 00000053336 15220204736 0013655 0 ustar 00 __( 'Settings', 'permalink-manager' ),
'function' => array( 'class' => 'Permalink_Manager_Settings', 'method' => 'output' )
);
return $admin_sections;
}
/**
* Get the array with settings and render the HTML output
*
* @return string
*/
public function output() {
// Get all registered post types & taxonomies
$all_post_types = Permalink_Manager_Helper_Functions::get_post_types_array( null, null, true );
$all_taxonomies = Permalink_Manager_Helper_Functions::get_taxonomies_array( false, false, true );
$content_types = ( defined( 'PERMALINK_MANAGER_PRO' ) ) ? array( 'post_types' => $all_post_types, 'taxonomies' => $all_taxonomies ) : array( 'post_types' => $all_post_types );
$sections_and_fields = apply_filters( 'permalink_manager_settings_fields', array(
'general' => array(
'section_name' => __( 'General settings', 'permalink-manager' ),
'container' => 'row',
'name' => 'general',
'fields' => array(
'auto_update_uris' => array(
'type' => 'select',
'label' => __( 'Permalink update', 'permalink-manager' ),
'input_class' => '',
'choices' => array( 0 => __( 'Don\'t auto-update custom permalinks (default mode)', 'permalink-manager' ), 1 => __( 'Auto-update custom permalinks', 'permalink-manager' ), 2 => __( 'Disable custom permalinks for new posts/terms', 'permalink-manager' ) ),
'description' => sprintf( '
%s %s
%s', __( 'Custom permalinks in Permalink Manager will not be updated automatically to avoid overwriting individual modifications. If necessary, you can opt to change them every time a post/term is saved to match the Permastructure settings default format.', 'permalink-manager' ), __( 'If you select the third option, Permalink Manager will not generate new custom permalinks for newly added items. This lets you choose which pages will have custom permalinks and which will continue to use the original WordPress permalinks.', 'permalink-manager' ), __( 'The Permalink Manager editor allows you to select a different mode and override this global settings for specific posts and terms.', 'permalink-manager' ) )
),
'force_custom_slugs' => array(
'type' => 'select',
'label' => __( 'Slugs mode', 'permalink-manager' ),
'input_class' => 'settings-select',
'choices' => array( 0 => __( 'Use WordPress slugs (default mode)', 'permalink-manager' ), 1 => __( 'Use actual titles instead of WordPress slugs', 'permalink-manager' ), 2 => __( 'Inherit parents\' slugs', 'permalink-manager' ) ),
'description' => sprintf( '
%s %s
%s', __( 'Permalink Manager can generate custom permalinks using either WordPress slugs or actual titles.', 'permalink-manager' ), __( 'A slug is a permalink component that identifies a certain page. For example, "
shop " and "
sample-product " are two slugs in the permalink "
shop/sample-product ".', 'permalink-manager' ), __( 'WordPress slugs are generated automatically from the first title and remain the same even if the title is changed.', 'permalink-manager' ) )
),
'trailing_slashes' => array(
'type' => 'select',
'label' => __( 'Trailing slashes', 'permalink-manager' ),
'input_class' => 'settings-select',
'choices' => array( 0 => __( 'Use default settings', 'permalink-manager' ), 1 => __( 'Add trailing slashes', 'permalink-manager' ), 2 => __( 'Remove trailing slashes', 'permalink-manager' ) ),
'description' => sprintf( '
%s %s
%s', __( 'This option can be used to alter the native settings and control if trailing slash should be added or removed from the end of posts & terms permalinks.', 'permalink-manager' ), __( 'You can use this feature to either add or remove the slashes from end of WordPress permalinks.', 'permalink-manager' ), __( 'Please go to "
Redirect settings -> Trailing slashes redirect " to force the trailing slashes mode with redirect.', 'permalink-manager' ) )
)
)
),
'redirect' => array(
'section_name' => __( 'Redirect settings', 'permalink-manager' ),
'container' => 'row',
'name' => 'general',
'fields' => array(
'canonical_redirect' => array(
'type' => 'single_checkbox',
'label' => __( 'Canonical redirect', 'permalink-manager' ),
'input_class' => '',
'description' => sprintf( '
%s %s', __( 'Canonical redirect allows WordPress to "correct" the requested URL and redirect visitor to the canonical permalink.', 'permalink-manager' ), __( 'Permalink Manager uses canonical redirect to avoid "duplicate content" SEO issues by redirecting different permalinks that lead to the same content to a custom permalink set in the plugin.', 'permalink-manager' ) )
),
/*'endpoint_redirect' => array(
'type' => 'single_checkbox',
'label' => __('Redirect with endpoints', 'permalink-manager'),
'input_class' => '',
'description' => sprintf('%s',
__('
Please enable this option if you would like to copy the endpoint from source URL to the target URL during the canonical redirect. ', 'permalink-manager')
)
),*/ 'old_slug_redirect' => array(
'type' => 'single_checkbox',
'label' => __( 'Old slug redirect', 'permalink-manager' ),
'input_class' => '',
'description' => sprintf( '
%s %s', __( 'Old slug redirect is used by WordPress to provide a fallback for old version of slugs after they are changed.', 'permalink-manager' ), __( 'If enabled, the visitors trying to access the URL with the old slug will be redirected to the canonical permalink.', 'permalink-manager' ) )
),
'extra_redirects' => array(
'type' => 'single_checkbox',
'label' => __( 'Extra redirects (aliases)', 'permalink-manager' ),
'input_class' => '',
'pro' => true,
'disabled' => true,
'description' => sprintf( '%s
%s ', __( 'Please enable this option if you would like to manage additional custom redirects (aliases) in URI Editor for individual posts & terms.', 'permalink-manager' ), __( 'You can disable this feature if you use another plugin to control the redirects, eg. Yoast SEO Premium or Redirection.', 'permalink-manager' ) )
),
'setup_redirects' => array(
'type' => 'single_checkbox',
'label' => __( 'Save old custom permalinks as extra redirects', 'permalink-manager' ),
'input_class' => '',
'pro' => true,
'disabled' => true,
'description' => sprintf( '%s
%s ', __( 'If enabled, Permalink Manager will save the "extra redirect" for earlier version of custom permalink after you change it (eg. with URI Editor or Regenerate/reset tool).', 'permalink-manager' ), __( 'Please note that the new redirects will be saved only if "Extra redirects (aliases)" option is turned on above.', 'permalink-manager' ) )
),
'trailing_slashes_redirect' => array(
'type' => 'single_checkbox',
'label' => __( 'Trailing slashes redirect', 'permalink-manager' ),
'input_class' => '',
'description' => sprintf( '%s
%s ', __( 'Permalink Manager can force the trailing slashes settings in the custom permalinks with redirect.', 'permalink-manager' ), __( 'Please go to "
General settings -> Trailing slashes " to choose if trailing slashes should be added or removed from WordPress permalinks.', 'permalink-manager' ) )
),
'copy_query_redirect' => array(
'type' => 'single_checkbox',
'label' => __( 'Redirect with query parameters', 'permalink-manager' ),
'input_class' => '',
'description' => sprintf( '%s
%s', __( 'If enabled, the query parameters will be copied to the target URL when the redirect is triggered.', 'permalink-manager' ), __( 'Example:
https://example.com/product/old-product-url/?discount-code=blackfriday =>
https://example.com/new-product-url/?discount-code=blackfriday ', 'permalink-manager' ) )
),
'sslwww_redirect' => array(
'type' => 'single_checkbox',
'label' => __( 'Force HTTPS/WWW', 'permalink-manager' ),
'input_class' => '',
'description' => sprintf( '
%s %s', __( 'You can use Permalink Manager to force SSL or "www" prefix in WordPress permalinks.', 'permalink-manager' ), __( 'Please disable it if you encounter any redirect loop issues.', 'permalink-manager' ) )
),
'redirect' => array(
'type' => 'select',
'label' => __( 'Redirect mode', 'permalink-manager' ),
'input_class' => 'settings-select',
'choices' => array( 0 => __( 'Disable (Permalink Manager redirect functions)', 'permalink-manager' ), "301" => __( '301 redirect', 'permalink-manager' ), "302" => __( '302 redirect', 'permalink-manager' ) ),
'description' => sprintf( '%s
%s ', __( 'Permalink Manager includes a set of hooks that allow to extend the redirect functions used natively by WordPress to avoid 404 errors.', 'permalink-manager' ), __( 'You can disable this feature if you do not want Permalink Manager to trigger any additional redirect functions at all.', 'permalink-manager' ) )
)
)
),
'exclusion' => array(
'section_name' => __( 'Exclusion settings', 'permalink-manager' ),
'container' => 'row',
'name' => 'general',
'fields' => array(
'partial_disable' => array(
'type' => 'checkbox',
'label' => __( 'Exclude content types', 'permalink-manager' ),
'choices' => $content_types,
'description' => __( 'Permalink Manager will ignore and not filter the custom permalinks of all selected above post types & taxonomies.', 'permalink-manager' )
),
'partial_disable_strict' => array(
'type' => 'single_checkbox',
'label' => __( '"Exclude content types" strict mode', 'permalink-manager' ),
'description' => __( 'If this option is enabled, any custom post types and taxonomies with the "
query_var " and "
rewrite " attributes set to "
false " will be excluded from the plugin and hence will not be shown in the "
Exclude content types " options.', 'permalink-manager' )
),
'exclude_post_ids' => array(
'type' => 'text',
'label' => __( 'Exclude posts/pages by ID', 'permalink-manager' ),
'input_class' => 'widefat',
'description' => sprintf( '%s
%s', __( 'Specify the IDs of posts/pages for which you want to preserve the original WordPress URLs instead of applying custom permalinks.', 'permalink-manager' ), __( 'Enter single IDs (e.g., "
4, 8, 15, 16 "), ID ranges (e.g., "
23-42 "), or a combination of both.', 'permalink-manager' ) )
),
'exclude_term_ids' => array(
'type' => 'text',
'label' => __( 'Exclude terms by ID', 'permalink-manager' ),
'input_class' => 'widefat',
'pro' => true,
'disabled' => true,
'description' => sprintf( '%s
%s', __( 'Specify the IDs of categories/terms for which you want to preserve the original WordPress URLs instead of applying custom permalinks.', 'permalink-manager' ), __( 'Enter single IDs (e.g., "
4, 8, 15, 16 "), ID ranges (e.g., "
23-42 "), or a combination of both.', 'permalink-manager' ) )
),
/*'exclude_query_vars' => array(
'type' => 'text',
'label' => __( 'Non-redirectable query variables', 'permalink-manager' ),
'placeholder' => 'e.g. um_user, um_tab',
'input_class' => 'widefat',
'description' => __( 'Use this field to exclude specific query variables from triggering a redirect when Permalink Manager detects permalinks. To prevent the redirect on dynamic sections (e.g. profile tabs), you can enter the variable used by the third-party plugin (e.g.
um_user for Ultimate Member plugin).', 'permalink-manager' )
),*/ 'ignore_drafts' => array(
'type' => 'select',
'label' => __( 'Exclude drafts & pending posts', 'permalink-manager' ),
'choices' => array( 0 => __( 'Do not exclude', 'permalink-manager' ), 1 => __( 'Exclude drafts', 'permalink-manager' ), 2 => __( 'Exclude drafts & pending posts', 'permalink-manager' ) ),
'description' => __( 'If enabled, custom permalinks for posts marked as "draft" or "pending" will not be created.', 'permalink-manager' )
)
)
),
'third_parties' => array(
'section_name' => __( 'Third party plugins', 'permalink-manager' ),
'container' => 'row',
'name' => 'general',
'fields' => array(
'fix_language_mismatch' => array(
'type' => 'select',
'label' => __( 'WPML/Polylang fix language mismatch', 'permalink-manager' ),
'input_class' => '',
'choices' => array( 0 => __( 'Disable', 'permalink-manager' ), 1 => __( 'Load the language variant of the requested page', 'permalink-manager' ), 2 => __( 'Redirect to the language variant of the requested page', 'permalink-manager' ) ),
'class_exists' => array( 'SitePress', 'Polylang' ),
'description' => __( 'The plugin may load the relevant translation or trigger the canonical redirect when a custom permalink is detected, but the URL language code does not match the detected item\'s language code. ', 'permalink-manager' )
),
'wpml_support' => array(
'type' => 'single_checkbox',
'label' => __( 'WPML compatibility functions', 'permalink-manager' ),
'input_class' => '',
'class_exists' => array( 'SitePress' ),
'description' => __( 'Please disable this feature if the language code in the custom permalinks is incorrect.', 'permalink-manager' )
),
'wpml_translate_mode' => array(
'type' => 'single_checkbox',
'label' => __( 'Edit permalinks in WPML\'s Translation Editor', 'permalink-manager' ),
'input_class' => '',
'class_exists' => array( 'SitePress' ),
'description' => __( 'If enabled, you will be able to
manually edit custom permalinks directly while translating the content in WPML.', 'permalink-manager' )
),
'pmxi_support' => array(
'type' => 'single_checkbox',
'label' => __( 'WP All Import/Export support', 'permalink-manager' ),
'input_class' => '',
'class_exists' => array( 'PMXI_Plugin', 'PMXE_Plugin' ),
'description' => __( 'If disabled, the custom permalinks
will not be saved for the posts imported with WP All Import plugin.', 'permalink-manager' )
),
'um_support' => array(
'type' => 'single_checkbox',
'label' => __( 'Ultimate Member support', 'permalink-manager' ),
'input_class' => '',
'class_exists' => 'UM',
'description' => __( 'If enabled, Permalink Manager will detect the additional Ultimate Member pages (eg. "account" sections).', 'permalink-manager' )
),
'rankmath_redirect' => array(
'type' => 'single_checkbox',
'label' => __( 'RankMath\'s "Redirections" fix redirect conflict', 'permalink-manager' ),
'input_class' => '',
'description' => sprintf( '%s
%s', __( 'If enabled, the Permalink Manager plugin
will stop a redirect set with the RankMath SEO plugin\'s "Source URLs" if this URL is already being used as a custom permalink by any post or term.', 'permalink-manager' ), __( 'This prevents redirect loops when both plugins manage redirects on the same URL.', 'permalink-manager' ) )
),
'yoast_breadcrumbs' => array(
'type' => 'single_checkbox',
'label' => __( 'Breadcrumbs support', 'permalink-manager' ),
'input_class' => '',
'description' => __( 'If enabled, the HTML breadcrumbs will be filtered by Permalink Manager to mimic the current URL structure.
Works with:
WooCommerce, Yoast SEO, Slim Seo, RankMath and SEOPress breadcrumbs.', 'permalink-manager' )
),
'primary_category' => array(
'type' => 'single_checkbox',
'label' => __( '"Primary category" support', 'permalink-manager' ),
'input_class' => '',
'description' => __( 'If enabled, Permalink Manager will use the "primary category" for the default post permalinks.
Works with:
Yoast SEO, The SEO Framework, RankMath and SEOPress .', 'permalink-manager' )
),
)
),
'advanced' => array(
'section_name' => __( 'Advanced settings', 'permalink-manager' ),
'container' => 'row',
'name' => 'general',
'fields' => array(
'show_native_slug_field' => array(
'type' => 'single_checkbox',
'label' => __( 'Show "Native slug" field in URI Editor', 'permalink-manager' )
),
'pagination_redirect' => array(
'type' => 'select',
'label' => __( 'Handling non-existent pagination pages', 'permalink-manager' ),
'choices' => array( 0 => __( 'Stop canonical redirect without forcing "404" status code', 'permalink-manager' ), 1 => __( 'Stop canonical redirect and force "404" status code', 'permalink-manager' ), 2 => __( 'Allow canonical redirect without forcing "404" status code', 'permalink-manager' ) ),
'description' => __( 'Decide if you would like the plugin to force a "404" error or allow canonical redirect for pagination pages that do not exist.
If you experience any issues with pagination pages, please select the first option. ', 'permalink-manager' )
),
'disable_slug_sanitization' => array(
'type' => 'select',
'label' => __( 'Strip special characters', 'permalink-manager' ),
'input_class' => 'settings-select',
'choices' => array( 0 => __( 'Yes, use native settings', 'permalink-manager' ), 1 => __( 'No, keep special characters (.,|_+) in the slugs', 'permalink-manager' ) ),
'description' => __( 'If enabled only alphanumeric characters, underscores and dashes will be allowed for post/term slugs.', 'permalink-manager' )
),
'keep_accents' => array(
'type' => 'select',
'label' => __( 'Convert accented letters', 'permalink-manager' ),
'input_class' => 'settings-select',
'choices' => array( 0 => __( 'Yes, use native settings', 'permalink-manager' ), 1 => __( 'No, keep accented letters in the slugs', 'permalink-manager' ) ),
'description' => __( 'If enabled, all the accented letters will be replaced with their non-accented equivalent (eg. Å => A, Æ => AE, Ø => O, Ć => C).', 'permalink-manager' )
),
'edit_uris_cap' => array(
'type' => 'select',
'label' => __( 'URI Editor role capability', 'permalink-manager' ),
'choices' => array( 'edit_theme_options' => __( 'Administrator (edit_theme_options)', 'permalink-manager' ), 'publish_pages' => __( 'Editor (publish_pages)', 'permalink-manager' ), 'publish_posts' => __( 'Author (publish_posts)', 'permalink-manager' ), 'edit_posts' => __( 'Contributor (edit_posts)', 'permalink-manager' ) ),
/* translators: %s: WordPress.org docs reference */ 'description' => sprintf( __( 'Only the users who have selected capability will be able to access URI Editor.
The list of capabilities
can be found here .', 'permalink-manager' ), 'https://wordpress.org/support/article/roles-and-capabilities/#capability-vs-role-table' )
),
'force_unique_uris' => array(
'type' => 'single_checkbox',
'label' => __( 'Force unique custom permalinks', 'permalink-manager' ),
'description' => __( 'Enable this option if you want the plugin to automatically append a numeric suffix (e.g. -2, -3) to duplicated permalinks.', 'permalink-manager' )
),
'auto_fix_duplicates' => array(
'type' => 'single_checkbox',
'label' => __( 'Automatically fix broken URIs', 'permalink-manager' ),
'description' => sprintf( '%s', __( 'Enable this option if you would like to automatically remove redundant permalinks & duplicated redirects during page load.', 'permalink-manager' ) )
),
'debug_mode' => array(
'type' => 'single_checkbox',
'label' => __( 'Debug mode', 'permalink-manager' ),
'description' => __( 'Debug mode allows access to extra debug info by adding
?debug_url to the URL for plugin troubleshooting.', 'permalink-manager' )
)
)
)
) );
return Permalink_Manager_UI_Elements::get_the_form( $sections_and_fields, 'tabs', array( 'text' => __( 'Save settings', 'permalink-manager' ), 'class' => 'primary margin-top' ), '', array( 'action' => 'permalink-manager', 'name' => 'permalink_manager_options' ) );
}
}
core/permalink-manager-gutenberg.php 0000644 00000004441 15220204736 0013563 0 ustar 00 post_type ) || empty( $post->post_type ) ) {
return;
}
// Stop the hook (if needed)
$show_uri_editor = apply_filters( "permalink_manager_show_uri_editor_post", true, $post, $post->post_type );
if ( ! $show_uri_editor ) {
return;
}
// Check the user capabilities
if ( Permalink_Manager_Admin_Functions::current_user_can_edit_uris() === false ) {
return;
}
// Check if the post is excluded
if ( ! empty( $post->ID ) && Permalink_Manager_Helper_Functions::is_post_excluded( $post ) ) {
return;
}
add_meta_box( 'permalink-manager', __( 'Permalink Manager', 'permalink-manager' ), array( $this, 'get_uri_editor' ), '', 'side', 'high' );
// wp_enqueue_script( 'permalink-manager-gutenberg', PERMALINK_MANAGER_URL . '/out/permalink-manager-gutenberg.js', array( 'wp-plugins', 'wp-edit-post', 'wp-i18n', 'wp-element' ) );
// wp_enqueue_style( 'permalink-manager-gutenberg', PERMALINK_MANAGER_URL . '/out/permalink-manager-gutenberg.css', array(), PERMALINK_MANAGER_VERSION );
}
/**
* Display the URI Editor for specific post
*
* @param WP_Post|int $post
*/
public function get_uri_editor( $post = null ) {
if ( empty( $post->ID ) && empty( $_REQUEST['post_id'] ) ) {
return;
} else if ( ! empty( $_REQUEST['post_id'] ) && is_numeric( $_REQUEST['post_id'] ) ) {
$post = get_post( $_REQUEST['post_id'] );
}
// Check if the user can edit this post
if ( ! empty( $post->ID ) && current_user_can( 'edit_post', $post->ID ) ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo ( $post ) ? Permalink_Manager_UI_Elements::display_uri_box( $post, true ) : '';
}
if ( wp_doing_ajax() && isset( $_GET['action'] ) && $_GET['action'] == 'pm_get_uri_editor' ) {
die();
}
}
} core/permalink-manager-core-functions.php 0000644 00000117442 15220204736 0014545 0 ustar 00 comments_pagination_base}-([\d]+)/", $uri_parts['uri'], $regex_parts );
if ( ! empty( $regex_parts[2] ) ) {
$uri_parts['uri'] = $regex_parts[1];
$uri_parts['endpoint'] = 'cpage';
$uri_parts['endpoint_value'] = $regex_parts[2];
}
// Support pagination endpoint
if ( ! empty( $wp_rewrite->pagination_base ) && $uri_parts['endpoint'] == $wp_rewrite->pagination_base ) {
$uri_parts['endpoint'] = 'page';
}
// Stop the function if $uri_parts is empty
if ( empty( $uri_parts ) ) {
return $query;
}
// Store the URI parts in a separate global variable
$pm_query = $uri_parts;
// Get the URI parts from REGEX parts
// $lang = $uri_parts['lang'];
$uri = $uri_parts['uri'];
$endpoint = $uri_parts['endpoint'];
$endpoint_value = $uri_parts['endpoint_value'];
// Trim slashes
$uri = trim( $uri, "/" );
// Ignore URLs with no URI grabbed
if ( empty( $uri ) ) {
return $query;
}
// Check what content type should be loaded in case of duplicate ("posts" or "terms")
$duplicates_priority = apply_filters( 'permalink_manager_duplicates_priority', false );
/**
* 2. Check if the requested URI matches any custom permalink assigned to a post or term
*/
$uri_query_iteration = 1;
$element_object = '';
$excluded_ids = array();
do {
// Store an array with custom permalinks in a separate variable
$all_uris = $permalink_manager_uris;
// Remove empty rows
$all_uris = array_filter( $all_uris );
// In case of multiple elements using the same URI, the function will follow the "permalink_manager_duplicates_priority" filter value to determine whether terms or posts should be ignored
if ( $duplicates_priority ) {
$duplicated_uris = array_keys( $all_uris, $uri );
$duplicates_removed = 0;
$duplicates_count = count( $duplicated_uris );
if ( $duplicates_count > 1 ) {
foreach ( $duplicated_uris as $duplicated_uri_id ) {
if ( ( $duplicates_priority == 'posts' && ! is_numeric( $duplicated_uri_id ) ) || ( $duplicates_priority !== 'posts' && is_numeric( $duplicated_uri_id ) ) ) {
$duplicates_removed ++;
if ( $duplicates_removed < $duplicates_count ) {
$excluded_ids[] = $duplicated_uri_id;
}
}
}
}
}
// If the element was excluded in the previous iteration add it to the array
if ( ! empty( $excluded ) ) {
$excluded_ids[] = $excluded;
}
$excluded = '';
// Exclude all the element detected in the previous iterations
if ( ! empty( $excluded_ids ) ) {
$excluded_ids = array_unique( $excluded_ids );
foreach ( $excluded_ids as $excluded_element ) {
unset( $all_uris[ $excluded_element ] );
}
}
// Flip array for better performance
$all_uris = array_flip( $all_uris );
// Attempt 1.
// Find the element ID
$element_id = isset( $all_uris[ $uri ] ) ? $all_uris[ $uri ] : false;
// Attempt 2.
// Decode both request URI & URIs array & make them lowercase (and save in a separate variable)
if ( empty( $element_id ) ) {
$uri = strtolower( urldecode( $uri ) );
foreach ( $all_uris as $raw_uri => $uri_id ) {
$raw_uri = strtolower( urldecode( $raw_uri ) );
$all_uris[ $raw_uri ] = $uri_id;
}
$element_id = isset( $all_uris[ $uri ] ) ? $all_uris[ $uri ] : $element_id;
}
// Attempt 3.
// Check custom permalinks with endpoints included
if ( $deep_detect_enabled && ! empty( $uri_parts['endpoint_value'] ) ) {
// Check again in case someone used post/tax IDs instead of slugs
if ( empty( $uri_parts['endpoint'] ) && is_numeric( $uri_parts['endpoint_value'] ) ) {
$uri_alt = sprintf( '%s/%s', $uri, $uri_parts['endpoint_value'] );
} // Check again for attachments' custom permalinks
else if ( ! empty( $uri_parts['endpoint'] ) && $uri_parts['endpoint'] == 'attachment' ) {
$uri_alt = sprintf( '%s/%s/%s', $uri, $uri_parts['endpoint'], $uri_parts['endpoint_value'] );
}
if ( ! empty( $uri_alt ) && isset( $all_uris[ $uri_alt ] ) ) {
$element_id = $all_uris[ $uri_alt ];
$endpoint_value = $endpoint = "";
}
}
// Allow to filter the item_id by third-parties after initial detection
$element_id = apply_filters( 'permalink_manager_detected_element_id', $element_id, $uri_parts, $request_url );
// Clear the original query before it is filtered
$query = ( $element_id ) ? array() : $query;
/**
* 2A. Custom URI assigned to taxonomy
*/
if ( strpos( $element_id, 'tax-' ) !== false ) {
// Remove the "tax-" prefix
$term_element_id = intval( preg_replace( "/[^0-9]/", "", $element_id ) );
// Filter detected post ID
$term_element_id = apply_filters( 'permalink_manager_detected_term_id', $term_element_id, $uri_parts, true, $old_query );
// Get the variables to filter wp_query and double-check if taxonomy exists
$term = $element_object = ( ! empty( $term_element_id ) && is_numeric( $term_element_id ) ) ? get_term( $term_element_id ) : false;
$term_taxonomy = ( ! empty( $term->taxonomy ) ) ? $term->taxonomy : false;
$term_taxonomy_object = ( ! empty( $term_taxonomy ) ) ? get_taxonomy( $term_taxonomy ) : '';
// Check if term is allowed
$disabled = ( $term_taxonomy_object && Permalink_Manager_Helper_Functions::is_term_excluded( $term ) ) ? true : false;
// Proceed only if the term is not removed and its taxonomy is not disabled
if ( ! $disabled && $term_taxonomy_object ) {
$term_ancestors = get_ancestors( $element_id, $term_taxonomy );
$final_uri = $term->slug;
// Fix for hierarchical terms
if ( ! empty( $term_ancestors ) ) {
foreach ( $term_ancestors as $parent_id ) {
$parent = get_term( $parent_id, $term_taxonomy );
if ( ! empty( $parent->slug ) ) {
$final_uri = $parent->slug . '/' . $final_uri;
}
}
}
if ( empty( $term_taxonomy_object->query_var ) ) {
$query["taxonomy"] = $term_taxonomy;
$query["term"] = $term->slug;
} else {
$query[ $term_taxonomy_object->query_var ] = $term->slug;
}
} else if ( $disabled ) {
$broken_uri = true;
$query = $old_query;
$excluded = $element_id;
} else {
$query = $old_query;
$excluded = $element_id;
}
} /**
* 2B. Custom URI assigned to post/page/CPT item
*/ else if ( isset( $element_id ) && is_numeric( $element_id ) ) {
// Fix for revisions
$is_revision = wp_is_post_revision( $element_id );
if ( $is_revision ) {
$revision_id = $element_id;
$element_id = $is_revision;
}
// Filter detected post ID
$post_element_id = apply_filters( 'permalink_manager_detected_post_id', $element_id, $uri_parts, false, $old_query );
$post_to_load = $element_object = ( ! empty( $post_element_id ) && is_numeric( $post_element_id ) ) ? get_post( $post_element_id ) : false;
$final_uri = ( ! empty( $post_to_load->post_name ) ) ? $post_to_load->post_name : false;
$post_type = ( ! empty( $post_to_load->post_type ) ) ? $post_to_load->post_type : false;
// Check if post is allowed
$disabled = ( $post_type && Permalink_Manager_Helper_Functions::is_post_excluded( $post_to_load, true ) ) ? true : false;
// Proceed only if the term is not removed and its taxonomy is not disabled
if ( ! $disabled && $post_type ) {
$post_type_object = get_post_type_object( $post_type );
// Fix for hierarchical CPT & pages
if ( ! ( empty( $post_to_load->ancestors ) ) && ! empty( $post_type_object->hierarchical ) ) {
foreach ( $post_to_load->ancestors as $parent ) {
$parent = get_post( $parent );
if ( $parent && $parent->post_name ) {
$final_uri = $parent->post_name . '/' . $final_uri;
}
}
}
// Alter the final query array
if ( $post_to_load->post_status == 'private' && ( ! is_user_logged_in() || current_user_can( 'read_private_posts', $element_id ) !== true ) ) {
$element_id = 0;
$query = $old_query;
} else if ( $post_to_load->post_status == 'draft' || empty( $final_uri ) ) {
// A. The draft permalinks should be allowed for logged-in users
if ( is_user_logged_in() ) {
if ( $post_type == 'page' ) {
$query['page_id'] = $element_id;
} else {
$query['p'] = $element_id;
}
$query['preview'] = true;
$query['post_type'] = $post_type;
} // B. The draft permalinks should be disabled for non-logged-in visitors
else if ( $post_to_load->post_status == 'draft' ) {
$query['pagename'] = '-';
$query['error'] = '404';
$element_id = 0;
} else {
$query = $old_query;
$excluded = $element_id;
}
} else if ( $post_type == 'page' ) {
$query['pagename'] = $final_uri;
// $query['post_type'] = $post_type;
} else if ( $post_type == 'post' ) {
$query['name'] = $final_uri;
} else if ( $post_type == 'attachment' ) {
$query['attachment'] = $final_uri;
} else {
// Get the query var
$query_var = ( ! empty( $post_type_object->query_var ) ) ? $post_type_object->query_var : $post_type;
$query['name'] = $final_uri;
$query['post_type'] = $post_type;
$query[ $query_var ] = $final_uri;
}
} else if ( $disabled ) {
$broken_uri = true;
$query = $old_query;
$excluded = $element_id;
} else {
$query = $old_query;
$excluded = $element_id;
}
}
// Auto-remove removed term custom URI & redirects (works if enabled in plugin settings)
if ( ! empty( $broken_uri ) && ( ! empty( $permalink_manager_options['general']['auto_fix_duplicates'] ) ) && $permalink_manager_options['general']['auto_fix_duplicates'] == 1 ) {
// Do not trigger if WP Rocket cache plugin is turned on
if ( ! defined( 'WP_ROCKET_VERSION' ) && is_array( $permalink_manager_uris ) ) {
$broken_element_id = ( ! empty( $revision_id ) ) ? $revision_id : $element_id;
$remove_broken_uri = ( ! empty( $broken_element_id ) ) ? Permalink_Manager_Actions::force_clear_single_element_uris_and_redirects( $broken_element_id ) : '';
// Reload page if success
if ( $remove_broken_uri && ! headers_sent() ) {
header( "Refresh:0" );
exit();
}
}
}
// Overwrite the detect function and decide whether to exclude the detected item
$excluded = apply_filters( 'permalink_manager_excluded_element_id', $excluded, $element_object, $old_query, $pm_query );
// Make sure the loop does not execute infinitely (limit it to 20 iterations)
$uri_query_iteration ++;
if ( $uri_query_iteration === 25 ) {
break;
}
} // If the detected element was excluded repeat the URI query and try to find a new one
while ( ! empty( $excluded ) );
/**
* 3A. Endpoints
*/
if ( ! empty( $element_id ) && empty( $disabled ) && ( ! empty( $endpoint ) || ! empty( $endpoint_value ) ) ) {
if ( is_array( $endpoint ) ) {
foreach ( $endpoint as $endpoint_name => $endpoint_value ) {
$query[ $endpoint_name ] = $endpoint_value;
}
} else if ( $endpoint == 'feed' ) {
$feed_rewrite = true;
// Check if /feed/ endpoint is allowed for selected post type or taxonomy
if ( ! empty( $post_type_object ) && is_array( $post_type_object->rewrite ) && empty( $post_type_object->rewrite['feeds'] ) ) {
$feed_rewrite = false;
}
if ( $feed_rewrite ) {
$query[ $endpoint ] = 'feed';
} else {
$element_id = '';
$query = array(
'error' => 404
);
}
} else if ( $endpoint == 'embed' ) {
$query[ $endpoint ] = true;
} else if ( $endpoint == 'page' ) {
$endpoint = 'paged';
if ( is_numeric( $endpoint_value ) ) {
$query[ $endpoint ] = $endpoint_value;
} else {
$query = $old_query;
}
} else if ( $endpoint == 'trackback' ) {
$endpoint = 'tb';
$query[ $endpoint ] = 1;
} else if ( empty( $endpoint ) && is_numeric( $endpoint_value ) ) {
$query['page'] = $endpoint_value;
} else {
$query[ $endpoint ] = $endpoint_value;
}
// Fix for attachments
if ( ! empty( $query['attachment'] ) ) {
$query = array( 'attachment' => $query['attachment'], 'do_not_redirect' => 1 );
} else if ( isset( $query['attachment'] ) ) {
$query = $old_query;
$element_id = false;
}
}
/**
* 3B. Endpoints - check if any endpoint is set with $_GET parameter
*/
if ( ! empty( $element_id ) && $deep_detect_enabled && ! empty( $_GET ) ) {
$get_endpoints = array_intersect( $wp->public_query_vars, array_keys( $_GET ) );
if ( ! empty( $get_endpoints ) ) {
// Append query vars from $_GET parameters
foreach ( $get_endpoints as $endpoint ) {
// Numeric endpoints
$endpoint_value = ( in_array( $endpoint, array( 'page', 'paged', 'attachment_id' ) ) ) ? filter_var( $_GET[ $endpoint ], FILTER_SANITIZE_NUMBER_INT ) : $_GET[ $endpoint ];
// Ignore page endpoint if its value is empty or equal to 1
if ( in_array( $endpoint, array( 'page', 'paged' ) ) && ( empty( $endpoint_value ) || $endpoint_value == 1 ) ) {
continue;
}
// Replace whitespaces with '+' (for YITH WooCommerce Ajax Product Filter URLs only) and sanitize the value
$endpoint_value = ( isset( $_GET['yith_wcan'] ) ) ? preg_replace( '/\s+/', '+', $endpoint_value ) : $endpoint_value;
$query[ $endpoint ] = sanitize_text_field( $endpoint_value );
}
}
}
/**
* 4. Set global with detected item id
*/
if ( ! empty( $element_id ) && empty( $disabled ) && empty( $excluded ) ) {
if ( ! empty( $element_object->taxonomy ) ) {
$pm_query['id'] = $element_object->term_id;
$content_type = "Taxonomy: {$element_object->taxonomy}";
} else if ( ! empty( $element_object->post_type ) ) {
$pm_query['id'] = $element_object->ID;
$content_type = "Post type: {$element_object->post_type}";
}
// If language mismatch is detected do not set 'do_not_redirect' to allow canonical redirect
if ( is_array( $query ) && ( empty( $pm_query['flag'] ) || $pm_query['flag'] !== 'language_mismatch' ) ) {
$query['do_not_redirect'] = 1;
}
}
}
/**
* 5. Debug data
*/
if ( empty ( $element_object ) || empty ( $content_type ) ) {
$content_type = $element_object = '';
}
$uri_parts = ( ! empty( $uri_parts ) ) ? $uri_parts : '';
$query = apply_filters( 'permalink_manager_filter_query', $query, $old_query, $uri_parts, $pm_query, $content_type, $element_object );
if ( $return_object && ! empty( $element_object ) ) {
return $element_object;
} else {
return $query;
}
}
/**
* Trailing slash & remove BOM and double slashes
*
* @param string $permalink
*
* @return string
*/
static function control_trailing_slashes( $permalink ) {
global $permalink_manager_options;
// Ignore empty & numeric permalinks
if ( empty( $permalink ) || is_numeric( $permalink ) ) {
return $permalink;
}
// Keep the original permalink in a separate variable
$original_permalink = $permalink;
$permalink_path = parse_url( $original_permalink, PHP_URL_PATH );
$permalink_path = ( ! empty( $permalink_path ) ) ? trim( $permalink_path, '/' ) : $permalink_path;
$trailing_slash_mode = ( ! empty( $permalink_manager_options['general']['trailing_slashes'] ) ) ? $permalink_manager_options['general']['trailing_slashes'] : "";
// Ignore homepage URLs
if ( filter_var( $permalink, FILTER_VALIDATE_URL ) && empty( $permalink_path ) ) {
return $permalink;
}
// Always remove trailing slashes from URLs/URIs that end with file extension (e.g. html)
if ( preg_match( '/(http(?:s)\:\/\/[^\/]+\/)?.*\.([a-zA-Z]{3,4})[\/]*(\?[^\/]+|$)/', $permalink ) ) {
$trailing_slash_mode = 2;
}
// Add trailing slashes
if ( in_array( $trailing_slash_mode, array( 1, 10 ) ) ) {
$permalink = preg_replace( '/(.+?)([\/]*)([\?\#][^\/]+|$)/', '$1/$3', $permalink ); // Instead of trailingslashit()
} // Remove trailing slashes
else if ( in_array( $trailing_slash_mode, array( 2, 20 ) ) ) {
$permalink = preg_replace( '/(.+?)([\/]*)([\?\#][^\/]+|$)/', '$1$3', $permalink ); // Instead of untrailingslashit()
} // Default settings
else {
$permalink = user_trailingslashit( $permalink );
}
// Remove double slashes
$permalink = preg_replace( '/(?taxonomy ) ) {
$term = $object;
} else if ( ! empty( $object->post_type ) ) {
$post = $object;
} else if ( empty( $object ) && ! empty( $wp_query->post ) ) {
$post = $wp_query->post;
} else {
return;
}
// 3.1. Validate the pages count
if ( ( ! empty( $post->post_type ) && isset( $post->post_content ) ) || ( isset( $wp_query->max_num_pages ) && ! empty( $term->taxonomy ) ) ) {
$current_page = ( ! empty( $wp_query->query_vars['page'] ) ) ? $wp_query->query_vars['page'] : 1;
$current_page = ( empty( $wp_query->query_vars['page'] ) && ! empty( $wp_query->query_vars['paged'] ) ) ? $wp_query->query_vars['paged'] : $current_page;
// 2.1B. Count post pages
$post_content = ( ! empty( $post->post_content ) ) ? $post->post_content : '';
$num_pages = ( is_home() || is_archive() || is_search() ) ? $wp_query->max_num_pages : substr_count( strtolower( $post_content ), '' ) + 1;
$is_404 = ( $current_page > 1 && ( $current_page > $num_pages ) ) ? true : false;
} // 3.2. Force 404 if no posts are loaded
else if ( ! empty( $wp_query->query['paged'] ) && $wp_query->post_count == 0 ) {
$is_404 = true;
}
// 3.4. Force 404 if endpoint value is not set or not numeric
if ( ! empty( $pm_query['endpoint'] ) && $pm_query['endpoint'] == 'page' && ( empty( $pm_query['endpoint_value'] ) || ! is_numeric( $pm_query['endpoint_value'] ) ) ) {
$is_404 = true;
}
// 4. Block non-existent pages (Force 404 error or allow canonical redirect)
if ( ! empty( $is_404 ) ) {
$pagination_mode = ( ! empty( $permalink_manager_options['general']['pagination_redirect'] ) ) ? $permalink_manager_options['general']['pagination_redirect'] : false;
// Make sure that canonical redirect is not disabled in adjust_canonical_redirect() method
if ( $pagination_mode == 2 ) {
$wp_query->query_vars['do_not_redirect'] = 0;
} else {
$wp_query->query = $wp_query->queried_object = $wp_query->queried_object_id = $pm_query = $post = null;
$wp_query->set_404();
status_header( 404 );
nocache_headers();
}
$pm_query = '';
}
}
/**
* Enhance the existing canonical redirect functionality, allowing users define custom redirects and support custom permalinks
*/
function new_uri_redirect_and_404() {
global $wp_query, $wp, $wp_rewrite, $wpdb, $permalink_manager_uris, $permalink_manager_redirects, $permalink_manager_external_redirects, $permalink_manager_options, $pm_query;
// Get the redirection mode & trailing slashes settings
$redirect_mode = ( ! empty( $permalink_manager_options['general']['redirect'] ) ) ? $permalink_manager_options['general']['redirect'] : false;
$trailing_slashes_mode = ( ! empty( $permalink_manager_options['general']['trailing_slashes'] ) ) ? $permalink_manager_options['general']['trailing_slashes'] : false;
$trailing_slashes_redirect = ( ! empty( $permalink_manager_options['general']['trailing_slashes_redirect'] ) ) ? $permalink_manager_options['general']['trailing_slashes_redirect'] : false;
$extra_redirects = ( ! empty( $permalink_manager_options['general']['extra_redirects'] ) ) ? $permalink_manager_options['general']['extra_redirects'] : false;
$canonical_redirect = ( ! empty( $permalink_manager_options['general']['canonical_redirect'] ) ) ? $permalink_manager_options['general']['canonical_redirect'] : false;
$old_slug_redirect = ( ! empty( $permalink_manager_options['general']['old_slug_redirect'] ) ) ? $permalink_manager_options['general']['old_slug_redirect'] : false;
$endpoint_redirect = ( ! empty( $permalink_manager_options['general']['endpoint_redirect'] ) ) ? $permalink_manager_options['general']['endpoint_redirect'] : false;
$copy_query_redirect = ( ! empty( $permalink_manager_options['general']['copy_query_redirect'] ) ) ? $permalink_manager_options['general']['copy_query_redirect'] : false;
$redirect_type = '-';
// Get home URL
$home_url = rtrim( get_option( 'home' ), "/" );
$home_dir = parse_url( $home_url, PHP_URL_PATH );
// Set up $correct_permalink variable
$correct_permalink = '';
// Get query string & URI
if ( empty( $_SERVER['REQUEST_URI'] ) ) {
return;
}
$query_string = ( $copy_query_redirect && ! empty( $_SERVER['QUERY_STRING'] ) ) ? $_SERVER['QUERY_STRING'] : '';
$old_uri = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
$old_uri = $old_uri_abs = ( empty( $old_uri ) ) ? strtok( $_SERVER["REQUEST_URI"], '?' ) : $old_uri;
// Fix for WP installed in directories (remove the directory name from the URI)
if ( ! empty( $home_dir ) ) {
$home_dir_regex = preg_quote( trim( $home_dir ), "/" );
$old_uri = preg_replace( "/{$home_dir_regex}/", "", $old_uri, 1 );
}
if ( is_front_page() || ( is_page() && get_option( 'show_on_front' ) === 'page' && get_queried_object_id() === (int) get_option( 'page_on_front' ) ) ) {
$is_front_page = true;
} else {
$is_front_page = false;
}
// Do not use custom redirects on author pages, search & front page
if ( ! is_author() && ! $is_front_page && ! is_home() && ! is_feed() && ! is_search() && empty( $_GET['s'] ) ) {
// Sometimes $wp_query indicates the wrong object if requested directly
$queried_object = get_queried_object();
// Unset 404 if custom URI is detected
if ( ! empty( $pm_query['id'] ) && ! empty( $queried_object->post_status ) ) {
$wp_query->is_404 = false;
}
/**
* 1A. External redirect
*/
if ( ! empty( $pm_query['id'] ) ) {
if ( ! empty( $queried_object->ID ) && ! empty( $permalink_manager_external_redirects[ $queried_object->ID ] ) ) {
$external_url = $permalink_manager_external_redirects[ $queried_object->ID ];
} else if ( ! empty( $queried_object->term_id ) && ! empty( $permalink_manager_external_redirects["tax-{$queried_object->term_id}"] ) ) {
$external_url = $permalink_manager_external_redirects["tax-{$queried_object->term_id}"];
}
if ( ! empty( $external_url ) && filter_var( $external_url, FILTER_VALIDATE_URL ) ) {
// Allow redirect
$wp_query->query_vars['do_not_redirect'] = 0;
wp_redirect( $external_url, 301, PERMALINK_MANAGER_PLUGIN_NAME ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- By design, the users can redirect the post/term to any external URL here
exit();
}
}
/**
* 1B. Custom redirects
*/
if ( empty( $wp_query->query_vars['do_not_redirect'] ) && $extra_redirects && ! empty( $permalink_manager_redirects ) && is_array( $permalink_manager_redirects ) && ! empty( $wp->request ) && ! empty( $pm_query['uri'] ) ) {
$uri = $pm_query['uri'];
$endpoint_value = $pm_query['endpoint_value'];
// Make sure that URIs with non-ASCII characters are also detected + Check the URLs that end with number
$decoded_url = urldecode( $uri );
$endpoint_url = "{$uri}/{$endpoint_value}";
// Convert to lowercase to make case-insensitive
$force_lowercase = apply_filters( 'permalink_manager_force_lowercase_uris', true );
if ( $force_lowercase ) {
$uri = strtolower( $uri );
$decoded_url = strtolower( $decoded_url );
$endpoint_url = strtolower( $endpoint_url );
}
// Check if the URI is not assigned to any post/term's redirects
foreach ( $permalink_manager_redirects as $element => $redirects ) {
if ( ! is_array( $redirects ) ) {
continue;
}
if ( in_array( $uri, $redirects ) || in_array( $decoded_url, $redirects ) || ( is_numeric( $endpoint_value ) && in_array( $endpoint_url, $redirects ) ) ) {
// Post is detected
if ( is_numeric( $element ) ) {
$correct_permalink = get_permalink( $element );
} // Term is detected
else {
$term_id = intval( preg_replace( "/[^0-9]/", "", $element ) );
$correct_permalink = get_term_link( $term_id );
}
// The custom redirect is found so there is no need to query the rest of array
break;
}
}
$redirect_type = ( ! empty( $correct_permalink ) ) ? 'custom_redirect' : $redirect_type;
}
// Ignore WP-Content links
if ( strpos( $_SERVER['REQUEST_URI'], '/wp-content' ) !== false ) {
return;
}
/**
* 1C. Enhance native redirect
*/
if ( $canonical_redirect && empty( $wp_query->query_vars['do_not_redirect'] ) && ! empty( $queried_object ) && empty( $correct_permalink ) ) {
// Affect only posts with custom URI and old URIs
if ( ! empty( $queried_object->ID ) && isset( $permalink_manager_uris[ $queried_object->ID ] ) && empty( $wp_query->query['preview'] ) ) {
// Ignore posts with specific statuses
if ( ! ( empty( $queried_object->post_status ) ) && in_array( $queried_object->post_status, array( 'draft', 'pending', 'auto-draft', 'future' ) ) ) {
return;
}
// Check if the post is excluded
if ( Permalink_Manager_Helper_Functions::is_post_excluded( $queried_object ) ) {
return;
}
// Get the real URL
$correct_permalink = get_permalink( $queried_object->ID );
} // Affect only terms with custom URI and old URIs
else if ( ! empty( $queried_object->term_id ) && isset( $permalink_manager_uris["tax-{$queried_object->term_id}"] ) && defined( 'PERMALINK_MANAGER_PRO' ) ) {
// Check if the term is excluded
if ( Permalink_Manager_Helper_Functions::is_term_excluded( $queried_object ) ) {
return;
}
// Get the real URL
$correct_permalink = get_term_link( $queried_object->term_id, $queried_object->taxonomy );
}
$redirect_type = ( ! empty( $correct_permalink ) ) ? 'native_redirect' : $redirect_type;
}
/**
* 1D. Old slug redirect
*/
if ( $old_slug_redirect && ! empty( $pm_query['uri'] ) && empty( $wp_query->query_vars['do_not_redirect'] ) && is_404() && empty( $correct_permalink ) ) {
$slug = basename( $pm_query['uri'] );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct SQL query is faster here
$post_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id from {$wpdb->postmeta} WHERE meta_key = '_wp_old_slug' AND meta_value = %s", $slug ) );
if ( ! empty( $post_id ) && Permalink_Manager_Helper_Functions::is_post_excluded( $post_id, true, true ) !== true ) {
$correct_permalink = get_permalink( $post_id );
$redirect_type = 'old_slug_redirect';
}
}
/**
* 2. Prevent redirect loop
*/
if ( ! empty( $correct_permalink ) && is_string( $correct_permalink ) && ! empty( $wp->request ) && $redirect_type != 'slash_redirect' ) {
$current_uri = trim( $wp->request, "/" );
$redirect_uri = trim( parse_url( $correct_permalink, PHP_URL_PATH ), "/" );
$correct_permalink = ( $redirect_uri == $current_uri ) ? null : $correct_permalink;
}
/**
* 3. Add endpoints to redirect URL
*/
if ( ! empty( $correct_permalink ) && $endpoint_redirect && ( ! empty( $pm_query['endpoint_value'] ) || ! empty( $pm_query['endpoint'] ) ) ) {
$endpoint_value = $pm_query['endpoint_value'];
if ( empty( $pm_query['endpoint'] ) && is_numeric( $endpoint_value ) ) {
$correct_permalink = sprintf( "%s/%d", trim( $correct_permalink, "/" ), $endpoint_value );
} else if ( isset( $pm_query['endpoint'] ) && ! empty( $endpoint_value ) ) {
if ( $pm_query['endpoint'] == 'cpage' ) {
$correct_permalink = sprintf( "%s/%s-%s", trim( $correct_permalink, "/" ), $wp_rewrite->comments_pagination_base, $endpoint_value );
} else {
$correct_permalink = sprintf( "%s/%s/%s", trim( $correct_permalink, "/" ), $pm_query['endpoint'], $endpoint_value );
}
} else {
$correct_permalink = sprintf( "%s/%s", trim( $correct_permalink, "/" ), $pm_query['endpoint'] );
}
}
} else {
$queried_object = '-';
}
/**
* 4. Check trailing & duplicated slashes (ignore links with query parameters)
*/
if ( ( ( $trailing_slashes_mode && $trailing_slashes_redirect ) || preg_match( '/\/{2,}/', $old_uri ) ) && empty( $is_front_page ) && empty( $_POST ) && empty( $correct_permalink ) && empty( $query_string ) && ! empty( $old_uri ) && $old_uri !== "/" ) {
$trailing_slash = ( substr( $old_uri, - 1 ) == "/" ) ? true : false;
$obsolete_slash = ( preg_match( '/\/{2,}/', $old_uri ) || preg_match( "/.*\.([a-zA-Z]{3,4})\/$/", $old_uri ) );
if ( ( $trailing_slashes_mode == 1 && ! $trailing_slash ) || ( $trailing_slashes_mode == 2 && $trailing_slash ) || $obsolete_slash ) {
$new_uri = self::control_trailing_slashes( $old_uri );
if ( $new_uri !== $old_uri ) {
$correct_permalink = sprintf( "%s/%s", $home_url, ltrim( $new_uri, '/' ) );
$redirect_type = 'slash_redirect';
}
}
}
/**
* 5. WWW prefix | SSL mismatch redirect
*/
if ( ! empty( $permalink_manager_options['general']['sslwww_redirect'] ) && ! empty( $_SERVER['HTTP_HOST'] ) ) {
$home_url_has_www = ( strpos( $home_url, 'www.' ) !== false ) ? true : false;
$requested_url_has_www = ( strpos( $_SERVER['HTTP_HOST'], 'www.' ) !== false ) ? true : false;
$home_url_has_ssl = ( strpos( $home_url, 'https' ) !== false ) ? true : false;
if ( ( $home_url_has_www !== $requested_url_has_www ) || ( ! is_ssl() && $home_url_has_ssl !== false ) ) {
$new_uri = ltrim( $old_uri, '/' );
$correct_permalink = sprintf( "%s/%s", $home_url, $new_uri );
$redirect_type = 'www_redirect';
}
}
/**
* 6. Debug redirect
*/
$correct_permalink = apply_filters( 'permalink_manager_filter_redirect', $correct_permalink, $redirect_type, $queried_object, $old_uri );
/**
* 7. Ignore default URIs (or do nothing if redirects are disabled)
*/
if ( ! empty( $correct_permalink ) && is_string( $correct_permalink ) && ! empty( $redirect_mode ) ) {
// Allow redirect
$wp_query->query_vars['do_not_redirect'] = 0;
// Adjust trailing slashes
$correct_permalink = self::control_trailing_slashes( $correct_permalink );
// Prevent redirect loop
$rel_old_uri = wp_make_link_relative( $old_uri_abs );
$rel_new_uri = wp_make_link_relative( $correct_permalink );
// Append query string
$correct_permalink = ( ! empty( $query_string ) ) ? sprintf( "%s?%s", strtok( $correct_permalink, "?" ), $query_string ) : $correct_permalink;
if ( filter_var( $correct_permalink, FILTER_VALIDATE_URL ) && ( $redirect_type === 'www_redirect' || $rel_old_uri !== $rel_new_uri ) ) {
wp_safe_redirect( $correct_permalink, $redirect_mode, PERMALINK_MANAGER_PLUGIN_NAME );
exit();
}
}
}
/**
* Control how the canonical redirect function in WordPress and other popular plugins works
*/
function adjust_canonical_redirect() {
global $permalink_manager_options, $wp, $wp_query, $wp_rewrite;
// Adjust rewrite settings for trailing slashes
$trailing_slash_setting = ( ! empty( $permalink_manager_options['general']['trailing_slashes'] ) ) ? $permalink_manager_options['general']['trailing_slashes'] : "";
if ( in_array( $trailing_slash_setting, array( 1, 10 ) ) ) {
$wp_rewrite->use_trailing_slashes = true;
} else if ( in_array( $trailing_slash_setting, array( 2, 20 ) ) ) {
$wp_rewrite->use_trailing_slashes = false;
}
// Get endpoints
$endpoints = Permalink_Manager_Helper_Functions::get_endpoints();
$endpoints_array = ( $endpoints ) ? explode( "|", $endpoints ) : array();
// Check if any endpoint is called (fix for endpoints)
foreach ( $endpoints_array as $endpoint ) {
if ( ! empty( $wp->query_vars[ $endpoint ] ) && ! in_array( $endpoint, array( 'attachment', 'page', 'paged', 'feed' ) ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
break;
}
}
// Allow canonical redirect for ../1/ and ../page/1/
if ( ( ! empty( $wp->query_vars['paged'] ) && $wp->query_vars['paged'] == 1 ) || ( ! empty( $wp->query_vars['page'] ) && $wp->query_vars['page'] == 1 ) ) {
$wp_query->query_vars['do_not_redirect'] = 0;
} // Allow canonical redirect for URL with specific query parameters
else if ( ( is_single() && ( ! empty( $_GET['p'] ) || ! empty( $_GET['name'] ) ) ) || ( is_page() && ! empty( $_GET['page_id'] ) ) ) {
$wp_query->query_vars['do_not_redirect'] = 0;
}
if ( empty( $permalink_manager_options['general']['canonical_redirect'] ) ) {
remove_action( 'template_redirect', 'redirect_canonical' );
}
if ( empty( $permalink_manager_options['general']['old_slug_redirect'] ) ) {
remove_action( 'template_redirect', 'wp_old_slug_redirect' );
}
if ( ! empty( $wp_query->query_vars['do_not_redirect'] ) ) {
if ( function_exists( 'rank_math' ) && ! empty( $permalink_manager_options['general']['rankmath_redirect'] ) ) {
$rank_math_instance = rank_math();
if ( ! empty( $rank_math_instance->manager ) && is_object( $rank_math_instance->manager ) && method_exists( $rank_math_instance->manager, 'get_module' ) ) {
$rank_math_redirections_module = $rank_math_instance->manager->get_module( 'redirections' );
if ( ! empty( $rank_math_redirections_module ) ) {
remove_action( 'template_redirect', array( $rank_math_redirections_module, 'do_redirection' ), 11 );
remove_action( 'wp', array( $rank_math_redirections_module, 'do_redirection' ), 11 );
}
}
}
// SEOPress
remove_action( 'template_redirect', 'seopress_category_redirect', 1 );
remove_action( 'template_redirect', 'wp_old_slug_redirect' );
remove_action( 'template_redirect', 'redirect_canonical' );
add_filter( 'wpml_is_redirected', '__return_false', 99, 2 );
add_filter( 'pll_check_canonical_url', '__return_false', 99, 2 );
}
}
/**
* Enable case-insensitive permalinks mode
*/
function case_insensitive_permalinks() {
global $permalink_manager_uris;
if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
$_SERVER['REQUEST_URI'] = strtolower( $_SERVER['REQUEST_URI'] );
$permalink_manager_uris = array_map( 'strtolower', $permalink_manager_uris );
}
}
}
core/permalink-manager-debug.php 0000644 00000013643 15220204736 0012673 0 ustar 00 functions['pro-license']->get_license_key();
// Mask the license key
$debug_info['license_key'] = preg_replace( '/([^-]+)-([^-]+)-([^-]+)-([^-]+)$/', '***-***-$3', $license_key );
}
// Plugin version
$debug_info['version'] = PERMALINK_MANAGER_VERSION;
self::display_debug_data( $debug_info );
}
return $query;
}
/**
* Debug the redirect controlled by Permalink_Manager_Core_Functions::new_uri_redirect_and_404();
*
* @param string $correct_permalink
* @param string $redirect_type
* @param mixed $queried_object
*
* @return string
*/
public function debug_redirect( $correct_permalink, $redirect_type, $queried_object ) {
if ( isset( $_REQUEST['debug_redirect'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No data is saved here
$debug_info['redirect_url'] = ( ! empty( $correct_permalink ) ) ? $correct_permalink : '-';
$debug_info['redirect_type'] = ( ! empty( $redirect_type ) ) ? $redirect_type : "-";
self::display_debug_data( $debug_info );
}
return $correct_permalink;
}
/**
* Debug wp_redirect() function used in 3rd party plugins
*
* @param string $url
* @param string $status
*
* @return string
*/
public function debug_wp_redirect( $url, $status ) {
if ( isset( $_GET['debug_wp_redirect'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No data is saved here
$debug_info['url'] = $url;
$debug_info['status'] = $status;
$debug_info['backtrace'] = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 10 ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace -- Backtrace is a part of debug tools
self::display_debug_data( $debug_info );
}
return $url;
}
/**
* Display the list of all custom fields assigned to specific post
*/
public static function debug_custom_fields() {
global $pagenow;
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- No data is saved here
if ( ! isset( $_GET['debug_custom_fields'] ) ) {
return;
}
if ( $pagenow == 'post.php' && isset( $_GET['post'] ) ) {
$post_id = intval( $_GET['post'] );
$custom_fields = get_post_meta( $post_id );
}
if ( $pagenow == 'term.php' && isset( $_GET['tag_ID'] ) ) {
$term_id = intval( $_GET['tag_ID'] );
$custom_fields = get_term_meta( $term_id );
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
if ( isset ( $custom_fields ) ) {
self::display_debug_data( $custom_fields );
}
}
/**
* A helper function used to display the debug data in various functions
*
* @param mixed $debug_info
*/
public static function display_debug_data( $debug_info ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- print_r() is a part of debug tool
$debug_txt = print_r( $debug_info, true );
$debug_txt = sprintf( "
%s ", esc_html( $debug_txt ) );
wp_die( wp_kses_post( $debug_txt ) );
}
/**
* Generate a CSV file from array
*
* @param array $array
* @param string $filename
*/
public static function output_csv( $array, $filename = 'debug.csv', $delimiter = ',' ) {
if ( count( $array ) == 0 ) {
return null;
}
// Disable caching
$now = gmdate( "D, d M Y H:i:s" );
header( "Expires: Tue, 03 Jul 2001 06:00:00 GMT" );
header( "Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate" );
header( "Last-Modified: {$now} GMT" );
// Force download
header( "Content-Type: application/force-download" );
header( "Content-Type: application/octet-stream" );
header( "Content-Type: application/download" );
header( 'Content-Type: text/csv; charset=utf-8' );
// Disposition / encoding on response body
header( "Content-Disposition: attachment;filename={$filename}" );
header( "Content-Transfer-Encoding: binary" );
$df = fopen( "php://output", 'w' );
// Use the same delimiter for headers and data
fputcsv( $df, array_keys( reset( $array ) ), $delimiter );
foreach ( $array as $row ) {
fputcsv( $df, $row, $delimiter );
}
fclose( $df ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
die();
}
}
core/permalink-manager-uri-functions-post.php 0000644 00000105637 15220204736 0015402 0 ustar 00 $label ) {
add_filter( "manage_{$post_type}_posts_columns", array( $this, 'quick_edit_column' ) );
add_filter( "manage_{$post_type}_posts_custom_column", array( $this, 'quick_edit_column_content' ), 10, 2 );
}
}
/**
* Apply the custom permalinks to the posts
*
* @param string $permalink
* @param WP_Post|int $post
*
* @return string
*/
static function custom_post_permalinks( $permalink, $post, $leavename = false ) {
global $permalink_manager_uris, $permalink_manager_options, $permalink_manager_ignore_permalink_filters;
// Do not filter permalinks in Customizer
if ( function_exists( 'is_customize_preview' ) && is_customize_preview() ) {
return $permalink;
}
// Do not filter in WPML String Editor
if ( ! empty( $_REQUEST['icl_ajx_action'] ) && $_REQUEST['icl_ajx_action'] == 'icl_st_save_translation' ) {
return $permalink;
}
// WPML (prevent duplicated posts)
if ( ! empty( $_REQUEST['trid'] ) && ! empty( $_REQUEST['skip_sitepress_actions'] ) ) {
return $permalink;
}
// Do not run when metaboxes are loaded with Gutenberg
/* if ( ! empty( $_REQUEST['meta-box-loader'] ) && empty( $_POST['custom_uri'] ) ) {
return $permalink;
}*/
// Do not filter if $permalink_manager_ignore_permalink_filters global is set
if ( ! empty( $permalink_manager_ignore_permalink_filters ) ) {
return $permalink;
}
$post = ( is_integer( $post ) ) ? get_post( $post ) : $post;
// Do not run if post object is invalid or the permalink is not string
if ( empty( $post ) || empty( $post->ID ) || empty( $post->post_type ) || ! is_string( $permalink ) ) {
return $permalink;
}
// Start with homepage URL
$home_url = Permalink_Manager_Permastructure_Functions::get_permalink_base( $post );
// Check if the post is excluded
if ( ! empty( $post->post_type ) && Permalink_Manager_Helper_Functions::is_post_excluded( $post ) && $post->post_type !== 'attachment' ) {
return $permalink;
}
// 2A. Do not change permalink of frontpage
if ( Permalink_Manager_Helper_Functions::is_front_page( $post->ID ) ) {
return $permalink;
} // 2B. Do not change permalink for drafts and future posts (+ remove trailing slash from them)
else if ( in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft', 'future' ) ) ) {
return $permalink;
}
// 3. Save the old permalink to a separate variable
$old_permalink = $permalink;
// 4. Filter only the posts with custom permalink assigned
if ( isset( $permalink_manager_uris[ $post->ID ] ) ) {
// Encode URI?
if ( ! empty( $permalink_manager_options['general']['decode_uris'] ) ) {
$permalink = "{$home_url}/" . rawurldecode( "/{$permalink_manager_uris[$post->ID]}" );
} else {
$permalink = "{$home_url}/" . Permalink_Manager_Helper_Functions::encode_uri( "{$permalink_manager_uris[$post->ID]}" );
}
// Keep the slug editor in Block Editor wherever it may help
if ( $leavename && is_admin() ) {
$placeholder = Permalink_Manager_Permastructure_Functions::get_post_tag( $post->post_type );
$post_name_esc = preg_quote( get_page_uri( $post->ID ), '/' );
$permalink = preg_replace( "/\/({$post_name_esc})\/?$/", "/{$placeholder}/", $permalink );
}
} else if ( $post->post_type == 'attachment' && $post->post_parent > 0 && $post->post_parent != $post->ID && ! empty( $permalink_manager_uris[ $post->post_parent ] ) ) {
$permalink = "{$home_url}/{$permalink_manager_uris[$post->post_parent]}/attachment/{$post->post_name}";
}
return apply_filters( 'permalink_manager_filter_final_post_permalink', $permalink, $post, $old_permalink );
}
/**
* Check if the provided slug is unique and then update it with SQL query.
*
* @param string $slug
* @param int $id
* @param bool $preview_mode
*
* @return string
*/
static function update_slug_by_id( $slug, $id, $preview_mode = false ) {
global $wpdb;
// Update slug and make it unique
$slug = ( empty( $slug ) ) ? get_the_title( $id ) : $slug;
$slug = sanitize_title( $slug );
$new_slug = wp_unique_post_slug( $slug, $id, get_post_status( $id ), get_post_type( $id ), 0 );
if ( ! $preview_mode ) {
$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_name = %s WHERE ID = %d", $new_slug, $id ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
clean_post_cache( $id );
}
return $new_slug;
}
/**
* Get the currently used custom permalink (or default/empty URI)
*
* @param int $post_id
* @param bool $native_uri
* @param bool $no_fallback
*
* @return string
*/
public static function get_post_uri( $post_id, $native_uri = false, $no_fallback = false ) {
global $permalink_manager_uris;
// Check if input is post object
$post_id = ( isset( $post_id->ID ) ) ? $post_id->ID : $post_id;
if ( ! empty( $permalink_manager_uris[ $post_id ] ) ) {
$final_uri = $permalink_manager_uris[ $post_id ];
} else if ( ! $no_fallback ) {
$final_uri = self::get_default_post_uri( $post_id, $native_uri );
} else {
$final_uri = '';
}
return $final_uri;
}
/**
* Get the default custom permalink (not overwritten by the user) or native permalink (unfiltered)
*
* @param WP_Post|int $post
* @param bool $native_uri
* @param bool $check_if_disabled
*
* @return string
*/
public static function get_default_post_uri( $post, $native_uri = false, $check_if_disabled = false ) {
global $permalink_manager_uris, $permalink_manager_permastructs, $wp_post_types;
// Disable WPML adjust ID filter
if ( class_exists( 'SitePress' ) ) {
add_filter( 'wpml_disable_term_adjust_id', '__return_true', 999 );
}
// Load all bases & post
$post = is_object( $post ) ? $post : get_post( $post );
// Check if post ID is defined (and front page permalinks should be empty)
if ( empty( $post->ID ) || Permalink_Manager_Helper_Functions::is_front_page( $post->ID ) ) {
return '';
}
$post_type = $post->post_type;
if ( empty( $post->post_name ) ) {
$pre_post_name = Permalink_Manager_Helper_Functions::sanitize_title( $post->post_title );
$post_name = wp_unique_post_slug( $pre_post_name, $post->ID, 'publish', $post->post_type, $post->post_parent );
} else {
$post_name = $post->post_name;
}
// 1A. Check if post type is allowed
if ( $check_if_disabled && Permalink_Manager_Helper_Functions::is_post_type_disabled( $post_type ) ) {
return '';
}
// 1A. Get the native permastructure
if ( $post_type == 'attachment' ) {
$parent_page = ( $post->post_parent > 0 && $post->post_parent != $post->ID ) ? get_post( $post->post_parent ) : false;
if ( ! empty( $parent_page->ID ) ) {
$parent_page_uri = ( ! empty( $permalink_manager_uris[ $parent_page->ID ] ) ) ? $permalink_manager_uris[ $parent_page->ID ] : get_page_uri( $parent_page->ID );
} else {
$parent_page_uri = "";
}
$native_permastructure = ( $parent_page ) ? trim( $parent_page_uri, "/" ) . "/attachment" : "";
} else {
$native_permastructure = Permalink_Manager_Permastructure_Functions::get_default_permastruct( $post_type );
}
// 1B. Get the permastructure
if ( $native_uri ) {
$permastructure = $native_permastructure;
} else {
$permastructure = ( ! empty( $permalink_manager_permastructs['post_types'][ $post_type ] ) ) ? $permalink_manager_permastructs['post_types'][ $post_type ] : $native_permastructure;
$permastructure = apply_filters( 'permalink_manager_filter_permastructure', $permastructure, $post );
}
// 1C. Set the permastructure
$default_base = ( ! empty( $permastructure ) ) ? trim( $permastructure, '/' ) : "";
// 2A. Get the date
$date = explode( " ", date( 'Y m d H i s', strtotime( $post->post_date ) ) );
$monthname = sanitize_title( date_i18n( 'F', strtotime( $post->post_date ) ) );
// 2B. Get the author (if needed)
$author = '';
if ( strpos( $default_base, '%author%' ) !== false ) {
$authordata = get_userdata( $post->post_author );
$author = $authordata->user_nicename;
}
// 2C. Get the post type slug
if ( ! empty( $wp_post_types[ $post_type ] ) ) {
if ( ! empty( $wp_post_types[ $post_type ]->rewrite['slug'] ) ) {
$post_type_slug = $wp_post_types[ $post_type ]->rewrite['slug'];
} else if ( is_string( $wp_post_types[ $post_type ]->rewrite ) ) {
$post_type_slug = $wp_post_types[ $post_type ]->rewrite;
}
}
$post_type_slug = ( ! empty( $post_type_slug ) ) ? $post_type_slug : $post_type;
$post_type_slug = apply_filters( 'permalink_manager_filter_post_type_slug', $post_type_slug, $post, $post_type );
$post_type_slug = preg_replace( '/(%([^%]+)%\/?)/', '', $post_type_slug );
// 3B. Get the full slug
$post_name = Permalink_Manager_Helper_Functions::remove_slashes( $post_name );
$custom_slug = $full_custom_slug = Permalink_Manager_Helper_Functions::force_custom_slugs( $post_name, $post, true, null, false );
$post_title_slug = Permalink_Manager_Helper_Functions::force_custom_slugs( $post_name, $post, true, 1 );
$full_native_slug = $post_name;
// 3A. Fix for hierarchical CPT (start)
if ( $post->ancestors && is_post_type_hierarchical( $post_type ) ) {
$full_native_slug = Permalink_Manager_Helper_Functions::get_post_full_slug( $post, false, true );
$full_custom_slug = Permalink_Manager_Helper_Functions::get_post_full_slug( $post, false, false );
}
// 3B. Allow filter the default slug (only custom permalinks)
if ( ! $native_uri ) {
$full_slug = apply_filters( 'permalink_manager_filter_default_post_slug', $full_custom_slug, $post, $post_name );
} else {
$full_slug = $full_native_slug;
}
$post_type_tag = Permalink_Manager_Permastructure_Functions::get_post_tag( $post_type );
// 3C. Get the standard tags and replace them with their values
$tags = array( '%year%', '%monthnum%', '%monthname%', '%day%', '%hour%', '%minute%', '%second%', '%post_id%', '%author%', '%post_type%' );
$tags_replacements = array( $date[0], $date[1], $monthname, $date[2], $date[3], $date[4], $date[5], $post->ID, $author, $post_type_slug );
$default_uri = str_replace( $tags, $tags_replacements, $default_base );
// 3D. Get the slug tags
$slug_tags = array( $post_type_tag, "%postname%", "%postname_flat%", "%pagename_flat%", "%{$post_type}_flat%", "%native_slug%", '%native_title%' );
$slug_tags_replacement = array( $full_slug, $full_slug, $custom_slug, $custom_slug, $custom_slug, $full_native_slug, $post_title_slug );
$do_not_append_slug = Permalink_Manager_Permastructure_Functions::is_slug_tag_present( $default_uri, $slug_tags, $post );
// 3E. Replace the post tags with slugs or append the slug if no post tag is defined
if ( ! empty( $do_not_append_slug ) ) {
$default_uri = str_replace( $slug_tags, $slug_tags_replacement, $default_uri );
} else {
$default_uri .= "/{$full_slug}";
}
// 4. Replace taxonomies
$taxonomies = get_taxonomies();
if ( $taxonomies ) {
foreach ( $taxonomies as $taxonomy ) {
// 0. Check if taxonomy tag is present
if ( strpos( $default_uri, "%{$taxonomy}" ) === false ) {
continue;
}
// 1. Get terms assigned to this post
$terms = wp_get_object_terms( $post->ID, $taxonomy );
// 2. Sort the terms
if ( ! empty( $terms ) ) {
$terms = wp_list_sort( $terms, array(
'parent' => 'DESC',
'term_id' => 'ASC',
) );
}
// 3A. Try to use Yoast SEO Primary Term
$replacement_term = Permalink_Manager_Helper_Functions::get_primary_term( $post->ID, $taxonomy, false );
// 3B. Get the first assigned term to this taxonomy
if ( empty( $replacement_term ) ) {
$replacement_term = ( ! is_wp_error( $terms ) && ! empty( $terms ) && is_object( $terms[0] ) ) ? Permalink_Manager_Helper_Functions::get_lowest_element( $terms[0], $terms ) : '';
}
$replacement_term = apply_filters( 'permalink_manager_filter_post_terms', $replacement_term, $post, $terms, $taxonomy, $native_uri );
// 4A. Custom URI as term base
if ( ! empty( $replacement_term->term_id ) && strpos( $default_uri, "%{$taxonomy}_custom_uri%" ) !== false && ! empty( $permalink_manager_uris["tax-{$replacement_term->term_id}"] ) ) {
$mode = 1;
} // 4B. Hierarchical term base
else if ( ! empty( $replacement_term->term_id ) && strpos( $default_uri, "%{$taxonomy}_flat%" ) === false && strpos( $default_uri, "%{$taxonomy}_top%" ) === false && is_taxonomy_hierarchical( $taxonomy ) ) {
$mode = 2;
} // 4C. Force flat/non-hierarchical term base - get the highest level term (if %taxonomy_top% tag is used)
else if ( strpos( $default_uri, "%{$taxonomy}_top%" ) !== false ) {
$mode = 3;
} // 4D. Force flat/non-hierarchical term base - get the lowest level term (if %taxonomy_flat% tag is used)
else {
$mode = 4;
}
// Get the replacement slug (custom + native)
$replacement = Permalink_Manager_Helper_Functions::get_term_full_slug( $replacement_term, $terms, $mode, $native_uri );
$native_replacement = Permalink_Manager_Helper_Functions::get_term_full_slug( $replacement_term, $terms, $mode, true );
// Trim slashes
$replacement = trim( $replacement, '/' );
$native_replacement = trim( $native_replacement, '/' );
// Filter final category slug
$replacement = apply_filters( 'permalink_manager_filter_term_slug', $replacement, $replacement_term, $post, $terms, $taxonomy, $native_uri );
// 4. Do the replacement
$default_uri = ( ! empty( $replacement ) ) ? str_replace( array( "%{$taxonomy}%", "%{$taxonomy}_flat%", "%{$taxonomy}_custom_uri%", "%{$taxonomy}_top%" ), $replacement, $default_uri ) : $default_uri;
$default_uri = ( ! empty( $native_replacement ) ) ? str_replace( "%{$taxonomy}_native_slug%", $native_replacement, $default_uri ) : $default_uri;
}
}
// Restore WPML adjust ID filter
if ( class_exists( 'SitePress' ) ) {
remove_filter( 'wpml_disable_term_adjust_id', '__return_true', 999 );
}
return apply_filters( 'permalink_manager_filter_default_post_uri', $default_uri, $post->post_name, $post, $post_name, $native_uri );
}
/**
* Exclude the page selected as "Front page"
*
* @param array $uris
*
* @return array
*/
function exclude_homepage( $uris ) {
// Find the homepage URI
$homepage_id = get_option( 'page_on_front' );
if ( is_array( $uris ) && ! empty( $uris[ $homepage_id ] ) ) {
unset( $uris[ $homepage_id ] );
}
return $uris;
}
/**
* Support url_to_postid() function
*
* @param string $url
*
* @return string
*/
public function url_to_postid( $url ) {
global $pm_query;
// Filter only defined URLs
if ( empty( $url ) ) {
return $url;
}
// Make sure that $pm_query global is not changed
$old_pm_query = $pm_query;
$post = Permalink_Manager_Core_Functions::detect_post( array(), $url, true );
$pm_query = $old_pm_query;
if ( ! empty( $post->ID ) ) {
$native_url = "/?p={$post->ID}";
}
return ( ! empty( $native_url ) ) ? $native_url : $url;
}
/**
* Get array with all post items based on the user-selected settings in the "Bulk tools" form
*
* @return array|false
*/
public static function get_items() {
global $wpdb, $permalink_manager_options;
// Check if post types & statuses are not empty
if ( empty( $_POST['post_types'] ) || empty( $_POST['post_statuses'] ) ) {
return false;
}
$post_types_array = array_map( 'sanitize_key', $_POST['post_types'] );
$post_statuses_array = array_map( 'sanitize_key', $_POST['post_statuses'] );
$post_types_in = Permalink_Manager_Helper_Functions::prepare_array_for_sql_in( $post_types_array, false );
$post_statuses_in = Permalink_Manager_Helper_Functions::prepare_array_for_sql_in( $post_statuses_array, false );
// Filter the posts by IDs
$where = '';
if ( ! empty( $_POST['ids'] ) ) {
// Remove whitespaces and prepare array with IDs and/or ranges
$ids = esc_sql( preg_replace( '/\s*/m', '', $_POST['ids'] ) );
preg_match_all( "/([\d]+(?:-?[\d]+)?)/x", $ids, $groups );
// Prepare the extra ID filters
$where .= "AND (";
foreach ( $groups[0] as $group ) {
$where .= ( $group == reset( $groups[0] ) ) ? "" : " OR ";
// A. Single number
if ( is_numeric( $group ) ) {
$where .= "(ID = {$group})";
} // B. Range
else if ( substr_count( $group, '-' ) ) {
$range_edges = explode( "-", $group );
$where .= "(ID BETWEEN {$range_edges[0]} AND {$range_edges[1]})";
}
}
$where .= ")";
}
// Get excluded items
$excluded_posts = (array) apply_filters( 'permalink_manager_excluded_post_ids', array() );
if ( ! empty( $excluded_posts ) ) {
$where .= sprintf( " AND ID NOT IN (%s) ", Permalink_Manager_Helper_Functions::prepare_array_for_sql_in( $excluded_posts ) );
}
// Support for attachments
$attachment_support = ( in_array( 'attachment', $post_types_array ) ) ? " OR (post_type = 'attachment')" : "";
// Check the auto-update mode
// A. Allow only user-approved posts
if ( ! empty( $permalink_manager_options["general"]["auto_update_uris"] ) && $permalink_manager_options["general"]["auto_update_uris"] == 2 ) {
$where .= " AND pm.meta_value IN (1, -1) ";
} // B. Allow all posts not disabled by the user
else {
$where .= " AND (pm.meta_value IS NULL OR pm.meta_value IN (1, -1)) ";
}
// Get the rows before they are altered
$query = "SELECT post_type, post_title, post_name, ID FROM {$wpdb->posts} AS p LEFT JOIN {$wpdb->postmeta} AS pm ON pm.post_ID = p.ID AND pm.meta_key = 'auto_update_uri' WHERE ((post_status IN ({$post_statuses_in}) AND post_type IN ({$post_types_in})){$attachment_support}) {$where}";
$query = apply_filters( 'permalink_manager_get_items_query', $query, $where, 'post_types' );
return $wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
}
/**
* Process the permalinks/slugs "Find & replace" & "Regenerate/rest" tool
*
* @param array $chunk
* @param string $mode
* @param string $operation
* @param string $old_string
* @param string $new_string
* @param bool $preview_mode
*
* @return array|false
*/
public static function bulk_process_items( $chunk = null, $mode = '', $operation = '', $old_string = '', $new_string = '', $preview_mode = false ) {
// Reset variables
$updated_slugs_count = 0;
$updated_array = array();
// Get the rows before they are altered
$posts_to_update = ( ! empty( $chunk ) ) ? $chunk : self::get_items();
if ( empty( $operation ) ) {
return false;
}
// Now if the array is not empty use IDs from each subarray as a key
if ( $posts_to_update ) {
foreach ( $posts_to_update as $row ) {
// Get default & native URL
$native_uri = self::get_default_post_uri( $row['ID'], true );
$default_uri = self::get_default_post_uri( $row['ID'] );
$old_custom_uri = Permalink_Manager_URI_Functions::get_single_uri( $row['ID'], true, true );
$old_uri = ( ! empty( $old_custom_uri ) ) ? $old_custom_uri : $native_uri;
$old_post_name = $row['post_name'];
if ( $operation == 'regenerate' ) {
if ( $mode == 'slugs' ) {
$new_uri = $old_uri;
$new_post_name = Permalink_Manager_Helper_Functions::sanitize_title( $row['post_title'] );
} else if ( $mode == 'native' ) {
$new_uri = $native_uri;
$new_post_name = $old_post_name;
} else {
$new_uri = $default_uri;
$new_post_name = $old_post_name;
}
} else {
list( $new_post_name, $new_uri ) = Permalink_Manager_Helper_Functions::replace_uri_slug( $old_string, $new_string, $old_post_name, $old_uri, $mode );
}
// Check if native slug should be changed
if ( $mode == 'slugs' && $old_post_name !== $new_post_name ) {
$new_post_name = self::update_slug_by_id( $new_post_name, $row['ID'], $preview_mode );
}
$new_uri = apply_filters( 'permalink_manager_pre_update_post_uri', $new_uri, $row['ID'], $old_uri, $native_uri, $default_uri );
if ( ! ( empty( $new_uri ) ) && ( $old_custom_uri !== $new_uri ) || ( $old_post_name !== $new_post_name ) ) {
if ( ! $preview_mode && ( $old_custom_uri !== $new_uri ) ) {
Permalink_Manager_URI_Functions::save_single_uri( $row['ID'], $new_uri, false, false );
do_action( 'permalink_manager_updated_post_uri', $row['ID'], $new_uri, $old_uri, $native_uri, $default_uri );
}
$updated_array[] = array( 'item_title' => $row['post_title'], 'ID' => $row['ID'], 'old_uri' => $old_uri, 'new_uri' => $new_uri, 'old_slug' => $old_post_name, 'new_slug' => $new_post_name );
$updated_slugs_count ++;
}
}
// Save all custom permalinks
if ( ! $preview_mode ) {
Permalink_Manager_URI_Functions::save_all_uris();
}
$output = array( 'updated' => $updated_array, 'updated_count' => $updated_slugs_count );
wp_reset_postdata();
}
return ( ! empty( $output ) ) ? $output : false;
}
/**
* Save the custom permalinks in "Bulk URI Editor" tool
*
* @return array|false
*/
static public function update_all_permalinks() {
// Setup needed variables
$updated_slugs_count = 0;
$updated_array = array();
$new_uris = isset( $_POST['uri'] ) ? $_POST['uri'] : array();
// Double check if the slugs and ids are stored in arrays
if ( ! is_array( $new_uris ) ) {
$new_uris = explode( ',', $new_uris );
}
if ( ! empty( $new_uris ) ) {
foreach ( $new_uris as $id => $new_uri ) {
// Prepare variables
$this_post = get_post( $id );
$old_uri = Permalink_Manager_URI_Functions::get_single_uri( $this_post, false, true );
$final_uri = self::save_uri( $this_post, $new_uri, false, false, false );
if ( $final_uri ) {
$updated_array[] = array( 'item_title' => get_the_title( $id ), 'ID' => $id, 'old_uri' => $old_uri, 'new_uri' => $final_uri );
$updated_slugs_count ++;
}
}
// Save all custom permalinks
Permalink_Manager_URI_Functions::save_all_uris();
$output = array( 'updated' => $updated_array, 'updated_count' => $updated_slugs_count );
}
return ( ! empty( $output ) ) ? $output : false;
}
/**
* Allow to edit URIs from "Edit Post" admin pages
*
* @param string $html
* @param int $id
* @param string $new_title
* @param string $new_slug
* @param WP_Post $post
*
* @return string
*/
function edit_uri_box( $html, $id, $new_title, $new_slug, $post ) {
// Detect auto drafts
$autosave = ( ! empty( $new_title ) && empty( $new_slug ) ) ? true : false;
// Check if the post is excluded
if ( empty( $post->post_type ) || Permalink_Manager_Helper_Functions::is_post_excluded( $post, true ) ) {
$show_uri_editor = false;
} else {
$show_uri_editor = true;
}
// Stop the hook (if needed)
$show_uri_editor = apply_filters( "permalink_manager_show_uri_editor_post", $show_uri_editor, $post, $post->post_type );
if ( ! $show_uri_editor ) {
return $html;
}
// Make sure that home URL ends with slash
$home_url = Permalink_Manager_Permastructure_Functions::get_permalink_base( $post );
// A. Display the original permalink on the front-page editor
if ( Permalink_Manager_Helper_Functions::is_front_page( $id ) ) {
preg_match( '/href="([^"]+)"/mi', $html, $matches );
$sample_permalink = ( ! empty( $matches[1] ) ) ? $matches[1] : "";
} else {
// B. Do not change anything if post is not saved yet (display sample permalink instead)
if ( $autosave || empty( $post->post_status ) ) {
$sample_permalink_uri = self::get_default_post_uri( $id );
} // C. Display custom URI (if set) or default custom URI (for drafts)
else {
$is_draft = ( $post->post_status == 'draft' ) ? true : false;
$sample_permalink_uri = Permalink_Manager_URI_Functions::get_single_uri( $post, ! $is_draft, false );
}
if ( empty( $sample_permalink_uri ) && $post->post_type !== 'attachment' ) {
return $html;
}
// Decode URI & allow to filter it
$sample_permalink_uri = apply_filters( 'permalink_manager_filter_post_sample_uri', rawurldecode( $sample_permalink_uri ), $post );
// Prepare the sample & default permalink
$sample_permalink = sprintf( "%s/
%s ", $home_url, str_replace( "//", "/", $sample_permalink_uri ) );
}
$sample_permalink_html = sprintf( "
%s ", strip_tags( $sample_permalink ), $sample_permalink );
// 1. Overwrite the sample permalink
if ( preg_match( '/(
<\/span>)/m', $html ) ) {
$new_html = preg_replace( '/(<\/span>)/m', $sample_permalink_html, $html );
} else if ( preg_match( '/(.*<\/a>)/m', $html ) ) {
$new_html = preg_replace( '/( .*<\/a>)/m', $sample_permalink_html, $html );
} else {
$new_html = $html;
}
// 2. Append the Permalink Editor
$new_html .= Permalink_Manager_UI_Elements::display_uri_box( $post );
// 3. Hide the "Edit" slug button
$new_html = str_replace( 'edit-slug button', 'edit-slug button hidden', $new_html );
// 4. Append hidden field with native slug
$new_html .= ( ! empty( $post->post_name ) && strpos( $new_html, 'editable-post-name-full' ) === false ) ? "{$post->post_name} " : "";
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return $new_html;
}
/**
* Add "Current URI" input field to "Quick Edit" form
*
* @param array $columns
*
* @return array mixed
*/
function quick_edit_column( $columns ) {
global $current_screen;
// Get post type
$post_type = ( ! empty( $current_screen->post_type ) ) ? $current_screen->post_type : false;
// Check if post type is disabled
if ( $post_type && Permalink_Manager_Helper_Functions::is_post_type_disabled( $post_type ) ) {
return $columns;
}
return ( is_array( $columns ) ) ? array_merge( $columns, array( 'permalink-manager-col' => __( 'Custom permalink', 'permalink-manager' ) ) ) : $columns;
}
/**
* Display the URI of the current post in the "Custom Permalink" column
*
* @param string $column_name The name of the column to display. In this case, we named our column permalink-manager-col.
* @param int $post_id The ID of the term.
*/
function quick_edit_column_content( $column_name, $post_id ) {
global $permalink_manager_uris, $permalink_manager_options;
if ( $column_name == 'permalink-manager-col' ) {
$exclude_drafts = ( isset( $permalink_manager_options['general']['ignore_drafts'] ) ) ? $permalink_manager_options['general']['ignore_drafts'] : false;
$is_draft = ( get_post_status( $post_id ) == 'draft' ) ? true : false;
// A. Disable the 'Quick Edit' form for draft posts if 'Exclude drafts' option is turned on
if ( $exclude_drafts && $is_draft ) {
$disabled = 1;
} // B. Get auto-update settings
else {
$auto_update_val = get_post_meta( $post_id, 'auto_update_uri', true );
$disabled = ( ! empty( $auto_update_val ) ) ? $auto_update_val : $permalink_manager_options['general']['auto_update_uris'];
}
$uri = ( ! empty( $permalink_manager_uris[ $post_id ] ) ) ? rawurldecode( $permalink_manager_uris[ $post_id ] ) : self::get_post_uri( $post_id, true, $is_draft );
printf( '%s ', esc_attr( intval( $disabled ) ), esc_html( $uri ) );
}
}
/**
* Save the custom permalink
*
* @param int|WP_Post $post
* @param string $new_uri
* @param bool $is_new_post
* @param int|bool $auto_update_mode
*
* @return bool|string
*/
static function save_uri( $post, $new_uri = '', $is_new_post = false, $auto_update_mode = false, $db_save = true ) {
global $permalink_manager_options;
// Do not do anything if post is auto-saved
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return false;
}
// Get the post object
if ( is_numeric( $post ) ) {
$post_object = get_post( $post );
} else if ( is_a( $post, 'WP_Post' ) ) {
$post_object = $post;
} else {
return false;
}
// Check if post is allowed
if ( empty( $post_object->post_type ) || ( $is_new_post && empty( $post_object->post_title ) ) || Permalink_Manager_Helper_Functions::is_post_excluded( $post_object, true, $is_new_post ) ) {
return false;
}
// Manage 'Auto-update URI' settings
if ( ! empty( $auto_update_mode ) ) {
$auto_update_uri = $auto_update_mode;
update_post_meta( $post_object->ID, 'auto_update_uri', $auto_update_mode );
} elseif ( $auto_update_mode === 0 ) {
$auto_update_uri = $permalink_manager_options['general']['auto_update_uris'];
delete_post_meta( $post_object->ID, 'auto_update_uri' );
} else {
$auto_update_uri = get_post_meta( $post_object->ID, 'auto_update_uri', true );
$auto_update_uri = ( ! empty( $auto_update_uri ) ) ? $auto_update_uri : $permalink_manager_options['general']['auto_update_uris'];
}
$default_uri = self::get_default_post_uri( $post_object );
$native_uri = self::get_default_post_uri( $post_object, true );
$old_uri = self::get_post_uri( $post_object->ID, false, true );
if ( $is_new_post ) {
$new_uri = $default_uri;
$allow_save = apply_filters( 'permalink_manager_allow_new_post_uri', true, $post_object );
} else {
$new_uri = ( ( $new_uri == '' || $auto_update_uri == 1 ) && ! in_array( $post_object->post_status, array( 'draft', 'auto-draft' ) ) ) ? $default_uri : Permalink_Manager_Helper_Functions::sanitize_title( $new_uri, true );
$allow_save = apply_filters( 'permalink_manager_allow_update_post_uri', true, $post_object );
}
$new_uri = apply_filters( 'permalink_manager_pre_update_post_uri', $new_uri, $post_object->ID, $old_uri, $native_uri, $default_uri );
// The update URI process is stopped by the hook above or disabled in "Auto-update" settings
if ( ! $allow_save || ( ! empty( $auto_update_uri ) && $auto_update_uri == 2 ) ) {
return false;
}
// Save the URI only if $new_uri variable is set
if ( ! empty( $new_uri ) && $new_uri !== $old_uri ) {
Permalink_Manager_URI_Functions::save_single_uri( $post_object->ID, $new_uri, false, $db_save );
$uri_saved = true;
} // The $new_uri variable is empty or no change is detected
else {
$uri_saved = false;
}
do_action( 'permalink_manager_updated_post_uri', $post_object->ID, $new_uri, $old_uri, $native_uri, $default_uri, $uri_saved );
return ( $uri_saved ) ? $new_uri : false;
}
/**
* Generate the custom permalink when 'wp_insert_post' action is triggered
*
* @param int $post_id Post ID.
*/
function insert_post_hook( $post_id ) {
global $permalink_manager_uris;
// Do not trigger if post is a revision or imported via WP All Import (URI should be set after the post meta is added)
if ( empty( $post_id ) || wp_is_post_revision( $post_id ) || ( ! empty( $_REQUEST['page'] ) && $_REQUEST['page'] == 'pmxi-admin-import' ) ) {
return;
}
// Stop when products are imported with WooCommerce importer
if ( ! empty( $_REQUEST['action'] ) && $_REQUEST['action'] == 'woocommerce_do_ajax_product_import' ) {
return;
}
// Do not process REST API requests originating from Gutenberg JS functions
if ( defined( 'REST_REQUEST' ) && REST_REQUEST && ! empty( $_SERVER['HTTP_X_WP_NONCE'] ) ) {
return;
}
// Do not do anything if the custom permalink was generated before or 'custom_uri' field is present in the request
if ( isset( $permalink_manager_uris[ $post_id ] ) || ( isset( $_POST['custom_uri'] ) ) ) {
return;
}
self::save_uri( $post_id, '', true, 0 );
}
/**
* Save the custom permalink when 'save_post' action is triggered
*
* @param int $post_id Term ID.
*/
static function update_post_hook( $post_id ) {
// Verify nonce at first
if ( ! isset( $_POST['permalink-manager-nonce'] ) || ! wp_verify_nonce( $_POST['permalink-manager-nonce'], 'permalink-manager-edit-uri-box' ) ) {
return;
}
// Do not do anything if the field with URI or element ID are not present or different from the provided post ID
if ( ! isset( $_POST['custom_uri'] ) || empty( $_POST['permalink-manager-edit-uri-element-id'] ) || $_POST['permalink-manager-edit-uri-element-id'] != $post_id ) {
return;
}
// Check if the user can edit this post
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
// Fix for revisions
$post_id = wp_is_post_revision( $post_id ) ? wp_is_post_revision( $post_id ) : $post_id;
$post = get_post( $post_id );
// Get auto-update URI setting (if empty use global setting)
if ( isset( $_POST["auto_update_uri"] ) && is_numeric( $_POST["auto_update_uri"] ) ) {
$auto_update_mode = intval( $_POST["auto_update_uri"] );
} else {
$auto_update_mode = false;
}
// Update the slug (if changed)
if ( isset( $_POST['permalink-manager-edit-uri-element-slug'] ) && isset( $_POST['native_slug'] ) && ( $_POST['native_slug'] !== $post->post_name ) ) {
// Make sure that '_wp_old_slug' is saved
if ( ! empty( $_POST['post_name'] ) || ( isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'pm_save_permalink', 'editpost' ) ) ) ) {
$post_after = clone $post;
$post_after->post_name = sanitize_title( $_POST['native_slug'] );
wp_check_for_changed_slugs( $post_id, $post_after, $post );
}
self::update_slug_by_id( $_POST['native_slug'], $post_id );
}
self::save_uri( $post_id, $_POST['custom_uri'], false, $auto_update_mode );
}
/**
* Remove URI from options array after post is moved to the trash
*
* @param int $post_id
*/
function remove_post_hook( $post_id ) {
Permalink_Manager_URI_Functions::remove_single_uri( $post_id, false, true );
}
}
core/permalink-manager-uri-functions.php 0000644 00000015572 15220204736 0014415 0 ustar 00 term_id ) ) {
$is_term = true;
$element_id = $element->term_id;
} else if ( ! empty( $element->ID ) ) {
$is_term = false;
$element_id = $element->ID;
} else if ( is_string( $element ) || is_numeric( $element ) ) {
$is_term = ( strpos( $element, 'tax-' ) !== false ) ? true : $is_tax;
$element_id = preg_replace( '/[^0-9]/', '', $element );
} else {
$element_id = "";
$is_term = null;
}
$array_index = ( $is_term && ! empty( $element_id ) ) ? sprintf( 'tax-%s', $element_id ) : $element_id;
return array( $element_id, $is_term, $array_index );
}
/**
* Get the single custom permalink
*
* @param WP_Post|WP_Term|int $element
* @param bool $native_uri
* @param bool $no_fallback
*/
public static function get_single_uri( $element, $native_uri = false, $no_fallback = false, $is_tax = false ) {
// Get the element key
list( $element_id, $is_term, $array_index ) = self::get_single_uri_key( $element, $is_tax );
if ( $is_term ) {
$final_uri = ( class_exists( 'Permalink_Manager_URI_Functions_Tax' ) ) ? Permalink_Manager_URI_Functions_Tax::get_term_uri( $element_id, $native_uri, $no_fallback ) : '';
} else {
$final_uri = Permalink_Manager_URI_Functions_Post::get_post_uri( $element_id, $native_uri, $no_fallback );
}
return $final_uri;
}
/**
* Save single custom permalink to the custom permalinks array
*
* @param int|string $element
* @param string $element_uri
* @param bool $is_tax
* @param bool $db_save
*/
public static function save_single_uri( $element, $element_uri = null, $is_tax = false, $db_save = false ) {
global $permalink_manager_uris;
// Get the element key
list( $element_id, $is_term, $array_index ) = self::get_single_uri_key( $element, $is_tax );
// Save the custom permalink if the URI is not empty
if ( ! empty( $array_index ) && ! empty( $element_uri ) ) {
$permalink_manager_uris[ $array_index ] = Permalink_Manager_Helper_Functions::sanitize_title( $element_uri, true );
if ( $db_save ) {
self::save_all_uris( $permalink_manager_uris );
}
}
}
/**
* Remove single custom permalink from the custom permalinks array
*
* @param int|string $element
* @param bool $is_tax
* @param bool $db_save
*/
public static function remove_single_uri( $element, $is_tax = false, $db_save = false ) {
global $permalink_manager_uris;
// Get the element key
list( $element_id, $is_term, $array_index ) = self::get_single_uri_key( $element, $is_tax );
// Check if the custom permalink is assigned to this element
if ( ! empty( $array_index ) && isset( $permalink_manager_uris[ $array_index ] ) ) {
unset( $permalink_manager_uris[ $array_index ] );
}
if ( $db_save ) {
self::save_all_uris( $permalink_manager_uris );
}
}
/**
* Find the ID(s) of the element(s) by its custom permalink
*
* @param string $search_query
* @param bool $strict_search
* @param string $content_type
*
* @return bool|string|array
*/
public static function find_uri( $search_query, $strict_search = true, $content_type = null ) {
$custom_permalinks = self::get_all_uris();
$found = false;
if ( $strict_search ) {
$all_uris = array_flip( $custom_permalinks );
$found = ( ! empty( $all_uris[ $search_query ] ) ) ? $all_uris[ $search_query ] : false;
} else {
$found = array();
$search_query = preg_quote( $search_query, '/' );
foreach ( $custom_permalinks as $id => $uri ) {
if ( preg_match( "/\b$search_query\b/i", $uri ) ) {
if ( $content_type == 'taxonomies' && ( strpos( $id, 'tax-' ) !== false ) ) {
$found[] = (int) abs( filter_var( $id, FILTER_SANITIZE_NUMBER_INT ) );
} else if ( $content_type == 'posts' && is_numeric( $id ) ) {
$found[] = (int) filter_var( $id, FILTER_SANITIZE_NUMBER_INT );
} else if ( empty( $content_type ) ) {
$found[] = $id;
}
}
}
}
return $found;
}
/**
* Check if a single URI is duplicated
*
* @param string $uri
* @param int $element_id
* @param array $duplicated_ids
*
* @return bool
*/
public static function is_uri_duplicated( $uri, $element_id, $duplicated_ids = array() ) {
$custom_permalinks = Permalink_Manager_URI_Functions::get_all_uris();
if ( empty( $uri ) || empty( $element_id ) || empty( $custom_permalinks ) ) {
return false;
}
$uri = trim( trim( sanitize_text_field( $uri ) ), "/" );
$element_id = sanitize_text_field( $element_id );
// Keep the URIs in a separate array just here
if ( ! empty( $duplicated_ids ) ) {
$all_duplicates = $duplicated_ids;
} else if ( in_array( $uri, $custom_permalinks ) ) {
$all_duplicates = array_keys( $custom_permalinks, $uri );
}
if ( ! empty( $all_duplicates ) ) {
// Get the language code of current element
$this_uri_lang = apply_filters( 'permalink_manager_get_language_code', '', $element_id );
foreach ( $all_duplicates as $key => $duplicated_id ) {
// Ignore custom redirects
if ( strpos( $key, 'redirect-' ) !== false ) {
unset( $all_duplicates[ $key ] );
continue;
}
if ( $this_uri_lang ) {
$duplicated_uri_lang = apply_filters( 'permalink_manager_get_language_code', '', $duplicated_id );
}
// Ignore the URI for requested element and other elements in other languages to prevent the false alert
if ( ( ! empty( $duplicated_uri_lang ) && $duplicated_uri_lang !== $this_uri_lang ) || $element_id == $duplicated_id ) {
unset( $all_duplicates[ $key ] );
}
}
return ( count( $all_duplicates ) > 0 ) ? true : false;
} else {
return false;
}
}
/**
* Get the array (or statistics) with custom permalinks
*
* @param bool $stats
*
* @return array
*/
public static function get_all_uris( $stats = false ) {
global $permalink_manager_uris;
if ( $stats ) {
$custom_permalinks_size = strlen( serialize( $permalink_manager_uris ) );
$custom_permalinks_count = count( $permalink_manager_uris );
return array( $custom_permalinks_size, $custom_permalinks_count );
} else {
return array_filter( $permalink_manager_uris );
}
}
/**
* Save the array with custom permalinks
*
* @param array $updated_uris
*/
public static function save_all_uris( $updated_uris = null ) {
if ( is_null( $updated_uris ) ) {
global $permalink_manager_uris;
$updated_uris = $permalink_manager_uris;
}
if ( is_array( $updated_uris ) && ! empty( $updated_uris ) ) {
update_option( 'permalink-manager-uris', $updated_uris, false );
}
}
}
core/permalink-manager-helper-functions.php 0000644 00000100054 15220204736 0015063 0 ustar 00 get_primary_term();
}
$primary_term = ( is_numeric( $yoast_primary_term_id ) ) ? get_term( $yoast_primary_term_id, $taxonomy ) : '';
} // B. The SEO Framework
else if ( function_exists( 'the_seo_framework' ) || function_exists( 'tsf' ) ) {
if ( class_exists( 'The_SEO_Framework\Data\Plugin\Post' ) ) {
$primary_term = The_SEO_Framework\Data\Plugin\Post::get_primary_term( $post_id, $taxonomy );
} elseif ( function_exists( 'the_seo_framework' ) && method_exists( the_seo_framework(), 'get_primary_term' ) ) {
$primary_term = the_seo_framework()->get_primary_term( $post_id, $taxonomy );
}
} // C. RankMath
else if ( class_exists( 'RankMath' ) ) {
$primary_cat_id = get_post_meta( $post_id, "rank_math_primary_{$taxonomy}", true );
$primary_term = ( ! empty( $primary_cat_id ) ) ? get_term( $primary_cat_id, $taxonomy ) : '';
} // D. SEOPress
else if ( function_exists( 'seopress_init' ) && $taxonomy == 'category' ) {
$primary_cat_id = get_post_meta( $post_id, '_seopress_robots_primary_cat', true );
$primary_term = ( ! empty( $primary_cat_id ) ) ? get_term( $primary_cat_id, 'category' ) : '';
} // E. SmartCrawler
else if ( class_exists( '\SmartCrawl\SmartCrawl' ) ) {
$primary_cat_id = get_post_meta( $post_id, "wds_primary_{$taxonomy}", true );
$primary_term = ( ! empty( $primary_cat_id ) ) ? get_term( $primary_cat_id, $taxonomy ) : '';
} // F. All In One SEO Pro
else if ( class_exists( '\AIOSEO\Plugin\Pro\Standalone\PrimaryTerm' ) ) {
$primary_term_class = new \AIOSEO\Plugin\Pro\Standalone\PrimaryTerm();
$primary_term = ( method_exists( $primary_term_class, 'getPrimaryTerm' ) ) ? $primary_term_class->getPrimaryTerm( $post_id, $taxonomy ) : '';
}
if ( ! empty( $primary_term ) && ! is_wp_error( $primary_term ) ) {
return ( $slug_only ) ? $primary_term->slug : $primary_term;
} else {
return '';
}
}
/**
* Get the lowest level term/post in the specific array
*
* @param WP_Post|WP_Term|int $first_element
* @param array $elements
*
* @return WP_Post|WP_Term|int
*/
static function get_lowest_element( $first_element, $elements ) {
if ( ! empty( $elements ) && ! empty( $first_element ) ) {
// Get the ID of first element
if ( ! empty( $first_element->term_id ) ) {
$first_element_id = $first_element->term_id;
$parent_key = 'parent';
} else if ( ! empty( $first_element->ID ) ) {
$first_element_id = $first_element->ID;
$parent_key = 'post_parent';
} else if ( is_numeric( $first_element ) ) {
$first_element_id = $first_element;
$parent_key = 'post_parent';
} else {
return false;
}
$children = wp_filter_object_list( $elements, array( $parent_key => $first_element_id ) );
if ( ! empty( $children ) ) {
// Get the first term
$child_term = reset( $children );
$first_element = self::get_lowest_element( $child_term, $elements );
}
}
return $first_element;
}
/**
* Get the full (hierarchical) slug for specific term object
*
* @param WP_Term $term
* @param mixed|WP_Term[] $terms
* @param bool $mode
* @param bool $native_uri
*
* @return string
*/
static function get_term_full_slug( $term, $terms, $mode = false, $native_uri = false ) {
global $permalink_manager_uris;
// Check if term is not empty
if ( empty( $term->taxonomy ) ) {
return '';
}
// Get taxonomy
$taxonomy = $term->taxonomy;
// Check if mode is set
if ( empty( $mode ) ) {
$mode = ( is_taxonomy_hierarchical( $taxonomy ) ) ? 2 : 4;
}
// A. Inherit the custom permalink from the term
if ( $mode == 1 ) {
$term_slug = ( ! empty( $permalink_manager_uris["tax-{$term->term_id}"] ) ) ? $permalink_manager_uris["tax-{$term->term_id}"] : '';
} // B. Hierarchical taxonomy base
else if ( $mode == 2 ) {
$ancestors = get_ancestors( $term->term_id, $taxonomy, 'taxonomy' );
$hierarchical_slugs = array();
foreach ( $ancestors as $ancestor ) {
$ancestor_term = get_term( $ancestor, $taxonomy );
$hierarchical_slugs[] = ( $native_uri ) ? $ancestor_term->slug : self::force_custom_slugs( $ancestor_term->slug, $ancestor_term );
}
$hierarchical_slugs = array_reverse( $hierarchical_slugs );
$term_slug = implode( '/', $hierarchical_slugs );
// Append the term slug now
$last_term_slug = ( $native_uri ) ? $term->slug : self::force_custom_slugs( $term->slug, $term );
$term_slug = "{$term_slug}/{$last_term_slug}";
} // C. Force flat taxonomy base - get the highest level term (if %taxonomy_top% tag is used)
else if ( $mode == 3 ) {
if ( ! empty( $term->parent ) ) {
$ancestors = get_ancestors( $term->term_id, $taxonomy, 'taxonomy' );
if ( is_array( $ancestors ) ) {
$top_ancestor = end( $ancestors );
$top_ancestor_term = get_term( $top_ancestor, $taxonomy );
$single_term = ( ! empty( $top_ancestor_term->slug ) ) ? $top_ancestor_term : $term;
}
}
$term_slug = ( ! empty( $single_term->slug ) ) ? self::force_custom_slugs( $single_term->slug, $single_term ) : $term->slug;
} // D. Force flat taxonomy base - get primary or lowest level term (if term is non-hierarchical or %taxonomy_flat% tag is used)
else {
if ( ! empty( $term->slug ) ) {
$term_slug = ( $native_uri ) ? $term->slug : Permalink_Manager_Helper_Functions::force_custom_slugs( $term->slug, $term );
} else if ( ! empty ( $terms ) ) {
foreach ( $terms as $single_term ) {
if ( $single_term->parent == 0 ) {
$term_slug = self::force_custom_slugs( $single_term->slug, $single_term );
break;
}
}
}
}
return ( ! empty( $term_slug ) ) ? $term_slug : "";
}
/**
* Get the full (hierarchical) slug for specific post object
*
* @param WP_Post|int $post
* @param bool $slugs_mode
* @param bool $native_uri
*
* @return string
*/
static function get_post_full_slug( $post, $slugs_mode = false, $native_uri = false ) {
$post = ( ! empty( $post->ID ) ) ? $post : get_post( $post );
if ( ! empty( $post->post_type ) ) {
$full_native_slug = $post->post_name;
$full_custom_slug = Permalink_Manager_Helper_Functions::force_custom_slugs( $post->post_name, $post, false, $slugs_mode );
if ( $post->ancestors && is_post_type_hierarchical( $post->post_type ) ) {
foreach ( $post->ancestors as $parent ) {
$parent = get_post( $parent );
if ( $parent && $parent->post_name ) {
$full_native_slug = $parent->post_name . '/' . $full_native_slug;
$full_custom_slug = Permalink_Manager_Helper_Functions::force_custom_slugs( $parent->post_name, $parent, false, $slugs_mode ) . '/' . $full_custom_slug;
}
}
}
$post_slug = ( $native_uri ) ? $full_native_slug : $full_custom_slug;
}
return ( ! empty( $post_slug ) ) ? $post_slug : "";
}
/**
* Allow to disable post types and taxonomies
*/
static function get_disabled_post_types( $include_user_excluded = true ) {
global $wp_post_types, $permalink_manager_options;
$allowed_post_types = array(
'shop_coupon'
);
$disabled_post_types = array(
'revision',
'nav_menu_item',
'algolia_task',
'fl_builder',
'fl-builder',
'fl-builder-template',
'fl-theme-layout',
'fusion_tb_layout',
'fusion_tb_section',
'fusion_template',
'fusion_element',
'wc_product_tab',
'wc_voucher',
'wc_voucher_template',
'sliders',
'thirstylink',
'elementor_library',
'elementor_menu_item',
'cms_block',
'nooz_coverage',
'bricks_template'
);
// 1. Disable post types that are not publicly_queryable
foreach ( $wp_post_types as $post_type ) {
if ( in_array( $post_type->name, $allowed_post_types ) ) {
continue;
}
if ( ! is_post_type_viewable( $post_type ) || ( empty( $post_type->query_var ) && empty( $post_type->rewrite ) && empty( $post_type->_builtin ) && ! empty( $permalink_manager_options['general']['partial_disable_strict'] ) ) ) {
$disabled_post_types[] = $post_type->name;
}
}
// 2. Add post types disabled by user
if ( $include_user_excluded ) {
$disabled_post_types = ( ! empty( $permalink_manager_options['general']['partial_disable']['post_types'] ) ) ? array_merge( (array) $permalink_manager_options['general']['partial_disable']['post_types'], $disabled_post_types ) : $disabled_post_types;
}
return apply_filters( 'permalink_manager_disabled_post_types', $disabled_post_types );
}
/**
* Get the array of all (including/excluding user selected) disabled taxonomies
*
* @param bool $include_user_excluded
*
* @return array
*/
static function get_disabled_taxonomies( $include_user_excluded = true ) {
global $wp_taxonomies, $permalink_manager_options;
$disabled_taxonomies = array(
'product_shipping_class',
'post_status',
'fl-builder-template-category',
'post_format',
'nav_menu',
'language',
'template_bundle'
);
// 1. Disable taxonomies that are not publicly_queryable
foreach ( $wp_taxonomies as $taxonomy ) {
if ( ! is_taxonomy_viewable( $taxonomy ) || ( empty( $taxonomy->query_var ) && empty( $taxonomy->rewrite ) && empty( $taxonomy->_builtin ) && ! empty( $permalink_manager_options['general']['partial_disable_strict'] ) ) ) {
$disabled_taxonomies[] = $taxonomy->name;
}
}
// 2. Add taxonomies disabled by user
if ( $include_user_excluded ) {
$disabled_taxonomies = ( ! empty( $permalink_manager_options['general']['partial_disable']['taxonomies'] ) ) ? array_merge( (array) $permalink_manager_options['general']['partial_disable']['taxonomies'], $disabled_taxonomies ) : $disabled_taxonomies;
}
return apply_filters( 'permalink_manager_disabled_taxonomies', $disabled_taxonomies );
}
/**
* Check if the post type should be ignored by Permalink Manager
*
* @param string $post_type
* @param bool $check_if_exists
*
* @return bool
*/
static public function is_post_type_disabled( $post_type, $check_if_exists = true ) {
$disabled_post_types = self::get_disabled_post_types();
$post_type_exists = ( $check_if_exists ) ? post_type_exists( $post_type ) : true;
return ( ( is_array( $disabled_post_types ) && in_array( $post_type, $disabled_post_types ) ) || empty( $post_type_exists ) ) ? true : false;
}
/**
* Check if the taxonomy should be ignored by Permalink Manager
*
* @param string $taxonomy
* @param bool $check_if_exists
*
* @return bool
*/
static public function is_taxonomy_disabled( $taxonomy, $check_if_exists = true ) {
$disabled_taxonomies = self::get_disabled_taxonomies();
$taxonomy_exists = ( $check_if_exists ) ? taxonomy_exists( $taxonomy ) : true;
return ( ( is_array( $disabled_taxonomies ) && in_array( $taxonomy, $disabled_taxonomies ) ) || empty( $taxonomy_exists ) ) ? true : false;
}
/**
* Get a list of all excluded posts' IDs
*
* @return array
*/
public static function get_excluded_post_ids() {
global $permalink_manager_options;
if ( ! empty( $permalink_manager_options["general"]["exclude_post_ids"] ) ) {
$excluded_post_ids_raw = $permalink_manager_options["general"]["exclude_post_ids"];
$excluded_post_ids = self::get_ids_from_string( $excluded_post_ids_raw );
} else {
$excluded_post_ids = array();
}
// Allow to filter the array via filter
$excluded_post_ids = apply_filters( 'permalink_manager_excluded_post_ids', $excluded_post_ids );
// Only numeric IDs are allowed
return ( ! empty( $excluded_post_ids ) ) ? array_filter( $excluded_post_ids, 'is_numeric' ) : array();
}
/**
* Check if specific post should be ignored by Permalink Manager
*
* @param WP_Post|int $post
* @param bool $draft_check
* @param bool $strict_draft_check
*
* @return bool
*/
public static function is_post_excluded( $post = null, $draft_check = false, $strict_draft_check = false ) {
$post = ( is_numeric( $post ) ) ? get_post( $post ) : $post;
// 1. Check if post type is disabled
if ( ! empty( $post->post_type ) && self::is_post_type_disabled( $post->post_type ) ) {
return true;
}
// 2. Get list of post IDs excluded in the plugin settings or via the filter
$excluded_post_ids = self::get_excluded_post_ids();
// 3. Exclude post IDs excluded via the filter or in the plugin settings
if ( is_array( $excluded_post_ids ) && ! empty( $post->ID ) && in_array( $post->ID, $excluded_post_ids ) ) {
return true;
}
// D. Check if post is a "draft", "pending", removed
if ( $draft_check ) {
return self::is_draft_excluded( $post, $strict_draft_check );
}
return false;
}
/**
* Check if specific post is a draft (or pending)
*
* @param WP_Post|int $post
* @param bool $strict_draft_check
*
* @return bool
*/
public static function is_draft_excluded( $post = null, $strict_draft_check = false ) {
global $permalink_manager_options;
$post = ( is_numeric( $post ) ) ? get_post( $post ) : $post;
// Check if post is a "draft", "pending" or moved to trash
if ( ! empty( $post->post_status ) ) {
if ( ! empty( $permalink_manager_options["general"]["ignore_drafts"] ) || $strict_draft_check ) {
$post_statuses = ( $permalink_manager_options["general"]["ignore_drafts"] == 2 ) ? array( 'draft', 'pending' ) : array( 'draft' );
if ( in_array( $post->post_status, $post_statuses ) ) {
return true;
}
}
if ( in_array( $post->post_status, array( 'auto-draft', 'trash' ) ) || ( strpos( $post->post_name, 'revision-v1' ) !== false ) || ( ! empty( $post->post_name ) && $post->post_name == 'auto-draft' ) ) {
return true;
}
}
return false;
}
/**
* Get a list of all excluded terms' IDs
*
* @return array
*/
public static function get_excluded_term_ids() {
global $permalink_manager_options;
if ( ! empty( $permalink_manager_options["general"]["exclude_term_ids"] ) ) {
$excluded_term_ids_raw = $permalink_manager_options["general"]["exclude_term_ids"];
$excluded_term_ids = self::get_ids_from_string( $excluded_term_ids_raw );
} else {
$excluded_term_ids = array();
}
// Allow to filter the array via filter
$excluded_term_ids = apply_filters( 'permalink_manager_excluded_term_ids', $excluded_term_ids );
// Only numeric IDs are allowed
return ( ! empty( $excluded_term_ids ) ) ? array_filter( $excluded_term_ids, 'is_numeric' ) : array();
}
/**
* Check if specific term should be ignored by Permalink Manager
*
* @param WP_Term $term
*
* @return bool
*/
public static function is_term_excluded( $term = null ) {
$term = ( is_numeric( $term ) ) ? get_term( $term ) : $term;
// 1. Check if taxonomy is disabled
if ( ! empty( $term->taxonomy ) && self::is_taxonomy_disabled( $term->taxonomy ) ) {
return true;
}
// 2. Get list of term IDs excluded in the plugin settings or via the filter
$excluded_term_ids = self::get_excluded_term_ids();
// 3. Exclude term IDs excluded via the filter or in the plugin settings
if ( is_array( $excluded_term_ids ) && ! empty( $term->term_id ) && in_array( $term->term_id, $excluded_term_ids ) ) {
return true;
}
return false;
}
/**
* Get all post types supported by Permalink Manager
*
* @param string $format
* @param string $cpt
* @param bool $include_user_excluded
*
* @return array
*/
static function get_post_types_array( $format = null, $cpt = null, $include_user_excluded = false ) {
global $wp_post_types;
$post_types_array = array();
$disabled_post_types = self::get_disabled_post_types( ! $include_user_excluded );
foreach ( $wp_post_types as $post_type ) {
if ( $format == 'full' ) {
$post_types_array[ $post_type->name ] = array( 'label' => $post_type->labels->name, 'name' => $post_type->name );
} else if ( $format == 'archive_slug' ) {
// Ignore non-public post types
if ( ! is_post_type_viewable( $post_type ) || empty( $post_type->has_archive ) ) {
continue;
}
if ( ! $post_type->has_archive ) {
$archive_slug = $post_type->has_archive;
} else if ( is_array( $post_type->rewrite ) && ! empty( $post_type->rewrite['slug'] ) ) {
$archive_slug = $post_type->rewrite['slug'];
} else {
$archive_slug = $post_type->name;
}
$post_types_array[ $post_type->name ] = $archive_slug;
} else {
$post_types_array[ $post_type->name ] = $post_type->labels->name;
}
}
if ( is_array( $disabled_post_types ) ) {
foreach ( $disabled_post_types as $post_type ) {
if ( ! empty( $post_types_array[ $post_type ] ) ) {
unset( $post_types_array[ $post_type ] );
}
}
}
return ( empty( $cpt ) ) ? $post_types_array : $post_types_array[ $cpt ];
}
/**
* Get all taxonomies supported by Permalink Manager
*
* @param string $format
* @param string $tax
* @param bool $include_user_excluded
*
* @return array
*/
static function get_taxonomies_array( $format = null, $tax = null, $include_user_excluded = false ) {
global $wp_taxonomies;
$taxonomies_array = array();
$disabled_taxonomies = self::get_disabled_taxonomies( ! $include_user_excluded );
foreach ( $wp_taxonomies as $taxonomy ) {
$taxonomy_name = ( ! empty( $taxonomy->labels->name ) ) ? $taxonomy->labels->name : '-';
$taxonomies_array[ $taxonomy->name ] = ( $format == 'full' ) ? array( 'label' => $taxonomy->labels->name, 'name' => $taxonomy->name ) : $taxonomy_name;
}
if ( is_array( $disabled_taxonomies ) ) {
foreach ( $disabled_taxonomies as $taxonomy ) {
if ( ! empty( $taxonomies_array[ $taxonomy ] ) ) {
unset( $taxonomies_array[ $taxonomy ] );
}
}
}
ksort( $taxonomies_array );
return ( empty( $tax ) ) ? $taxonomies_array : $taxonomies_array[ $tax ];
}
/**
* Get all post statuses supported by Permalink Manager
*/
static function get_post_statuses() {
$post_statuses = get_post_statuses();
if ( is_array( $post_statuses ) ) {
$post_statuses['future'] = __( 'Scheduled', 'permalink-manager' );
}
return apply_filters( 'permalink_manager_post_statuses', $post_statuses );
}
/**
* Get all the endpoints registered for WP_Rewrite object
*/
static function get_endpoints() {
global $wp_rewrite;
$pagination_endpoint = ( ! empty( $wp_rewrite->pagination_base ) ) ? $wp_rewrite->pagination_base : 'page';
// Start with default endpoints
$endpoints = "{$pagination_endpoint}|feed|embed|attachment|trackback";
if ( ! empty( $wp_rewrite->endpoints ) ) {
foreach ( $wp_rewrite->endpoints as $endpoint ) {
$endpoints .= "|{$endpoint[1]}";
}
}
return apply_filters( "permalink_manager_endpoints", str_replace( "/", "\/", $endpoints ) );
}
/**
* Check if the specific post is selected as a front-page
*
* @param int $page_id
*
* @return bool
*/
static function is_front_page( $page_id ) {
$front_page_id = get_option( 'page_on_front' );
$bool = ( ! empty( $front_page_id ) && $page_id == $front_page_id ) ? true : false;
return apply_filters( 'permalink_manager_is_front_page', $bool, $page_id, $front_page_id );
}
/**
* Check if the advanced mode is turned on
*
* @return bool
*/
static function is_advanced_mode_on() {
global $permalink_manager_options;
$bool = ( ! empty( $permalink_manager_options["general"]["advanced_mode"] ) && $permalink_manager_options["general"]["advanced_mode"] == 1 ) ? true : false;
return apply_filters( 'permalink_manager_advanced_mode_on', $bool );
}
/**
* Sanitize the multidimensional array
*
* @param array $data
*
* @return array
*/
static function sanitize_array( $data = array(), $sanitize = false ) {
if ( ! is_array( $data ) || ! count( $data ) ) {
return array();
}
foreach ( $data as $k => $v ) {
if ( ! is_array( $v ) && ! is_object( $v ) ) {
$data[ $k ] = ( $sanitize ) ? sanitize_text_field( trim( $v ) ) : htmlspecialchars( trim( $v ) );
}
if ( is_array( $v ) ) {
$data[ $k ] = self::sanitize_array( $v );
}
}
return $data;
}
/**
* Prepare an array of strings for use in SQL IN clause
*
* @param array $array Array of strings to prepare
* @param bool $sanitize
*
* @return string Comma-separated string with quoted values, e.g., "'value1','value2','value3'"
*/
static function prepare_array_for_sql_in( $array, $sanitize = true ) {
// Ensure we have an array
if ( ! is_array( $array ) ) {
$array = (array) $array;
}
// Remove empty values and sanitize
$array = array_filter( $array );
$array = ( $sanitize ) ? array_map( 'sanitize_text_field', $array ) : $array;
// Return empty string if array is empty
if ( empty( $array ) ) {
return '';
}
// Escape and wrap each value with quotes
$escaped = array_map( function ( $value ) {
return "'" . esc_sql( $value ) . "'";
}, $array );
// Join with comma
return implode( ',', $escaped );
}
/**
* Convert the text containing IDs and/or ID ranges to an array
*
* @param string $data
*
* @return array
*/
static function get_ids_from_string( $data ) {
// Remove whitespaces and other invalid characters
$raw_ids = esc_sql( preg_replace( '/[^\d,-]/', '', $data ) );
// Convert the string into the array with IDs and/or ranges
preg_match_all( "/([\d]+(?:-?[\d]+)?)/x", $raw_ids, $groups );
$ids = array();
if ( ! empty( $groups[1] ) ) {
foreach ( $groups[1] as $group ) {
if ( is_numeric( $group ) ) {
$ids[] = $group;
} else if ( preg_match( '/([\d]+)-([\d]+)/', $group, $range_limits ) ) {
$range_start = (int) $range_limits[1];
$range_end = (int) $range_limits[2];
if ( $range_start < $range_end ) {
$ids = array_merge( $ids, range( $range_start, $range_end ) );
}
}
}
$ids = array_unique( $ids );
}
return $ids;
}
/**
* Encode URI and keep special characters
*
* @param string $uri
*
* @return string
*/
static function encode_uri( $uri ) {
return str_replace( array( '%2F', '%2C', '%7C', '%2B' ), array( '/', ',', '|', '+' ), urlencode( $uri ) );
}
/**
* Sanitize the given string to URI-safe format
*
* @param string $str
* @param bool $keep_percent_sign
* @param bool $force_lowercase
* @param bool $sanitize_slugs
*
* @return string
*/
public static function sanitize_title( $str, $keep_percent_sign = false, $force_lowercase = null, $sanitize_slugs = null ) {
global $permalink_manager_options;
// Force lowercase & hyphens
$force_lowercase = ( ! is_null( $force_lowercase ) ) ? $force_lowercase : apply_filters( 'permalink_manager_force_lowercase_uris', true );
if ( is_null( $sanitize_slugs ) ) {
$sanitize_slugs = ( ! empty( $permalink_manager_options['general']['disable_slug_sanitization'] ) ) ? false : true;
}
// Allow to filter the slug before it is sanitized
$str = apply_filters( 'permalink_manager_pre_sanitize_title', $str, $keep_percent_sign, $force_lowercase, $sanitize_slugs );
// Remove accents & entities
$clean = ( empty( $permalink_manager_options['general']['keep_accents'] ) ) ? remove_accents( $str ) : $str;
$clean = str_replace( array( '<', '>', '&' ), '', $clean );
$percent_sign = ( $keep_percent_sign ) ? "\%" : "";
$sanitize_regex = apply_filters( "permalink_manager_sanitize_regex", "/[^\p{Xan}a-zA-Z0-9{$percent_sign}\/_\.|+, -]/ui", $percent_sign );
$clean = preg_replace( $sanitize_regex, '', $clean );
if ( empty( $clean ) ) {
return $str;
}
// Lowercase with non-ASCII characters support
if ( $force_lowercase && function_exists( 'mb_strtolower' ) && function_exists( 'wp_is_valid_utf8' ) && wp_is_valid_utf8( $clean ) ) {
$clean = mb_strtolower( $clean, 'UTF-8' );
} else if ( $force_lowercase ) {
$clean = strtolower( $clean );
}
// Remove ampersand
$clean = str_replace( array( '%26', '&' ), '', $clean );
// Remove special characters
if ( $sanitize_slugs !== false ) {
$clean = preg_replace( "/[\s|+-]+/", "-", $clean );
$clean = preg_replace( "/[,]+/", "", $clean );
$clean = preg_replace( '/([\.]+)(?![a-z]{3,4}$)/i', '', $clean );
$clean = preg_replace( '/([-\s+]\/[-\s+])/', '-', $clean );
$clean = preg_replace( '|-+|', '-', $clean );
} else {
$clean = preg_replace( "/[\s]+/", "-", $clean );
}
// Remove widow & duplicated slashes
$clean = preg_replace( '/([-]*[\/]+[-]*)/', '/', $clean );
$clean = preg_replace( '/([\/]+)/', '/', $clean );
// Trim slashes, dashes and whitespaces
$clean = trim( $clean, " /-" );
// Allow to filter the slug after it is sanitized
return apply_filters( 'permalink_manager_sanitize_title', $clean, $str, $keep_percent_sign, $force_lowercase, $sanitize_slugs );
}
/**
* Replace the string in custom permalink or native slug
*
* @param string $old_string
* @param string $new_string
* @param string $old_slug
* @param string $old_uri
* @param string $mode
*
* @return array|void
*/
public static function replace_uri_slug( $old_string, $new_string, $old_slug, $old_uri, $mode ) {
if ( preg_match( "/^\#.+\#$/i", $old_string ) ) {
$old_string = trim( $old_string, '#' );
$allowed_in_chars = '/[\w\d\s\.\*\+\-\?\!\/\|\(\)\{\}\[\]^$]/';
$allowed_out_chars = '/^[a-zA-Z0-9\.\/\$\-\_]+$/';
if ( ! preg_match( $allowed_in_chars, $old_string ) || ! preg_match( $allowed_out_chars, $new_string ) ) {
die( 'Invalid characters in REGEX formula.' );
}
$pattern = "#{$old_string}#";
$new_slug = ( $mode == 'slugs' ) ? preg_replace( $pattern, $new_string, $old_slug ) : $old_slug;
$new_uri = ( $mode != 'slugs' ) ? preg_replace( $pattern, $new_string, $old_uri ) : $old_uri;
} else {
$new_slug = ( $mode == 'slugs' ) ? str_replace( $old_string, $new_string, $old_slug ) : $old_slug;
$new_uri = ( $mode != 'slugs' ) ? str_replace( $old_string, $new_string, $old_uri ) : $old_uri;
}
return array( $new_slug, $new_uri );
}
/**
* Sanitize the final custom permalink URI
*
* @param string $uri
*
* @return string
*/
public static function clear_single_uri( $uri ) {
return self::sanitize_title( $uri, true );
}
/**
* Remove all slashes from given string
*
* @param string $uri
* @param string $replacement
*
* @return array|string|string[]|null
*/
public static function remove_slashes( $uri, $replacement = '' ) {
return preg_replace( "/[\/]+/", $replacement, $uri );
}
/**
* Replace the given slug with the actual title or custom permalink of specific post or term
*
* @param string $slug
* @param WP_Post|WP_Term $object
* @param bool $flat
* @param null $force_custom_slugs
* @param bool $allow_inherit
*
* @return string
*/
public static function force_custom_slugs( $slug, $object, $flat = false, $force_custom_slugs = null, $allow_inherit = true ) {
global $permalink_manager_options;
if ( empty( $force_custom_slugs ) ) {
$force_custom_slugs = ( ! empty( $permalink_manager_options['general']['force_custom_slugs'] ) ) ? $permalink_manager_options['general']['force_custom_slugs'] : false;
$force_custom_slugs = apply_filters( 'permalink_manager_force_custom_slugs', $force_custom_slugs, $slug, $object );
}
if ( $force_custom_slugs ) {
// A. Custom slug (title)
if ( $force_custom_slugs == 1 ) {
if ( ! empty( $object->name ) && ! empty( $object->taxonomy ) ) {
$title = $object->name;
} else if ( ! empty( $object->post_title ) && ! empty( $object->post_type ) ) {
$title = $object->post_title;
} else {
return $slug;
}
$title = wp_strip_all_tags( $title );
$title = self::remove_slashes( $title, '-' );
$new_slug = self::sanitize_title( $title );
} // B. Custom slug (inherit custom permalink from the parent object)
else if ( $allow_inherit ) {
$new_slug = basename( Permalink_Manager_URI_Functions::get_single_uri( $object, false, true ) );
}
$slug = ( ! empty( $new_slug ) ) ? preg_replace( '/([^\/]+)$/', $new_slug, $slug ) : $slug;
}
if ( $flat ) {
$slug = preg_replace( "/([^\/]+)(.*)/", "$1", $slug );
}
return $slug;
}
/**
* Ensures that a permalink is unique by appending a numeric suffix if needed
*
* @param string $new_uri
* @param string|int $element_id
* @param string|WP_Post|WP_Term $element
*
* @return string A unique custom permalink string.
*/
function force_unique_uri( $new_uri, $element_id, $element = '' ) {
// Determine the ID based on the object type
if ( is_a( $element, 'WP_Term' ) && strpos( current_action(), "_term_" ) !== false ) {
$element_id = $element->term_id;
$is_tax = true;
} elseif ( is_a( $element, 'WP_Post' ) ) {
$element_id = $element->ID;
}
if ( empty( $element_id ) || ! is_numeric( $element_id ) ) {
return $new_uri;
}
if ( ! empty( $is_tax ) ) {
$element_id = "tax-{$element_id}";
}
// Prepare base + extension
if ( ! preg_match( '/^(.+?)(?:-([\d]+))?(\.[^\.]+$|$)/', $new_uri, $parts ) ) {
return $new_uri; // fallback if regex fails
}
$base = $parts[1];
$index = empty( $parts[2] ) ? 1 : (int) $parts[2];
$extension = $parts[3];
$unique_uri = $new_uri;
// Increment suffix until URI is unique
while ( Permalink_Manager_URI_Functions::is_uri_duplicated( $unique_uri, $element_id ) ) {
$index ++;
$unique_uri = "{$base}-{$index}{$extension}";
}
return $unique_uri;
}
/**
* Reload the globals when the blog is switched (multisite)
*
* @param int $new_blog_id
*/
public function reload_globals_in_network( $new_blog_id ) {
global $permalink_manager_uris, $permalink_manager_redirects, $permalink_manager_external_redirects;
if ( function_exists( 'get_blog_option' ) ) {
$permalink_manager_uris = get_blog_option( $new_blog_id, 'permalink-manager-uris', array() );
$permalink_manager_redirects = get_blog_option( $new_blog_id, 'permalink-manager-redirects', array() );
$permalink_manager_external_redirects = get_blog_option( $new_blog_id, 'permalink-manager-external-redirects', array() );
}
}
}
core/permalink-manager-actions.php 0000644 00000076335 15220204736 0013254 0 ustar 00 trigger_filter_action();
}
$actions_map = array(
'uri_editor_nonce' => array( 'function' => 'update_all_permalinks', 'display_uri_table' => true ),
'permalink_manager_options' => array( 'function' => 'save_settings' ),
'permalink_manager_permastructs' => array( 'function' => 'save_permastructures' ),
'import' => array( 'function' => 'import_custom_permalinks_uris' ),
);
// 3. Find the action
foreach ( $actions_map as $action => $map ) {
$nonce = ( isset( $_POST[ $action ] ) ) ? sanitize_key( $_POST[ $action ] ) : '';
if ( ! empty( $nonce ) && wp_verify_nonce( $nonce, 'permalink-manager' ) ) {
// Execute the function
$output = call_user_func( array( $this, $map['function'] ) );
// Get list of updated URIs
if ( ! empty( $map['display_uri_table'] ) ) {
$updated_slugs_count = ( isset( $output['updated_count'] ) && $output['updated_count'] > 0 ) ? $output['updated_count'] : false;
$updated_slugs_array = ( $updated_slugs_count ) ? $output['updated'] : '';
}
// Trigger only one function
break;
}
}
// 4. Display the slugs table (and append the globals)
if ( isset( $updated_slugs_count ) && isset( $updated_slugs_array ) ) {
$permalink_manager_after_sections_html .= Permalink_Manager_UI_Elements::display_updated_slugs( $updated_slugs_array );
}
}
/**
* Adjust the Bulk URI Editor filter URL
*
* @return void
*/
public function trigger_filter_action() {
if ( empty( $_POST['uri_editor_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['uri_editor_nonce'] ), 'permalink-manager' ) ) {
return;
}
$query_args = array(
'langcode' => ! empty( $_REQUEST['langcode'] ) ? sanitize_key( $_REQUEST['langcode'] ) : null,
'month' => ! empty( $_REQUEST['month'] ) ? sanitize_key( $_REQUEST['month'] ) : null,
's' => ! empty( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : null,
);
if ( ! empty( $query_args ) ) {
$sendback = remove_query_arg( array_keys( $query_args ), wp_get_referer() );
$sendback = add_query_arg( array_filter( $query_args ), $sendback );
wp_safe_redirect( $sendback );
exit;
}
}
/**
* Route the requests to the additional tools-related functions with the relevant callbacks
*/
public static function extra_actions() {
global $permalink_manager_before_sections_html;
if ( current_user_can( 'manage_options' ) && ! empty( $_GET['permalink-manager-nonce'] ) ) {
// Check if the nonce field is correct
$nonce = sanitize_key( $_GET['permalink-manager-nonce'] );
if ( ! wp_verify_nonce( $nonce, 'permalink-manager' ) ) {
$permalink_manager_before_sections_html = Permalink_Manager_UI_Elements::get_alert_message( __( 'You are not allowed to remove Permalink Manager data!', 'permalink-manager' ), 'error updated_slugs' );
return;
}
if ( isset( $_GET['clear-permalink-manager-uris'] ) ) {
self::clear_all_uris();
} else if ( isset( $_GET['remove-permalink-manager-settings'] ) ) {
$option_name = sanitize_key( $_GET['remove-permalink-manager-settings'] );
self::remove_plugin_data( $option_name );
} else if ( ! empty( $_REQUEST['remove-uri'] ) ) {
$uri_key = sanitize_key( $_REQUEST['remove-uri'] );
self::force_clear_single_element_uris_and_redirects( $uri_key );
} else if ( ! empty( $_REQUEST['remove-redirect'] ) ) {
$redirect_key = sanitize_key( $_REQUEST['remove-redirect'] );
self::force_clear_single_redirect( $redirect_key );
}
} else if ( ! empty( $_POST['screen-options-apply'] ) ) {
self::save_screen_options();
}
}
/**
* Bulk remove obsolete custom permalinks and redirects
*/
public static function clear_all_uris() {
global $permalink_manager_redirects, $permalink_manager_before_sections_html;
// Get all custom permalinks & redirects
$custom_permalinks = Permalink_Manager_URI_Functions::get_all_uris();
$custom_redirects = (array) $permalink_manager_redirects;
// Check if array with custom URIs exists
if ( empty( $custom_permalinks ) ) {
return;
}
// Count removed URIs & redirects
$removed_uris = 0;
$removed_redirects = 0;
// Get all element IDs
$element_ids = array_merge( array_keys( $custom_permalinks ), array_keys( $custom_redirects ) );
// 1. Remove unused custom URI & redirects for deleted post or term
foreach ( $element_ids as $element_id ) {
$count = self::clear_single_element_uris_and_redirects( $element_id, true );
$removed_uris = ( ! empty( $count[0] ) ) ? $count[0] + $removed_uris : $removed_uris;
$removed_redirects = ( ! empty( $count[1] ) ) ? $count[1] + $removed_redirects : $removed_redirects;
}
// 2. Keep only a single redirect
$removed_redirects += self::clear_redirects_array();
// 3. Save cleared URIs & Redirects
if ( $removed_uris > 0 || $removed_redirects > 0 ) {
Permalink_Manager_URI_Functions::save_all_uris();
update_option( 'permalink-manager-redirects', array_filter( $permalink_manager_redirects ) );
// Translators: 1: Number of custom URIs, 2: Number of custom redirects.
$permalink_manager_before_sections_html .= Permalink_Manager_UI_Elements::get_alert_message( sprintf( __( '%1$d Custom URIs and %2$d Custom Redirects were removed!', 'permalink-manager' ), $removed_uris, $removed_redirects ), 'updated updated_slugs' );
} else {
$permalink_manager_before_sections_html .= Permalink_Manager_UI_Elements::get_alert_message( __( 'No Custom URIs or Custom Redirects were removed!', 'permalink-manager' ), 'error updated_slugs' );
}
}
/**
* Remove obsolete custom permalink & redirects for specific post or term
*
* @param string|int $element_id
* @param bool $count_removed
*
* @return array
*/
public static function clear_single_element_uris_and_redirects( $element_id, $count_removed = false ) {
global $wpdb, $permalink_manager_redirects, $permalink_manager_options;
// Count removed URIs & redirects
$removed_uris = 0;
$removed_redirects = 0;
// Only admin users can remove the broken URIs for removed post types & taxonomies
$check_if_admin = is_admin();
// Check if the advanced mode is turned on
$advanced_mode = Permalink_Manager_Helper_Functions::is_advanced_mode_on();
// If "Disable URI Editor to disallow Permalink changes" is set globally, the pages that follow the global settings should also be removed
if ( $advanced_mode && ! empty( $permalink_manager_options["general"]["auto_update_uris"] ) && $permalink_manager_options["general"]["auto_update_uris"] == 2 ) {
$strict_mode = true;
} else {
$strict_mode = false;
}
// 1. Check if element exists
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct SQL query is needed to join multiple tables
if ( strpos( $element_id, 'tax-' ) !== false ) {
$term_id = preg_replace( "/[^0-9]/", "", $element_id );
$term_info = $wpdb->get_row( $wpdb->prepare( "SELECT taxonomy, meta_value FROM {$wpdb->term_taxonomy} AS t LEFT JOIN {$wpdb->termmeta} AS tm ON tm.term_id = t.term_id AND tm.meta_key = 'auto_update_uri' WHERE t.term_id = %d", $term_id ) );
// Custom URIs for disabled taxonomies may only be deleted via the admin dashboard, although they will always be removed if the term no longer exists in the database
$remove = ( ! empty( $term_info->taxonomy ) ) ? Permalink_Manager_Helper_Functions::is_taxonomy_disabled( $term_info->taxonomy, $check_if_admin ) : true;
// Remove custom URIs for URIs disabled in URI Editor
if ( $strict_mode ) {
$remove = ( empty( $term_info->meta_value ) || $term_info->meta_value == 2 ) ? true : $remove;
} else {
$remove = ( ! empty( $term_info->meta_value ) && $term_info->meta_value == 2 ) ? true : $remove;
}
} else if ( is_numeric( $element_id ) ) {
$post_info = $wpdb->get_row( $wpdb->prepare( "SELECT post_type, meta_value FROM {$wpdb->posts} AS p LEFT JOIN {$wpdb->postmeta} AS pm ON pm.post_ID = p.ID AND pm.meta_key = 'auto_update_uri' WHERE ID = %d AND post_status NOT IN ('auto-draft', 'trash') AND post_type != 'nav_menu_item'", $element_id ) );
// Custom URIs for disabled post types may only be deleted via the admin dashboard, although they will always be removed if the post no longer exists in the database
$remove = ( ! empty( $post_info->post_type ) ) ? Permalink_Manager_Helper_Functions::is_post_type_disabled( $post_info->post_type, $check_if_admin ) : true;
// Remove custom URIs for URIs disabled in URI Editor
if ( $strict_mode ) {
$remove = ( empty( $post_info->meta_value ) || $post_info->meta_value == 2 ) ? true : $remove;
} else {
$remove = ( ! empty( $post_info->meta_value ) && $post_info->meta_value == 2 ) ? true : $remove;
}
// Remove custom URIs for attachments redirected with Yoast's SEO Premium
$yoast_permalink_options = ( class_exists( 'WPSEO_Premium' ) ) ? get_option( 'wpseo_permalinks' ) : array();
if ( ! empty( $yoast_permalink_options['redirectattachment'] ) && $post_info->post_type == 'attachment' ) {
$attachment_parent = $wpdb->get_var( $wpdb->prepare( "SELECT post_parent FROM {$wpdb->prefix}posts WHERE ID = %d AND post_type = %s", $element_id, 'attachment' ) );
if ( ! empty( $attachment_parent ) ) {
$remove = true;
}
}
}
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
// 2A. Remove ALL unused custom permalinks & redirects
if ( ! empty( $remove ) ) {
$current_uri = Permalink_Manager_URI_Functions::get_single_uri( $element_id, false, true, null );
// Remove URI
if ( ! empty( $current_uri ) ) {
$removed_uris = 1;
Permalink_Manager_URI_Functions::remove_single_uri( $element_id, null, false );
}
// Remove all custom redirects
if ( ! empty( $permalink_manager_redirects[ $element_id ] ) && is_array( $permalink_manager_redirects[ $element_id ] ) ) {
$removed_redirects = count( $permalink_manager_redirects[ $element_id ] );
unset( $permalink_manager_redirects[ $element_id ] );
}
} // 2B. Check if the post/term uses the same URI for both permalink & custom redirects
else {
$removed_redirect = self::clear_single_element_duplicated_redirect( $element_id, false );
$removed_redirects = ( ! empty( $removed_redirect ) ) ? 1 : 0;
}
// Check if function should only return the counts or update
if ( $count_removed ) {
return array( $removed_uris, $removed_redirects );
} else if ( ! empty( $removed_uris ) || ! empty( $removed_redirects ) ) {
Permalink_Manager_URI_Functions::save_all_uris();
update_option( 'permalink-manager-redirects', array_filter( $permalink_manager_redirects ) );
}
return array();
}
/**
* Remove the duplicated custom redirect if the post/term has the same URI for both custom permalink and custom redirect
*
* @param string|int $element_id
* @param bool $save_redirects
* @param string $uri
*
* @return int
*/
public static function clear_single_element_duplicated_redirect( $element_id, $save_redirects = true, $uri = null ) {
global $permalink_manager_redirects;
// If the custom permalink is not changed ($uri) use the one that is currently used
if ( ! empty( $uri ) ) {
$current_uri = $uri;
} else {
$current_uri = Permalink_Manager_URI_Functions::get_single_uri( $element_id, false, true, null );
}
if ( ! empty( $current_uri ) && ! empty( $permalink_manager_redirects[ $element_id ] ) && in_array( $current_uri, $permalink_manager_redirects[ $element_id ] ) ) {
$duplicated_redirect_id = array_search( $current_uri, $permalink_manager_redirects[ $element_id ] );
unset( $permalink_manager_redirects[ $element_id ][ $duplicated_redirect_id ] );
}
// Update the redirects array in the database if the duplicated redirect was unset
if ( isset( $duplicated_redirect_id ) && $save_redirects ) {
update_option( 'permalink-manager-redirects', array_filter( $permalink_manager_redirects ) );
}
return ( isset( $duplicated_redirect_id ) ) ? 1 : 0;
}
/**
* Remove the duplicated if the same URI is used for multiple custom redirects and return the removed redirects count
*
* @param bool $save_redirects
*
* @return int
*/
public static function clear_redirects_array( $save_redirects = false ) {
global $permalink_manager_redirects;
$removed_redirects = 0;
$all_redirect_duplicates = Permalink_Manager_Admin_Functions::get_all_duplicates();
foreach ( $all_redirect_duplicates as $single_redirect_duplicate ) {
$last_element = reset( $single_redirect_duplicate );
foreach ( $single_redirect_duplicate as $redirect_key ) {
// Keep a single redirect
if ( $last_element == $redirect_key ) {
continue;
}
preg_match( "/redirect-(\d+)_(tax-\d+|\d+)/", $redirect_key, $ids );
if ( ! empty( $ids[2] ) && ! empty( $permalink_manager_redirects[ $ids[2] ][ $ids[1] ] ) ) {
$removed_redirects ++;
unset( $permalink_manager_redirects[ $ids[2] ][ $ids[1] ] );
}
}
}
// Update the redirects array in the database if the duplicated redirect was unset
if ( isset( $duplicated_redirect_id ) && $save_redirects ) {
update_option( 'permalink-manager-redirects', array_filter( $permalink_manager_redirects ) );
}
return $removed_redirects;
}
/**
* Remove custom permalinks & custom redirects for requested post or term
*
* @param $uri_key
*
* @return bool
*/
public static function force_clear_single_element_uris_and_redirects( $uri_key ) {
global $permalink_manager_redirects, $permalink_manager_before_sections_html;
$custom_uri = Permalink_Manager_URI_Functions::get_single_uri( $uri_key, false, true, null );
// Check if custom URI is set
if ( ! empty( $custom_uri ) ) {
Permalink_Manager_URI_Functions::remove_single_uri( $uri_key, null, true );
// translators: %s is the custom URI that was removed.
$updated = Permalink_Manager_UI_Elements::get_alert_message( sprintf( __( 'URI "%s" was removed successfully!', 'permalink-manager' ), $custom_uri ), 'updated' );
}
// Check if custom redirects are set
if ( isset( $permalink_manager_redirects[ $uri_key ] ) ) {
unset( $permalink_manager_redirects[ $uri_key ] );
update_option( 'permalink-manager-redirects', $permalink_manager_redirects );
}
if ( empty( $updated ) ) {
$permalink_manager_before_sections_html .= Permalink_Manager_UI_Elements::get_alert_message( __( 'URI and/or custom redirects does not exist or were already removed!', 'permalink-manager' ), 'error' );
} else {
// Display the alert in admin panel
if ( isset( $permalink_manager_before_sections_html ) && is_admin() ) {
$permalink_manager_before_sections_html .= $updated;
}
}
return true;
}
/**
* Remove only custom redirects for requested post or term
*
* @param string $redirect_key
*/
public static function force_clear_single_redirect( $redirect_key ) {
global $permalink_manager_redirects, $permalink_manager_before_sections_html;
preg_match( "/redirect-(\d+)_(tax-\d+|\d+)/", $redirect_key, $ids );
if ( ! empty( $permalink_manager_redirects[ $ids[2] ][ $ids[1] ] ) ) {
unset( $permalink_manager_redirects[ $ids[2] ][ $ids[1] ] );
update_option( 'permalink-manager-redirects', array_filter( $permalink_manager_redirects ) );
$permalink_manager_before_sections_html = Permalink_Manager_UI_Elements::get_alert_message( __( 'The redirect was removed successfully!', 'permalink-manager' ), 'updated' );
}
}
/**
* Save "Screen Options"
*/
public static function save_screen_options() {
check_admin_referer( 'screen-options-nonce', 'screenoptionnonce' );
$screen_options = ( isset( $_POST['screen-options'] ) ) ? map_deep( wp_unslash( $_POST['screen-options'] ), 'sanitize_text_field' ) : array();
if ( ! empty( $screen_options ) ) {
self::save_settings( 'screen-options', $screen_options );
}
}
/**
* Save the plugin settings
*
* @param bool $field
* @param bool $value
* @param bool $display_alert
*/
public static function save_settings( $field = false, $value = false, $display_alert = true ) {
global $permalink_manager_options, $permalink_manager_before_sections_html;
// Info: The settings array is used also by "Screen Options"
$new_options = $permalink_manager_options;
// Save only selected field/sections
if ( $field && $value ) {
$new_options[ $field ] = $value;
} else {
$post_fields = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- The nonce is already validated in enclosing function
foreach ( $post_fields as $option_name => $option_value ) {
$new_options[ $option_name ] = $option_value;
}
}
// Allow only white-listed option groups
foreach ( $new_options as $group => $group_options ) {
if ( ! in_array( $group, array( 'licence', 'screen-options', 'general', 'permastructure-settings', 'stop-words' ) ) ) {
unset( $new_options[ $group ] );
}
}
// Sanitize & override the global with new settings
$new_options = Permalink_Manager_Helper_Functions::sanitize_array( $new_options );
$permalink_manager_options = $new_options = array_filter( $new_options );
// Save the settings in database
update_option( 'permalink-manager', $new_options );
// Display the message
$permalink_manager_before_sections_html .= ( $display_alert ) ? Permalink_Manager_UI_Elements::get_alert_message( __( 'The settings are saved!', 'permalink-manager' ), 'updated' ) : "";
}
/**
* Save the permastructures
*/
public static function save_permastructures() {
global $permalink_manager_permastructs;
$permastructure_options = $permastructures = array();
$permastructure_types = array( 'post_types', 'taxonomies' );
// Split permastructures & sanitize them
// phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- The nonce is already validated in enclosing function
foreach ( $permastructure_types as $type ) {
if ( empty( $_POST[ $type ] ) || ! is_array( $_POST[ $type ] ) ) {
continue;
}
$permastructures[ $type ] = $_POST[ $type ];
foreach ( $permastructures[ $type ] as &$single_permastructure ) {
$single_permastructure = Permalink_Manager_Helper_Functions::sanitize_title( $single_permastructure, true, false, false );
$single_permastructure = trim( $single_permastructure, '\/ ' );
}
}
if ( ! empty( $_POST['permastructure-settings'] ) ) {
$permastructure_options = $_POST['permastructure-settings'];
}
// phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
// A. Permastructures
if ( ! empty( $permastructures['post_types'] ) || ! empty( $permastructures['taxonomies'] ) ) {
// Override the global with settings
$permalink_manager_permastructs = $permastructures;
// Save the settings in database
update_option( 'permalink-manager-permastructs', $permastructures );
}
// B. Permastructure settings
if ( ! empty( $permastructure_options ) ) {
self::save_settings( 'permastructure-settings', $permastructure_options );
}
}
/**
* Update all permalinks in "Bulk URI Editor"
*/
function update_all_permalinks() {
// Check if posts or terms should be updated
if ( ! empty( $_POST['content_type'] ) && $_POST['content_type'] == 'taxonomies' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- The nonce is already validated in enclosing function
return Permalink_Manager_URI_Functions_Tax::update_all_permalinks();
} else {
return Permalink_Manager_URI_Functions_Post::update_all_permalinks();
}
}
/**
* Remove a specific section of the plugin data stored in the database
*
* @param $field_name
*/
public static function remove_plugin_data( $field_name ) {
global $permalink_manager, $permalink_manager_before_sections_html;
// Make sure that the user is allowed to remove the plugin data
if ( ! current_user_can( 'manage_options' ) ) {
$permalink_manager_before_sections_html .= Permalink_Manager_UI_Elements::get_alert_message( __( 'You are not allowed to remove Permalink Manager data!', 'permalink-manager' ), 'error updated_slugs' );
}
switch ( $field_name ) {
case 'uris' :
$option_name = 'permalink-manager-uris';
$alert = __( 'Custom permalinks', 'permalink-manager' );
break;
case 'redirects' :
$option_name = 'permalink-manager-redirects';
$alert = __( 'Custom redirects', 'permalink-manager' );
break;
case 'external-redirects' :
$option_name = 'permalink-manager-external-redirects';
$alert = __( 'External redirects', 'permalink-manager' );
break;
case 'permastructs' :
$option_name = 'permalink-manager-permastructs';
$alert = __( 'Permastructure settings', 'permalink-manager' );
break;
case 'settings' :
$option_name = 'permalink-manager';
$alert = __( 'Permastructure settings', 'permalink-manager' );
break;
default :
$alert = '';
}
if ( ! empty( $option_name ) ) {
// Remove the option from DB
delete_option( $option_name );
// Reload globals
$permalink_manager->get_options_and_globals();
// Translators: %s is the name of the data field that was removed
$alert_message = sprintf( __( '%s were removed!', 'permalink-manager' ), $alert );
$permalink_manager_before_sections_html .= Permalink_Manager_UI_Elements::get_alert_message( $alert_message, 'updated updated_slugs' );
}
}
/**
* Trigger bulk tools ("Regenerate & reset", "Find & replace") via AJAX
*/
function ajax_bulk_tools() {
global $sitepress, $wpdb;
// Define variables
$return = array( 'alert' => Permalink_Manager_UI_Elements::get_alert_message( __( 'No items were processed!', 'permalink-manager' ), 'error updated_slugs' ) );
// Get the name of the function
if ( isset( $_POST['regenerate'] ) ) {
$nonce_name = sanitize_key( $_POST['regenerate'] );
$operation = 'regenerate';
} else if ( isset( $_POST['find_and_replace'] ) ) {
$nonce_name = sanitize_key( $_POST['find_and_replace'] );
$operation = ( ! empty( $_POST['old_string'] ) && ! empty( $_POST['new_string'] ) ) ? 'find_and_replace' : '';
}
// Validate the nonce
if ( empty( $nonce_name ) || ! wp_verify_nonce( $nonce_name, 'permalink-manager' ) ) {
$error = true;
$return = array( 'alert' => Permalink_Manager_UI_Elements::get_alert_message( __( 'Nonce is invalid!', 'permalink-manager' ), 'error updated_slugs' ) );
}
// Get the session ID
$uniq_id = ( ! empty( $_POST['pm_session_id'] ) ) ? sanitize_key( $_POST['pm_session_id'] ) : '';
// Get content type & post statuses
if ( ! empty( $_POST['content_type'] ) && $_POST['content_type'] == 'taxonomies' ) {
$content_type = 'taxonomies';
if ( empty( $_POST['taxonomies'] ) ) {
$error = true;
$return = array( 'alert' => Permalink_Manager_UI_Elements::get_alert_message( __( 'No taxonomy selected!', 'permalink-manager' ), 'error updated_slugs' ) );
}
} else {
$content_type = 'post_types';
// Check if any post type was selected
if ( empty( $_POST['post_types'] ) ) {
$error = true;
$return = array( 'alert' => Permalink_Manager_UI_Elements::get_alert_message( __( 'No post type selected!', 'permalink-manager' ), 'error updated_slugs' ) );
}
// Check post status
if ( empty( $_POST['post_statuses'] ) ) {
$error = true;
$return = array( 'alert' => Permalink_Manager_UI_Elements::get_alert_message( __( 'No post status selected!', 'permalink-manager' ), 'error updated_slugs' ) );
}
}
if ( ! empty( $operation ) && empty( $error ) ) {
// Hotfix for WPML (start)
if ( $sitepress ) {
remove_filter( 'get_terms_args', array( $sitepress, 'get_terms_args_filter' ), 10 );
remove_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1 );
remove_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ), 10 );
remove_filter( 'get_pages', array( $sitepress, 'get_pages_adjust_ids' ), 1 );
}
// Get the mode
$mode = ( isset( $_POST['mode'] ) ) ? sanitize_key( $_POST['mode'] ) : 'custom_uris';
$preview_mode = ( ! empty( $_POST['preview_mode'] ) ) ? true : false;
// Get items (try to get them from transient)
$items = get_transient( "pm_{$uniq_id}" );
// Get the iteration count and chunk size
$iteration = isset( $_POST['iteration'] ) ? intval( $_POST['iteration'] ) : 1;
$chunk_size = apply_filters( 'permalink_manager_chunk_size', 50 );
if ( empty( $items ) && ! empty ( $chunk_size ) ) {
if ( $content_type == 'taxonomies' ) {
$items = Permalink_Manager_URI_Functions_Tax::get_items();
} else {
$items = Permalink_Manager_URI_Functions_Post::get_items();
}
if ( ! empty( $items ) ) {
// Count how many items need to be processed
$total = count( $items );
// Split items array into chunks and save them to transient
$items = array_chunk( $items, $chunk_size );
set_transient( "pm_{$uniq_id}", $items, 600 );
// Check for MySQL errors
if ( ! empty( $wpdb->last_error ) ) {
printf( '%s (%sMB)', esc_html( $wpdb->last_error ), esc_html( number_format( strlen( serialize( $items ) ) / 1000000, 2 ) ) );
http_response_code( 500 );
die();
}
}
}
// Get homepage URL and ensure that it ends with slash
$home_url = Permalink_Manager_Permastructure_Functions::get_permalink_base() . "/";
// Process the variables from $_POST object
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- The escaped slashes can be a part of REGEX formula
$old_string = ( ! empty( $_POST['old_string'] ) ) ? str_replace( $home_url, '', esc_sql( sanitize_text_field( $_POST['old_string'] ) ) ) : '';
$new_string = ( ! empty( $_POST['new_string'] ) ) ? str_replace( $home_url, '', esc_sql( sanitize_text_field( $_POST['new_string'] ) ) ) : '';
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
// Process only one subarray
if ( ! empty( $items[ $iteration - 1 ] ) ) {
$chunk = $items[ $iteration - 1 ];
// Check how many iterations are needed
$total_iterations = count( $items );
if ( $content_type == 'taxonomies' ) {
$output = Permalink_Manager_URI_Functions_Tax::bulk_process_items( $chunk, $mode, $operation, $old_string, $new_string, $preview_mode );
} else {
$output = Permalink_Manager_URI_Functions_Post::bulk_process_items( $chunk, $mode, $operation, $old_string, $new_string, $preview_mode );
}
if ( ! empty( $output['updated_count'] ) ) {
$return = array_merge( $return, (array) Permalink_Manager_UI_Elements::display_updated_slugs( $output['updated'], true, true, $preview_mode ) );
$return['updated_count'] = $output['updated_count'];
}
// Send total number of processed items with a first chunk
if ( ! empty( $total ) && ! empty( $total_iterations ) && $iteration == 1 ) {
$return['total'] = $total;
$return['items'] = $items;
}
$return['iteration'] = $iteration;
$return['total_iterations'] = $total_iterations;
$return['progress'] = $chunk_size * $iteration;
$return['chunk'] = $chunk;
// After all chunks are processed remove the transient
if ( $iteration == $total_iterations ) {
delete_transient( "pm_{$uniq_id}" );
}
}
// Hotfix for WPML (end)
if ( $sitepress ) {
add_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ), 10, 4 );
add_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1, 1 );
add_filter( 'get_terms_args', array( $sitepress, 'get_terms_args_filter' ), 10, 2 );
add_filter( 'get_pages', array( $sitepress, 'get_pages_adjust_ids' ), 1, 2 );
}
}
wp_send_json( $return );
die();
}
/**
* Save permalink via AJAX
*/
public function ajax_save_permalink() {
$element_id = ( ! empty( $_POST['permalink-manager-edit-uri-element-id'] ) ) ? sanitize_key( $_POST['permalink-manager-edit-uri-element-id'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is validated inside update_post_hook
if ( ! empty( $element_id ) && is_numeric( $element_id ) && current_user_can( 'edit_post', $element_id ) ) {
Permalink_Manager_URI_Functions_Post::update_post_hook( $element_id );
// Reload URI Editor & clean post cache
clean_post_cache( $element_id );
die();
}
}
/**
* Check if URI was used before
*/
function ajax_detect_duplicates() {
$duplicate_alert = __( "Permalink is already in use, please select another one!", "permalink-manager" );
$duplicates_data = array();
$custom_uris = ( ! empty( $_REQUEST['custom_uris'] ) ) ? Permalink_Manager_Helper_Functions::sanitize_array( $_REQUEST['custom_uris'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended -- No data is saved here
if ( ! empty( $custom_uris ) ) {
// Check each URI
foreach ( $custom_uris as $raw_element_id => $element_uri ) {
$element_id = sanitize_key( $raw_element_id );
$duplicates_data[ $element_id ] = Permalink_Manager_URI_Functions::is_uri_duplicated( $element_uri, $element_id ) ? $duplicate_alert : 0;
}
} else if ( ! empty( $_REQUEST['custom_uri'] ) && ! empty( $_REQUEST['element_id'] ) ) {
$duplicates_data = Permalink_Manager_URI_Functions::is_uri_duplicated( $_REQUEST['custom_uri'], sanitize_key( $_REQUEST['element_id'] ) );
}
wp_send_json( $duplicates_data );
}
/**
* Hide global notices (AJAX)
*/
function ajax_hide_global_notice() {
global $permalink_manager_alerts;
// Get the ID of the alert
$alert_id = ( ! empty( $_REQUEST['alert_id'] ) ) ? sanitize_title( $_REQUEST['alert_id'] ) : "";
if ( ! empty( $permalink_manager_alerts[ $alert_id ] ) ) {
$dismissed_transient_name = sprintf( 'permalink-manager-notice_%s', $alert_id );
$dismissed_time = ( ! empty( $permalink_manager_alerts[ $alert_id ]['dismissed_time'] ) ) ? (int) $permalink_manager_alerts[ $alert_id ]['dismissed_time'] : DAY_IN_SECONDS;
set_transient( $dismissed_transient_name, 1, $dismissed_time );
}
}
/**
* Import old URIs from "Custom Permalinks" (Pro)
*/
function import_custom_permalinks_uris() {
Permalink_Manager_Third_Parties::import_custom_permalinks_uris();
}
}
core/permalink-manager-permastructures-functions.php 0000644 00000012003 15220204736 0017050 0 ustar 00 get_page_permastruct();
} else if ( $post_type == 'post' ) {
$permastruct = get_option( 'permalink_structure' );
} else {
$permastruct = $wp_rewrite->get_extra_permastruct( $post_type );
}
return ( $remove_post_tag ) ? trim( str_replace( array( "%postname%", "%pagename%", "%{$post_type}%" ), "", $permastruct ), "/" ) : $permastruct;
}
/**
* Get the post name permastructure tag for specific post type
*
* @param string $post_type
*
* @return string
*/
static function get_post_tag( $post_type ) {
// Get the post type (with fix for posts & pages)
if ( $post_type == 'page' ) {
$post_type_tag = '%pagename%';
} else if ( $post_type == 'post' ) {
$post_type_tag = '%postname%';
} else {
$post_type_tag = "%{$post_type}%";
}
return $post_type_tag;
}
/**
* Check if any of slug tags is present inside Permastructure settings
*
* @param $default_uri
* @param $slug_tags
* @param $content_element
*
* @return bool|null
*/
public static function is_slug_tag_present( $default_uri, $slug_tags, $content_element ) {
global $permalink_manager_options;
// Check if any post tag is present in custom permastructure
if ( ! empty( $content_element->post_type ) ) {
$content_type = $content_element->post_type;
$content_type_key = 'post_types';
} else if ( ! empty( $content_element->taxonomy ) ) {
$content_type = $content_element->taxonomy;
$content_type_key = 'taxonomies';
} else {
return null;
}
$permastructure_settings = ( ! empty( $permalink_manager_options['permastructure-settings'] ) ) ? $permalink_manager_options['permastructure-settings'] : array();
$do_not_append_settings = ( ! empty( $permastructure_settings['do_not_append_slug'] ) ) ? $permastructure_settings['do_not_append_slug'] : array();
$do_not_append_slug = ( ! empty( $do_not_append_settings[ $content_type_key ] ) && ! empty( $do_not_append_settings[ $content_type_key ][ $content_type ] ) ) ? true : false;
$do_not_append_slug = apply_filters( "permalink_manager_do_not_append_slug", $do_not_append_slug, $content_type, $content_element );
if ( ! $do_not_append_slug ) {
foreach ( $slug_tags as $tag ) {
if ( strpos( $default_uri, $tag ) !== false ) {
$do_not_append_slug = true;
break;
}
}
}
// 3F. Replace the post tags with slugs or append the slug if no post tag is defined
if ( ! empty( $do_not_append_slug ) ) {
return true;
} else {
return false;
}
}
/**
* Replace empty placeholder tags & remove BOM
*
* @param string $default_uri
* @param string $native_slug
* @param string $element
* @param string $slug
* @param bool $native_uri
*
* @return string
*/
public static function replace_empty_placeholder_tags( $default_uri, $native_slug = "", $element = "", $slug = "", $native_uri = false ) {
// Remove the BOM
$default_uri = str_replace( array( "\xEF\xBB\xBF", "%ef%bb%bf" ), '', $default_uri );
// Encode the URI before placeholders are removed
$chunks = explode( '/', $default_uri );
foreach ( $chunks as &$chunk ) {
if ( ! preg_match( "/^(%.+?%)$/", $chunk ) && preg_match( '/%[A-F0-9]{2}%[A-F0-9]{2}/i', $chunk ) ) {
$chunk = rawurldecode( $chunk );
}
}
$default_uri = implode( "/", $chunks );
$empty_tag_replacement = apply_filters( 'permalink_manager_empty_tag_replacement', '', $element );
$default_uri = preg_replace( "/%(.+?)%/", $empty_tag_replacement, $default_uri );
$default_uri = str_replace( "//", "/", $default_uri );
return trim( $default_uri, "/" );
}
/**
* Get the permalink base (home URL) for custom permalink
*
* @param string|int|WP_Post|WP_Term $element
*
* @return string
*/
public static function get_permalink_base( $element = null ) {
$home_url = trim( get_option( 'home' ), "/" );
// Make sure that the custom permalinks have a valid scheme
if ( strpos( $home_url, 'http' ) === false ) {
$home_url = set_url_scheme( $home_url );
}
return apply_filters( 'permalink_manager_filter_permalink_base', $home_url, $element );
}
} core/permalink-manager-admin-functions.php 0000644 00000025726 15220204736 0014710 0 ustar 00 sections = apply_filters( 'permalink_manager_sections', array() );
$this->get_current_section();
}
/**
* Use the native URL for "Customize" button in the admin bar
*
* @param WP_Admin_Bar $wp_admin_bar
*/
public function fix_customize_url( $wp_admin_bar ) {
global $permalink_manager_ignore_permalink_filters;
$object = get_queried_object();
$customize = $wp_admin_bar->get_node( 'customize' );
if ( empty( $customize->href ) ) {
return;
}
$permalink_manager_ignore_permalink_filters = true;
if ( ! empty( $object->ID ) && is_a( $object, 'WP_Post' ) ) {
$new_url = get_permalink( $object->ID );
} else if ( ! empty( $object->taxonomy ) && is_a( $object, 'WP_Term' ) ) {
$new_url = get_term_link( $object, $object->taxonomy );
}
$permalink_manager_ignore_permalink_filters = false;
if ( ! empty( $new_url ) ) {
// The original permalink should be already encoded via "utf8_uri_encode()" in "sanitize_title_with_dashes()" function, so there is no need to encode them once again
$new_url = filter_var( $new_url, FILTER_SANITIZE_URL );
$customize_url = preg_replace( '/url=([^&]+)/', "url={$new_url}", $customize->href );
$wp_admin_bar->add_node( array(
'id' => 'customize',
'href' => $customize_url,
) );
}
}
/**
* Get current section of Permalink Manager admin panel
*/
public function get_current_section() {
global $active_section, $active_subsection, $current_admin_tax;
// 1. Get current section
if ( isset( $_GET['page'] ) && $_GET['page'] == PERMALINK_MANAGER_PLUGIN_SLUG ) {
if ( isset( $_POST['section'] ) ) {
$this->active_section = sanitize_title_with_dashes( $_POST['section'] );
} else if ( isset( $_GET['section'] ) ) {
$this->active_section = sanitize_title_with_dashes( $_GET['section'] );
} else {
$sections_names = array_keys( $this->sections );
$this->active_section = $sections_names[0];
}
}
// 2. Get current subsection
if ( $this->active_section && isset( $this->sections[ $this->active_section ]['subsections'] ) ) {
if ( isset( $_POST['subsection'] ) ) {
$this->active_subsection = sanitize_title_with_dashes( $_POST['subsection'] );
} else if ( isset( $_GET['subsection'] ) ) {
$this->active_subsection = sanitize_title_with_dashes( $_GET['subsection'] );
} else {
$subsections_names = array_keys( $this->sections[ $this->active_section ]['subsections'] );
$this->active_subsection = $subsections_names[0];
}
}
// 3. Check if current admin page is related to taxonomies
if ( ! empty( $this->active_subsection ) && substr( $this->active_subsection, 0, 4 ) == 'tax_' ) {
$current_admin_tax = substr( $this->active_subsection, 4, strlen( $this->active_subsection ) );
} else {
$current_admin_tax = false;
}
// Set globals
$active_section = $this->active_section;
$active_subsection = $this->active_subsection;
}
/**
* Add "Tools -> Permalink Manager" to the admin sidebar menu
*/
public function add_menu_page() {
add_management_page( __( 'Permalink Manager', 'permalink-manager' ), __( 'Permalink Manager', 'permalink-manager' ), 'manage_options', PERMALINK_MANAGER_PLUGIN_SLUG, array( $this, 'display_section' ) );
add_action( 'admin_init', array( $this, 'enqueue_cssjs' ) );
}
/**
* Display the plugin sections
*/
public function display_section() {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo Permalink_Manager_UI_Elements::get_plugin_sections_html( $this->sections, $this->active_section, $this->active_subsection );
}
/**
* Register the CSS & JS files for the plugin's dashboard
*/
public function enqueue_cssjs() {
wp_enqueue_style( 'permalink-manager-plugins', PERMALINK_MANAGER_URL . '/out/permalink-manager-plugins.css', array(), PERMALINK_MANAGER_VERSION );
wp_enqueue_style( 'permalink-manager', PERMALINK_MANAGER_URL . '/out/permalink-manager-admin.css', array( 'permalink-manager-plugins' ), PERMALINK_MANAGER_VERSION );
wp_enqueue_script( 'permalink-manager-plugins', PERMALINK_MANAGER_URL . '/out/permalink-manager-plugins.js', array( 'jquery', ), PERMALINK_MANAGER_VERSION, array( 'in_footer' => false ) );
wp_enqueue_script( 'permalink-manager', PERMALINK_MANAGER_URL . '/out/permalink-manager-admin.js', array( 'jquery', 'permalink-manager-plugins' ), PERMALINK_MANAGER_VERSION, array( 'in_footer' => false ) );
if ( isset( $_GET['section'] ) && $_GET['section'] === 'permastructs' ) {
wp_enqueue_script( 'thickbox' );
wp_enqueue_style( 'thickbox' );
}
wp_localize_script( 'permalink-manager', 'permalink_manager', array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'url' => PERMALINK_MANAGER_URL,
'confirm' => __( 'Are you sure? This action cannot be undone!', 'permalink-manager' ),
'spinners' => admin_url( 'images' )
) );
}
/**
* Get the URL of the plugin's dashboard
*
* @param string $append
*
* @return string
*/
public static function get_admin_url( $append = '' ) {
//return menu_page_url(PERMALINK_MANAGER_PLUGIN_SLUG, false) . $append;
$admin_page = sprintf( "tools.php?page=%s", PERMALINK_MANAGER_PLUGIN_SLUG . $append );
return admin_url( $admin_page );
}
/**
* Add shortcut links for Permalink Manager on "Plugins" page
*
* @param array $links
*
* @return array
*/
public function plugins_page_links( $links ) {
$new_links = array(
sprintf( ' %s ', $this->get_admin_url(), __( 'URI Editor', 'permalink-manager' ) ),
sprintf( '%s ', $this->get_admin_url( '§ion=settings' ), __( 'Settings', 'permalink-manager' ) ),
);
return array_merge( $links, $new_links );
}
/**
* Add shortcut meta links for Permalink Manager on "Plugins" page
*
* @param array $links
* @param string $file
*
* @return array
*/
public function plugins_page_meta( $links, $file ) {
if ( $file == PERMALINK_MANAGER_BASENAME ) {
$new_links = array(
'doc' => sprintf( '%s ', 'https://permalinkmanager.pro/docs/', __( 'Documentation', 'permalink-manager' ) )
);
if ( ! defined( 'PERMALINK_MANAGER_PRO' ) ) {
$new_links['upgrade'] = sprintf( '%s ', PERMALINK_MANAGER_PROMO, __( 'Buy Permalink Manager Pro', 'permalink-manager' ) );
}
$links = array_merge( $links, $new_links );
}
return $links;
}
/**
* Check if URI Editor should be displayed for current user
*
* @return bool
*/
public static function current_user_can_edit_uris() {
global $permalink_manager_options;
$edit_uris_cap = ( ! empty( $permalink_manager_options['general']['edit_uris_cap'] ) ) ? $permalink_manager_options['general']['edit_uris_cap'] : 'publish_posts';
return current_user_can( $edit_uris_cap );
}
/**
* Display global notices (throughout wp-admin dashboard)
*/
function display_global_notices() {
global $permalink_manager_alerts, $active_section;
$html = "";
if ( ! empty( $permalink_manager_alerts ) && is_array( $permalink_manager_alerts ) ) {
foreach ( $permalink_manager_alerts as $alert_id => $alert ) {
$dismissed_transient_name = sprintf( 'permalink-manager-notice_%s', sanitize_title( $alert_id ) );
$dismissed = get_transient( $dismissed_transient_name );
// Check if alert was dismissed
if ( empty( $dismissed ) ) {
// Hide notice in Permalink Manager Pro
if ( defined( 'PERMALINK_MANAGER_PRO' ) && $alert['show'] == 'pro_hide' ) {
continue;
}
// Display the notice only on the plugin pages
if ( empty( $active_section ) && ! empty( $alert['plugin_only'] ) ) {
continue;
}
// Check if the notice did not expire
if ( isset( $alert['until'] ) && ( time() > strtotime( $alert['until'] ) ) ) {
continue;
}
$html .= Permalink_Manager_UI_Elements::get_alert_message( $alert['txt'], $alert['type'], true, $alert_id );
}
}
}
echo wp_kses_post( $html );
}
/**
* Display notices generated by Permalink Manager tools
*/
function display_plugin_notices() {
global $permalink_manager_before_sections_html;
echo wp_kses_post( $permalink_manager_before_sections_html );
}
/**
* Get the list of all duplicated redirects and custom permalinks
*
* @param bool $include_custom_uris
*
* @return array
*/
public static function get_all_duplicates( $include_custom_uris = true ) {
global $permalink_manager_redirects;
// Make sure that both variables are arrays
$all_uris = ( $include_custom_uris ) ? Permalink_Manager_URI_Functions::get_all_uris() : array();
$permalink_manager_redirects = ( is_array( $permalink_manager_redirects ) ) ? $permalink_manager_redirects : array();
// Convert redirects list, so it can be merged with custom permalinks array
foreach ( $permalink_manager_redirects as $element_id => $redirects ) {
if ( is_array( $redirects ) ) {
foreach ( $redirects as $index => $uri ) {
$all_uris["redirect-{$index}_{$element_id}"] = $uri;
}
}
}
// Count duplicates
$duplicates_groups = array();
$duplicates_list = array_count_values( $all_uris );
$duplicates_list = array_filter( $duplicates_list, function ( $x ) {
return $x >= 2;
} );
// Assign keys to duplicates (group them)
if ( count( $duplicates_list ) > 0 ) {
foreach ( $duplicates_list as $duplicated_uri => $count ) {
$duplicated_ids = array_keys( $all_uris, $duplicated_uri );
// Ignore duplicates in different langauges
if ( Permalink_Manager_URI_Functions::is_uri_duplicated( $duplicated_uri, $duplicated_ids[0], $duplicated_ids ) ) {
$duplicates_groups[ $duplicated_uri ] = $duplicated_ids;
}
}
}
return $duplicates_groups;
}
/**
* Check if Permalink Manager Pro is active
*
* @return bool
*/
public static function is_pro_active() {
if ( defined( 'PERMALINK_MANAGER_PRO' ) && class_exists( 'Permalink_Manager_Pro_License' ) ) {
// Check if license is active
$exp_date = Permalink_Manager_Pro_License::get_expiration_date( true );
$is_pro = ( $exp_date > 2 ) ? false : true;
} else {
$is_pro = false;
}
return $is_pro;
}
}
integrations/permalink-manager-third-parties.php 0000644 00000127557 15220204736 0016154 0 ustar 00 query ) ) {
$query_vars = $wp_query->query;
// WordPress Photo Seller Plugin
if ( ! empty( $query_vars['image_id'] ) && ! empty( $query_vars['gallery_id'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // Ultimate Member
else if ( ! empty( $query_vars['um_user'] ) || ! empty( $query_vars['um_tab'] ) || ( ! empty( $query_vars['provider'] ) && ! empty( $query_vars['state'] ) ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // Mailster
else if ( ! empty( $query_vars['_mailster_page'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // WP Route
else if ( ! empty( $query_vars['WP_Route'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // WooCommerce Wishlist
else if ( ! empty( $query_vars['wishlist-action'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // UserPro
else if ( ! empty( $query_vars['up_username'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // The Events Calendar
else if ( ! empty( $query_vars['eventDisplay'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // Groundhogg
else if ( class_exists( '\Groundhogg\Plugin' ) && ! empty( $query_vars['subpage'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // MyListing theme
else if ( ! empty( $query_vars['explore_tab'] ) || ! empty( $query_vars['explore_region'] ) || ! empty( $_POST['submit_job'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // GeoDirectory
else if ( function_exists( 'geodir_location_page_id' ) && ! empty( $post->ID ) && geodir_location_page_id() == $post->ID ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // RankMath Pro
else if ( isset( $query_vars['schema-preview'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // Theme.co - Pro Theme
else if ( ! empty( $_POST['_cs_nonce'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // Tutor LMS
else if ( ! empty( $query_vars['tutor_dashboard_page'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
} // AMP
else if ( function_exists( 'amp_get_slug' ) && array_key_exists( amp_get_slug(), $query_vars ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
}
// LearnPress
if ( ! empty( $query_vars['view'] ) && ! empty( $query_vars['page_id'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
}
}
}
/**
* AMP hooks (support for older versions)
*
* @param array $uri_parts
* @param string $request_url
*
* @return array
*/
function detect_amp( $uri_parts, $request_url ) {
global $amp_enabled;
if ( defined( 'AMP_QUERY_VAR' ) ) {
$amp_query_var = AMP_QUERY_VAR;
// Check if AMP should be triggered
preg_match( "/^(.+?)\/({$amp_query_var})?\/?$/i", $uri_parts['uri'], $regex_parts );
if ( ! empty( $regex_parts[2] ) ) {
$uri_parts['uri'] = $regex_parts[1];
$amp_enabled = true;
}
}
return $uri_parts;
}
/**
* AMP hooks
*
* @param array $query
*
* @return array
*/
function enable_amp( $query ) {
global $amp_enabled;
if ( ! empty( $amp_enabled ) && defined( 'AMP_QUERY_VAR' ) ) {
$query[ AMP_QUERY_VAR ] = 1;
}
return $query;
}
/**
* AMP for WP hooks (support for older versions)
*
* @param array $query
*
* @return array
*/
function detect_amp_for_wp( $query ) {
global $wp_rewrite, $pm_query;
if ( defined( 'AMPFORWP_AMP_QUERY_VAR' ) ) {
$amp_endpoint = AMPFORWP_AMP_QUERY_VAR;
$paged_endpoint = $wp_rewrite->pagination_base;
if ( ! empty( $pm_query['endpoint'] ) && strpos( $pm_query['endpoint_value'], "{$paged_endpoint}/" ) !== false ) {
$paged_val = preg_replace( "/({$paged_endpoint}\/)([\d]+)/", '$2', $pm_query['endpoint_value'] );
if ( ! empty( $paged_val ) ) {
$query[ $amp_endpoint ] = 1;
$query['paged'] = $paged_val;
}
}
}
return $query;
}
/**
* Parse Custom Permalinks import
*/
public static function custom_permalinks_uris() {
global $wpdb;
$custom_permalinks_uris = array();
// List tags/categories
$table = get_option( 'custom_permalink_table' );
if ( $table && is_array( $table ) ) {
foreach ( $table as $permalink => $info ) {
$custom_permalinks_uris[] = array(
'id' => "tax-" . $info['id'],
'uri' => trim( $permalink, "/" )
);
}
}
// List posts/pages
$query = "SELECT p.ID, m.meta_value FROM $wpdb->posts AS p LEFT JOIN $wpdb->postmeta AS m ON (p.ID = m.post_id) WHERE m.meta_key = 'custom_permalink' AND m.meta_value != '';";
$posts = $wpdb->get_results( $query );
foreach ( $posts as $post ) {
$custom_permalinks_uris[] = array(
'id' => $post->ID,
'uri' => trim( $post->meta_value, "/" ),
);
}
return $custom_permalinks_uris;
}
/**
* Import the URIs from the Custom Permalinks plugin.
*/
static public function import_custom_permalinks_uris() {
global $permalink_manager_before_sections_html;
$custom_permalinks_plugin = 'custom-permalinks/custom-permalinks.php';
if ( is_plugin_active( $custom_permalinks_plugin ) && ! empty( $_POST['disable_custom_permalinks'] ) ) {
deactivate_plugins( $custom_permalinks_plugin );
}
// Get a list of imported URIs
$custom_permalinks_uris = self::custom_permalinks_uris();
if ( ! empty( $custom_permalinks_uris ) && count( $custom_permalinks_uris ) > 0 ) {
foreach ( $custom_permalinks_uris as $item ) {
$item_uri = $item['uri'];
// Decode custom permalink if contains percent-encoded characters
if ( preg_match( '/%[0-9A-F]{2}/i', $item_uri ) ) {
$item_uri = urldecode( $item_uri );
}
Permalink_Manager_URI_Functions::save_single_uri( $item['id'], $item_uri, false, false );
}
$permalink_manager_before_sections_html .= Permalink_Manager_UI_Elements::get_alert_message( __( '"Custom Permalinks" URIs were imported!', 'permalink-manager' ), 'updated' );
Permalink_Manager_URI_Functions::save_all_uris();
} else {
$permalink_manager_before_sections_html .= Permalink_Manager_UI_Elements::get_alert_message( __( 'No "Custom Permalinks" URIs were imported!', 'permalink-manager' ), 'error' );
}
}
/**
* Do not use custom permalinks if any action is triggered inside Theme My Login plugin
*/
function tml_ignore_custom_permalinks() {
global $wp, $permalink_manager_ignore_permalink_filters;
if ( isset( $wp->query_vars['action'] ) || ! empty( $_GET['redirect_to'] ) ) {
$permalink_manager_ignore_permalink_filters = true;
// Allow the canonical redirect (if blocked earlier by Permalink Manager)
if ( ! empty( $wp_query->query_vars['do_not_redirect'] ) ) {
$wp_query->query_vars['do_not_redirect'] = 0;
}
}
}
/**
* Copy the custom URI from original post and apply it to the new temp. revision post (Revisionize)
*
* @param int $old_id
* @param int $new_id
*/
function revisionize_keep_post_uri( $old_id, $new_id ) {
$old_uri = Permalink_Manager_URI_Functions::get_single_uri( $old_id, false, true );
if ( ! empty( $old_uri ) ) {
Permalink_Manager_URI_Functions::save_single_uri( $new_id, $old_uri, false, true );
}
}
/**
* Copy the custom URI from revision post and apply it to the original post
*
* @param int $old_id
* @param int $new_id
*/
function revisionize_clone_uri( $old_id, $new_id ) {
$new_uri = Permalink_Manager_URI_Functions::get_single_uri( $new_id, false, true );
if ( ! empty( $new_uri ) ) {
Permalink_Manager_URI_Functions::save_single_uri( $old_id, $new_uri, false, true );
Permalink_Manager_URI_Functions::remove_single_uri( $new_id, false, true );
}
}
/**
* Add a new section to the WP All Import interface
*
* @param string $content_type The type of content being imported
* @param array $current_values An array of the current values for the post
*/
function wpaiextra_uri_display( $content_type, $current_values ) {
// Check if post type is supported
if ( $content_type !== 'taxonomies' && Permalink_Manager_Helper_Functions::is_post_type_disabled( $content_type ) ) {
return;
} else if ( $content_type == 'taxonomies' && ( ! class_exists( 'Permalink_Manager_URI_Functions_Tax' ) || empty( $current_values['taxonomy_type'] ) || Permalink_Manager_Helper_Functions::is_taxonomy_disabled( $current_values['taxonomy_type'] ) ) ) {
return;
}
// Get custom URI format
$custom_uri = ( ! empty( $current_values['custom_uri'] ) ) ? sanitize_text_field( $current_values['custom_uri'] ) : "";
$html = '';
$html .= '
';
$html .= sprintf( '', __( 'Permalink Manager', 'permalink-manager' ) );
$html .= '
';
$html .= '
';
$html .= '
';
$html .= '
';
$html .= '
';
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $html;
}
/**
* Add a new field to the list of WP All Import options
*
* @param array $all_options The array of all options that are currently set
*
* @return array The array of all options plus the custom_uri option
*/
function wpai_api_options( $all_options ) {
return $all_options + array( 'custom_uri' => null );
}
/**
* Allow to choose if the custom permalink should be updated during import
*
* @param $post_type
* @param $post
*/
function wpai_toggle_import( $post_type, $post ) {
if ( Permalink_Manager_Helper_Functions::is_post_type_disabled( $post_type ) ) {
return;
}
$default_value = ( ! isset( $post['is_update_custom_uri'] ) ) ? 1 : $post['is_update_custom_uri'];
$html = ' ';
$html .= sprintf( ' ', checked( 1, $default_value, false ) );
$html .= sprintf( '%s (%s) ', esc_html__( 'Custom permalink', 'permalink-manager' ), esc_html__( 'Permalink Manager', 'permalink-manager' ) );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo sprintf( '%s
', $html );
}
/**
* Save the "Choose which data to update" settings
*
* @param $post
*
* @return mixed
*/
function wpai_save_toggle_import( $post ) {
if ( isset( $_POST['is_update_custom_uri'] ) && is_numeric( $_POST['is_update_custom_uri'] ) ) {
$post['is_update_custom_uri'] = filter_var( $_POST['is_update_custom_uri'], FILTER_SANITIZE_NUMBER_INT );
}
return $post;
}
/**
* Add Permalink Manager plugin to the WP All Import API
*
* @param array $addons
*
* @return array
*/
function wpai_api_register( $addons ) {
if ( empty( $addons[ PERMALINK_MANAGER_PLUGIN_SLUG ] ) ) {
$addons[ PERMALINK_MANAGER_PLUGIN_SLUG ] = 1;
}
return $addons;
}
/**
* Register function that parses Permalink Manager plugin data in WP All Import API data feed
*
* @param array $functions
*
* @return array
*/
function wpai_api_parse( $functions ) {
$functions[ PERMALINK_MANAGER_PLUGIN_SLUG ] = array( $this, 'wpai_api_parse_function' );
return $functions;
}
/**
* Register function that saves Permalink Manager plugin data extracted from WP All Import API data feed
*
* @param array $functions
*
* @return array
*/
function wpai_api_import( $functions ) {
$functions[ PERMALINK_MANAGER_PLUGIN_SLUG ] = array( $this, 'wpai_api_import_function' );
return $functions;
}
/**
* Parse Permalink Manager plugin data in WP All Import API data feed
*
* @param array $data
*
* @return array
* @throws XmlImportException
*/
function wpai_api_parse_function( $data ) {
extract( $data );
$data = array(); // parsed data
$option_name = 'custom_uri';
// Check if the custom permalinks data should be imported
if ( isset( $import->options['is_update_custom_uri'] ) && $import->options['is_update_custom_uri'] == 0 ) {
return $data;
}
if ( ! empty( $import->options[ $option_name ] ) && class_exists( 'XmlImportParser' ) ) {
$cxpath = $xpath_prefix . $import->xpath;
$tmp_files = array();
if ( isset( $import->options[ $option_name ] ) && $import->options[ $option_name ] != '' ) {
if ( $import->options[ $option_name ] == "xpath" ) {
$data[ $option_name ] = XmlImportParser::factory( $xml, $cxpath, (string) $import->options['xpaths'][ $option_name ], $file )->parse();
} else {
$data[ $option_name ] = XmlImportParser::factory( $xml, $cxpath, (string) $import->options[ $option_name ], $file )->parse();
}
$tmp_files[] = $file;
} else {
$data[ $option_name ] = array_fill( 0, $count, "" );
}
foreach ( $tmp_files as $file ) {
wp_delete_file( $file );
}
}
return $data;
}
/**
* Save the Permalink Manager plugin data extracted from WP All Import API data feed
*
* @param array $importData
* @param array $parsedData
*/
function wpai_api_import_function( $importData, $parsedData ) {
// Check if the array with $parsedData is not empty
if ( empty( $parsedData ) || empty( $importData['post_type'] ) ) {
return;
}
// Check if the imported elements are terms
if ( $importData['post_type'] == 'taxonomies' ) {
$is_term = true;
if ( ! class_exists( 'Permalink_Manager_URI_Functions_Tax' ) ) {
return;
}
} else if ( Permalink_Manager_Helper_Functions::is_post_type_disabled( $importData['post_type'] ) ) {
return;
}
// Get the parsed custom URI
$index = ( isset( $importData['i'] ) ) ? $importData['i'] : false;
$pid = ( ! empty( $importData['pid'] ) ) ? (int) $importData['pid'] : false;
if ( isset( $index ) && ! empty( $pid ) && ! empty( $parsedData['custom_uri'][ $index ] ) ) {
$new_uri = Permalink_Manager_Helper_Functions::sanitize_title( $parsedData['custom_uri'][ $index ] );
if ( ! empty( $new_uri ) ) {
if ( ! empty( $is_term ) ) {
if ( ! class_exists( 'Permalink_Manager_URI_Functions_Tax' ) ) {
return;
}
$default_uri = Permalink_Manager_URI_Functions_Tax::get_default_term_uri( $pid );
$native_uri = Permalink_Manager_URI_Functions_Tax::get_default_term_uri( $pid, true );
$custom_uri = Permalink_Manager_URI_Functions_Tax::get_term_uri( $pid, false, true );
$old_uri = ( ! empty( $custom_uri ) ) ? $custom_uri : $native_uri;
if ( $new_uri !== $old_uri ) {
Permalink_Manager_URI_Functions::save_single_uri( $pid, $new_uri, true, true );
do_action( 'permalink_manager_updated_term_uri', $pid, $new_uri, $old_uri, $native_uri, $default_uri, $uri_saved = true );
}
} else {
$default_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri( $pid );
$native_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri( $pid, true );
$custom_uri = Permalink_Manager_URI_Functions_Post::get_post_uri( $pid, false, true );
$old_uri = ( ! empty( $custom_uri ) ) ? $custom_uri : $native_uri;
if ( $new_uri !== $old_uri ) {
Permalink_Manager_URI_Functions::save_single_uri( $pid, $new_uri, false, true );
do_action( 'permalink_manager_updated_post_uri', $pid, $new_uri, $old_uri, $native_uri, $default_uri, $uri_saved = true );
}
}
}
}
}
/**
* Copy the external redirect from the "external_redirect" custom field to the data model used by Permalink Manager Pro
*
* @param int $pid The post ID of the post being imported.
*/
function wpai_save_redirects( $pid ) {
$external_url = get_post_meta( $pid, '_external_redirect', true );
$external_url = ( empty( $external_url ) ) ? get_post_meta( $pid, 'external_redirect', true ) : $external_url;
if ( $external_url && class_exists( 'Permalink_Manager_Pro_Functions' ) ) {
Permalink_Manager_Pro_Functions::save_external_redirect( $external_url, $pid );
}
}
/**
* Use the import ID to extract all the post IDs that were imported, then splits them into chunks and schedule a single regenerate permalink event for each chunk
*
* @param int $import_id The ID of the import.
*/
function wpai_bulk_regenerate_uris_after_xml_import( $import_id ) {
global $wpdb;
$post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT post_id FROM {$wpdb->prefix}pmxi_posts WHERE import_id = %d", $import_id ) );
$chunk_size = 200;
if ( count( $post_ids ) <= $chunk_size ) {
$this->wpai_regenerate_uris_after_import( $post_ids );
} else {
// Schedule URI regenerate and split into bulks to prevent timeout
$chunks = array_chunk( $post_ids, $chunk_size );
foreach ( $chunks as $i => $chunk ) {
wp_schedule_single_event( time() + ( $i * 30 ), 'wpai_regenerate_uris_after_import_event', array( $chunk ) );
}
}
}
/**
* Regenerate the custom permalinks for all imported posts
*
* @param array $post_ids An array of post IDs that were just imported.
*/
function wpai_regenerate_uris_after_import( $post_ids ) {
if ( ! is_array( $post_ids ) ) {
return;
}
foreach ( $post_ids as $id ) {
$this->wpai_regenerate_single_uri_after_import( $id, false );
}
Permalink_Manager_URI_Functions::save_all_uris();
}
/**
* Regenerate the single custom permalink
*
* @param int $post_id
* @param bool $db_save
*/
function wpai_regenerate_single_uri_after_import( $post_id, $db_save = true ) {
$current_uri = Permalink_Manager_URI_Functions::get_single_uri( $post_id, false, true, false );
if ( ! empty( $current_uri ) ) {
return;
}
Permalink_Manager_URI_Functions_Post::save_uri( $post_id, '', true, 0, $db_save );
}
/**
* Add a new section to the WP All Export interface
*
* @param array $sections
*
* @return array
*/
function wpae_custom_uri_section( $sections ) {
if ( is_array( $sections ) ) {
$sections['permalink_manager'] = array(
'title' => __( 'Permalink Manager', 'permalink-manager' ),
'content' => 'permalink_manager_fields'
);
}
return $sections;
}
/**
* Add a new field to the "Permalink Manager" section of the WP All Export interface
*
* @param array $fields The fields to be displayed in the section.
*
* @return array
*/
function wpae_custom_uri_section_fields( $fields ) {
if ( is_array( $fields ) ) {
$fields['permalink_manager_fields'] = array(
array(
'label' => 'custom_uri',
'name' => 'Custom URI',
'type' => 'custom_uri'
)
);
}
return $fields;
}
/**
* Add a new column to the export file with the custom permalink for each post/term
*
* @param array $articles The array of articles to be exported.
* @param array $options an array of options for the export.
*
* @return array
*/
function wpae_export_custom_uri( $articles, $options ) {
if ( ( ! empty( $options['selected_post_type'] ) && $options['selected_post_type'] == 'taxonomies' ) || ! empty( $options['is_taxonomy_export'] ) ) {
$is_term = true;
} else {
$is_term = false;
}
if ( $is_term && ! class_exists( 'Permalink_Manager_URI_Functions_Tax' ) ) {
return $articles;
}
foreach ( $articles as &$article ) {
if ( ! empty( $article['id'] ) ) {
$item_id = $article['id'];
} else if ( ! empty( $article['ID'] ) ) {
$item_id = $article['ID'];
} else if ( ! empty( $article['Term ID'] ) ) {
$item_id = $article['Term ID'];
} else {
continue;
}
if ( ! empty( $is_term ) ) {
$article['Custom URI'] = Permalink_Manager_URI_Functions_Tax::get_term_uri( $item_id );
} else {
$article['Custom URI'] = Permalink_Manager_URI_Functions_Post::get_post_uri( $item_id );
}
}
return $articles;
}
/**
* Check if the "custom_uri" is not blacklisted in the "duplicate_post_blacklist" option and if it's not, clone the custom permalink of the original post to the new one
*
* @param int $new_post_id The ID of the newly created post.
* @param WP_Post $old_post The post object of the original post.
*/
function duplicate_custom_uri( $new_post_id, $old_post ) {
$duplicate_post_blacklist = get_option( 'duplicate_post_blacklist', false );
$duplicate_custom_uri_bool = ( ! empty( $duplicate_post_blacklist ) && strpos( $duplicate_post_blacklist, 'custom_uri' ) !== false ) ? false : true;
if ( ! empty( $old_post->ID ) && $duplicate_custom_uri_bool ) {
$old_post_uri = Permalink_Manager_URI_Functions::get_single_uri( $old_post->ID, false, true, false );
// Clone custom permalink (if set for cloned post/page)
if ( ! empty( $old_post_uri ) ) {
$new_post_uri = preg_replace( '/(.+?)(\.[^\.]+$|$)/', '$1-2$2', $old_post_uri );
Permalink_Manager_URI_Functions::save_single_uri( $new_post_id, $new_post_uri, false, true );
}
}
}
/**
* Replace in the default permalink format the unique permastructure tags available for My Listing theme
*
* @param string $default_uri The default permalink for the element.
* @param string $native_slug The native slug of the post type.
* @param WP_Post $element The post object.
* @param string $slug The slug of the post type.
* @param bool $native_uri
*
* @return string
*/
public function ml_listing_custom_fields( $default_uri, $native_slug, $element, $slug, $native_uri ) {
// Use only for "listing" post type & custom permalink
if ( empty( $element->post_type ) || $element->post_type !== 'job_listing' || ! class_exists( 'MyListing\Src\Listing' ) ) {
return $default_uri;
}
// Get the listing object
$listing_object = MyListing\Src\Listing::get( $element );
// A1. Listing type
if ( strpos( $default_uri, '%listing-type%' ) !== false || strpos( $default_uri, '%listing_type%' ) !== false ) {
$listing_type = ( is_object( $listing_object ) && ! empty( $listing_object->type ) ) ? $listing_object->type->get_permalink_name() : '';
if ( ! empty( $listing_type ) ) {
$default_uri = str_replace( array( '%listing-type%', '%listing_type%' ), Permalink_Manager_Helper_Functions::sanitize_title( $listing_type, true ), $default_uri );
}
}
// A2. Listing type (slug)
if ( strpos( $default_uri, '%listing-type-slug%' ) !== false || strpos( $default_uri, '%listing_type_slug%' ) !== false || strpos( $default_uri, '%case27_listing_type%' ) !== false ) {
$listing_type = get_post_meta( $element->ID, '_case27_listing_type', true );
if ( ! empty( $listing_type ) ) {
$listing_type = Permalink_Manager_Helper_Functions::sanitize_title( $listing_type, true );
$default_uri = str_replace( array( '%listing-type-slug%', '%listing_type_slug%', '%case27_listing_type%' ), $listing_type, $default_uri );
}
}
// B. Listing location
if ( strpos( $default_uri, '%listing-location%' ) !== false || strpos( $default_uri, '%listing_location%' ) !== false ) {
$listing_location = get_post_meta( $element->ID, '_job_location', true );
if ( ! empty( $listing_location ) ) {
$listing_location = Permalink_Manager_Helper_Functions::sanitize_title( $listing_location, true );
$default_uri = str_replace( array( '%listing-location%', '%listing_location%' ), $listing_location, $default_uri );
}
}
// C. Listing region
if ( strpos( $default_uri, '%listing-region%' ) !== false || strpos( $default_uri, '%listing_region%' ) !== false ) {
$listing_region_terms = wp_get_object_terms( $element->ID, 'region' );
if ( is_object( $listing_object ) && method_exists( $listing_object, 'get_field' ) ) {
$listing_regions = $listing_object->get_field( 'region' );
$listing_region_term = $listing_regions[0];
} else {
$listing_region_term = ( ! is_wp_error( $listing_region_terms ) && ! empty( $listing_region_terms ) && is_object( $listing_region_terms[0] ) ) ? Permalink_Manager_Helper_Functions::get_lowest_element( $listing_region_terms[0], $listing_region_terms ) : "";
}
if ( ! empty( $listing_region_term ) && is_a( $listing_region_term, 'WP_Term' ) ) {
$listing_region = Permalink_Manager_Helper_Functions::get_term_full_slug( $listing_region_term, $listing_region_terms, 2 );
$listing_region = Permalink_Manager_Helper_Functions::sanitize_title( $listing_region, true );
} else {
$listing_region = '';
}
$default_uri = str_replace( array( '%listing-region%', '%listing_region%' ), $listing_region, $default_uri );
}
// D. Listing category
if ( strpos( $default_uri, '%listing-category%' ) !== false || strpos( $default_uri, '%listing_category%' ) !== false ) {
$listing_category_terms = wp_get_object_terms( $element->ID, 'job_listing_category' );
if ( is_object( $listing_object ) && method_exists( $listing_object, 'get_field' ) ) {
$listing_categories = $listing_object->get_field( 'category' );
$listing_category_term = $listing_categories[0];
} else {
$listing_category_term = ( ! is_wp_error( $listing_category_terms ) && ! empty( $listing_category_terms ) && is_object( $listing_category_terms[0] ) ) ? Permalink_Manager_Helper_Functions::get_lowest_element( $listing_category_terms[0], $listing_category_terms ) : "";
}
if ( ! empty( $listing_category_term ) && is_a( $listing_category_term, 'WP_Term' ) ) {
$listing_category = Permalink_Manager_Helper_Functions::get_term_full_slug( $listing_category_term, $listing_category_terms, 2 );
$listing_category = Permalink_Manager_Helper_Functions::sanitize_title( $listing_category, true );
} else {
$listing_category = '';
}
$default_uri = str_replace( array( '%listing-category%', '%listing_category%' ), $listing_category, $default_uri );
}
return $default_uri;
}
/**
* Make sure that the custom permalink is generated only after the listing's field are processed
*
* @param array $data
*
* @return array
*/
function ml_delay_set_listing_uri( $data ) {
add_filter( 'permalink_manager_allow_new_post_uri', '__return_false' );
return $data;
}
/**
* Set the default custom permalink for the listing item when it is created
*
* @param int $post_id
*/
function ml_set_listing_uri( $post_id ) {
$custom_uri = Permalink_Manager_URI_Functions_Post::get_post_uri( $post_id, false, true );
if ( empty( $custom_uri ) ) {
add_filter( 'permalink_manager_allow_new_post_uri', '__return_true' );
Permalink_Manager_URI_Functions_Post::save_uri( $post_id, '', true, 0 );
}
}
/**
* If the user is on a MyListing archive page submitting a job, redirect the user to the Explore Listings page
*
* @param array $query The query object.
* @param array $old_query The original query array.
*
* @return array The new query array is being returned if the explore_tab property is present. Otherwise, the original query is returned.
*/
function ml_detect_archives( $query, $old_query ) {
if ( function_exists( 'mylisting_custom_taxonomies' ) && empty( $_POST['submit_job'] ) ) {
$explore_page_id = get_option( 'options_general_explore_listings_page', false );
if ( empty( $explore_page_id ) ) {
return $query;
}
// Set-up new query array variable
$new_query = array(
"page_id" => $explore_page_id
);
// Check if any custom MyListing taxonomy was detected
$ml_taxonomies = mylisting_custom_taxonomies();
if ( ! empty( $ml_taxonomies ) && is_array( $ml_taxonomies ) ) {
$ml_taxonomies = array_keys( $ml_taxonomies );
foreach ( $ml_taxonomies as $taxonomy ) {
if ( ! empty( $query[ $taxonomy ] ) && ! empty( $query['term'] ) && empty( $_GET[ $taxonomy ] ) ) {
$new_query["explore_tab"] = $taxonomy;
$new_query["explore_{$taxonomy}"] = $query['term'];
}
}
}
// Check if any MyListing query var was detected
$ml_query_vars = array(
'explore_tag' => 'tags',
'explore_region' => 'regions',
'explore_category' => 'categories'
);
foreach ( $ml_query_vars as $query_var => $explore_tab ) {
if ( ! empty( $old_query[ $query_var ] ) && empty( $_GET[ $query_var ] ) ) {
$new_query[ $query_var ] = $old_query[ $query_var ];
$new_query["explore_tab"] = $explore_tab;
}
}
}
return ( ! empty( $new_query["explore_tab"] ) ) ? $new_query : $query;
}
/**
* Add the bbPress endpoints to the list of endpoints that are supported by the plugin
*
* @param string $endpoints
* @param bool $all Whether to return all endpoints or just the bbPress ones
*
* @return string|array
*/
function bbpress_endpoints( $endpoints, $all = true ) {
$bbpress_endpoints = array();
$bbpress_endpoints[] = bbp_get_edit_slug();
return ( $all ) ? $endpoints . "|" . implode( "|", $bbpress_endpoints ) : $bbpress_endpoints;
}
/**
* If the query contains the edit endpoint, then set the appropriate bbPress query variable
*/
function bbpress_detect_endpoints() {
global $wp_query;
if ( ! empty( $wp_query->query ) ) {
$edit_endpoint = bbp_get_edit_slug();
if ( isset( $wp_query->query[ $edit_endpoint ] ) ) {
if ( isset( $wp_query->query['forum'] ) ) {
$wp_query->bbp_is_forum_edit = true;
} else if ( isset( $wp_query->query['topic'] ) ) {
$wp_query->bbp_is_topic_edit = true;
} else if ( isset( $wp_query->query['reply'] ) ) {
$wp_query->bbp_is_reply_edit = true;
}
}
}
}
/**
* Add the endpoint "edit" used by Dokan to the endpoints array supported by Permalink Manager
*
* @param string $endpoints
*
* @return string
*/
function dokan_endpoints( $endpoints ) {
return "{$endpoints}|edit|edit-account";
}
/**
* Check if the current page is a Dokan page, and if so, adjust the query variables to disable the canonical redirect
*/
function dokan_detect_endpoints() {
global $post, $wp_query, $wp, $pm_query;
// Check if Dokan is activated
if ( ! function_exists( 'dokan_get_option' ) || is_admin() || empty( $pm_query['id'] ) ) {
return;
}
// Get Dokan dashboard page id
$dashboard_page = dokan_get_option( 'dashboard', 'dokan_pages' );
// Stop the redirect
if ( ! empty( $dashboard_page ) && ! empty( $post->ID ) && ( $post->ID == $dashboard_page ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
// Detect Dokan shortcode
if ( empty( $pm_query['endpoint'] ) ) {
$wp->query_vars['page'] = 1;
} else if ( isset( $wp->query_vars['page'] ) ) {
unset( $wp->query_vars['page'] );
}
}
// Support "Edit Product" pages
if ( isset( $wp_query->query_vars['edit'] ) ) {
$wp_query->query_vars['edit'] = 1;
$wp_query->query_vars['do_not_redirect'] = 1;
}
}
/**
* Replace in the default permalink format the unique permastructure tags available for GeoDirectory plugin
*
* @param string $default_uri The default permalink for the element.
* @param string $native_slug The native slug of the post type.
* @param WP_Post $element The post object.
* @param string $slug The slug of the post type.
* @param bool $native_uri
*
* @return string
*/
public function geodir_custom_fields( $default_uri, $native_slug, $element, $slug, $native_uri ) {
// Use only for GeoDirectory post types & custom permalinks
if ( empty( $element->post_type ) || ( strpos( $element->post_type, 'gd_' ) === false ) || $native_uri || ! function_exists( 'geodir_get_post_info' ) ) {
return $default_uri;
}
// Get place info
$place_data = geodir_get_post_info( $element->ID );
// A. Category
if ( strpos( $default_uri, '%category%' ) !== false ) {
$place_category_terms = wp_get_object_terms( $element->ID, 'gd_placecategory' );
$place_category_term = ( ! is_wp_error( $place_category_terms ) && ! empty( $place_category_terms ) && is_object( $place_category_terms[0] ) ) ? Permalink_Manager_Helper_Functions::get_lowest_element( $place_category_terms[0], $place_category_terms ) : "";
if ( ! empty( $place_category_term ) && is_a( $place_category_term, 'WP_Term' ) ) {
$place_category = Permalink_Manager_Helper_Functions::get_term_full_slug( $place_category_term, '', 2 );
$place_category = Permalink_Manager_Helper_Functions::sanitize_title( $place_category, true );
$default_uri = str_replace( '%category%', $place_category, $default_uri );
}
}
// B. Country
if ( strpos( $default_uri, '%country%' ) !== false && ! empty( $place_data->country ) ) {
$place_country = Permalink_Manager_Helper_Functions::sanitize_title( $place_data->country, true );
$default_uri = str_replace( '%country%', $place_country, $default_uri );
}
// C. Region
if ( strpos( $default_uri, '%region%' ) !== false && ! empty( $place_data->region ) ) {
$place_region = Permalink_Manager_Helper_Functions::sanitize_title( $place_data->region, true );
$default_uri = str_replace( '%region%', $place_region, $default_uri );
}
// D. City
if ( strpos( $default_uri, '%city%' ) !== false && ! empty( $place_data->city ) ) {
$place_city = Permalink_Manager_Helper_Functions::sanitize_title( $place_data->city, true );
$default_uri = str_replace( '%city%', $place_city, $default_uri );
}
return $default_uri;
}
/**
* Adjust the query if BasePress page is detected
*
* @param array $query The query object.
* @param array $old_query The original query array.
* @param array $uri_parts An array of the URI parts.
* @param array $pm_query
* @param string $content_type
*
* @return array
*/
function kb_adjust_query( $query, $old_query, $uri_parts, $pm_query, $content_type ) {
$knowledgebase_options = get_option( 'basepress_settings' );
$knowledgebase_page = ( ! empty( $knowledgebase_options['entry_page'] ) ) ? $knowledgebase_options['entry_page'] : '';
// A. Knowledgebase category
if ( isset( $query['knowledgebase_cat'] ) && ! empty( $pm_query['id'] ) ) {
$kb_category = $query['knowledgebase_cat'];
$query = array(
'post_type' => 'knowledgebase',
'knowledgebase_items' => $kb_category
);
// Disable the canonical redirect function included in BasePress
add_filter( 'basepress_canonical_redirect', '__return_false' );
} // B. Knowledgebase main page
else if ( ! empty( $knowledgebase_page ) && ! empty( $pm_query['id'] ) && $pm_query['id'] == $knowledgebase_page ) {
$query = array(
'page_id' => $knowledgebase_page
);
}
return $query;
}
/**
* Detect the extra pages created by Ultimate Member plugin
*
* @param array $uri_parts
*
* @return array
*/
public function um_detect_extra_pages( $uri_parts ) {
if ( ! function_exists( 'UM' ) ) {
return $uri_parts;
}
$request_url = trim( "{$uri_parts['uri']}/{$uri_parts['endpoint_value']}", "/" );
$um_pages = array(
'user' => 'um_user',
'account' => 'um_tab',
);
// Detect UM permalinks
foreach ( $um_pages as $um_page => $query_var ) {
$um_page_id = UM()->config()->permalinks[ $um_page ];
// Support for WPML/Polylang
$um_page_id = ( ! empty( $uri_parts['lang'] ) ) ? apply_filters( 'wpml_object_id', $um_page_id, 'page', true, $uri_parts['lang'] ) : $um_page_id;
$um_page_uri = ( ! empty( $um_page_id ) ) ? Permalink_Manager_URI_Functions::get_single_uri( $um_page_id, false, true, false ) : '';
if ( ! empty( $um_page_id ) && ! empty( $um_page_uri ) ) {
$user_page_uri = preg_quote( $um_page_uri, '/' );
preg_match( "/^({$user_page_uri})\/([^\/]+)?$/", $request_url, $parts );
if ( ! empty( $parts[2] ) ) {
$uri_parts['uri'] = $parts[1];
$uri_parts['endpoint'] = $query_var;
$uri_parts['endpoint_value'] = Permalink_Manager_Helper_Functions::sanitize_title( $parts[2], null, null, false );
}
}
}
return $uri_parts;
}
/**
* Excluding the LearnPress pages from Permalink Manager
*
* @param array $excluded_ids
*
* @return array
*/
function learnpress_exclude_pages( $excluded_ids ) {
if ( is_array( $excluded_ids ) && function_exists( 'learn_press_get_page_id' ) ) {
$learnpress_pages = array( 'profile', 'courses', 'checkout', 'become_a_teacher' );
foreach ( $learnpress_pages as $page ) {
$learnpress_page_id = learn_press_get_page_id( $page );
if ( empty( $learnpress_page_id ) || in_array( $learnpress_page_id, $excluded_ids ) ) {
continue;
}
$excluded_ids[] = $learnpress_page_id;
}
}
return $excluded_ids;
}
/**
* Regenerate the default permalink for the post after the custom permalink is imported by Store Locator - CSV Manager
*
* @param int $meta_id The ID of the metadata entry.
* @param int $post_id The ID of the post that the metadata is for.
* @param string $meta_key The meta key of the metadata being updated.
* @param mixed $meta_value The value of the meta key.
*/
public function wpsl_regenerate_after_import( $meta_id, $post_id, $meta_key, $meta_value ) {
if ( strpos( $meta_key, 'wpsl_' ) !== false && isset( $_POST['wpsl_csv_import_nonce'] ) ) {
$default_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri( $post_id );
if ( $default_uri ) {
Permalink_Manager_URI_Functions::save_single_uri( $post_id, $default_uri, false, true );
}
}
}
/**
* Support custom permalinks in query functions used by Google Site Kit plugin
*
* @param $request
*
* @return array
*/
function googlesitekit_fix_request( $request ) {
if ( ! empty( $_GET['permaLink'] ) && ! empty( $_GET['page'] ) && $_GET['page'] === 'googlesitekit-dashboard' ) {
global $pm_query;
$old_url = trim( esc_url_raw( $_GET['permaLink'] ), '/' );
$new_query = Permalink_Manager_Core_Functions::detect_post( array(), $old_url );
if ( ! empty( $new_query ) && ! empty( $pm_query['id'] ) ) {
$request = $new_query;
}
}
return $request;
}
/**
* Adjust the query if WPForo page is detected
*
* @param array $query The query object.
* @param array $old_query The original query array.
* @param array $uri_parts An array of the URI parts.
* @param array $pm_query
* @param string $content_type
* @param null|WP_Post|WP_Term $element_object
*
* @return array
*/
function wpforo_adjust_query( $query, $old_query, $uri_parts, $pm_query, $content_type, $element_object ) {
if ( ! function_exists( 'wpforo_get_option' ) || ! function_exists( 'WPF' ) ) {
return $query;
}
$boards = WPF()->board->get_boards_pageids();
$boards[] = wpforo_get_option( 'wpforo_pageid2', 0 );
if ( ! empty( $element_object->ID ) ) {
$detected_page_id = $element_object->ID;
} else if ( ! empty( $old_query['page_id'] ) ) {
$detected_page_id = $old_query['page_id'];
}
if ( ! empty( $boards ) && ! empty( $detected_page_id ) && in_array( $detected_page_id, $boards ) ) {
$query['do_not_redirect'] = 1;
}
return $query;
}
}
integrations/permalink-manager-language-plugins.php 0000644 00000134460 15220204736 0016626 0 ustar 00 links_model->options ) || class_exists( 'TRP_Translate_Press' ) ) {
// Detect Post/Term function
add_filter( 'permalink_manager_detected_post_id', array( $this, 'fix_language_mismatch' ), 9, 4 );
add_filter( 'permalink_manager_detected_term_id', array( $this, 'fix_language_mismatch' ), 9, 4 );
// Fix posts page
add_filter( 'permalink_manager_filter_query', array( $this, 'fix_posts_page' ), 5, 5 );
// URI Editor
add_filter( 'permalink_manager_uri_editor_extra_info', array( $this, 'uri_editor_get_lang_col' ), 9, 3 );
add_filter( 'permalink_manager_uri_editor_extra_fields', array( $this, 'uri_editor_filter_lang' ), 9, 2 );
add_filter( 'permalink_manager_filter_uri_editor_query', array( $this, 'uri_editor_filter_sql_query' ), 9, 3 );
// Adjust front page ID
add_filter( 'permalink_manager_is_front_page', array( $this, 'wpml_is_front_page' ), 9, 3 );
// Provide the language code for specific post/term
add_filter( 'permalink_manager_get_language_code', array( $this, 'filter_get_language_code' ), 9, 2 );
// Regenerate/reset + Find & replace tools
add_filter( 'permalink_manager_get_items_query', array( $this, 'filter_query_by_language' ), 10, 2 );
add_filter( 'permalink_manager_tools_fields', array( $this, 'add_language_field' ), 10, 2 );
// Get translation mode
$mode = 0;
// A. WPML
if ( isset( $sitepress_settings['language_negotiation_type'] ) ) {
$url_settings = $sitepress_settings['language_negotiation_type'];
if ( in_array( $url_settings, array( 1, 2 ) ) ) {
$mode = 'prepend';
} else if ( $url_settings == 3 ) {
$mode = 'append';
}
} // B. Polylang
else if ( isset( $polylang->links_model->options['force_lang'] ) ) {
$url_settings = $polylang->links_model->options['force_lang'];
if ( in_array( $url_settings, array( 1, 2, 3 ) ) ) {
$mode = 'prepend';
}
} // C. TranslatePress
else if ( class_exists( 'TRP_Translate_Press' ) ) {
$mode = 'prepend';
}
if ( $mode === 'prepend' ) {
add_filter( 'permalink_manager_detect_uri', array( $this, 'detect_uri_language' ), 9, 3 );
add_filter( 'permalink_manager_filter_permalink_base', array( $this, 'prepend_lang_prefix' ), 9, 2 );
} else if ( $mode === 'append' ) {
add_filter( 'permalink_manager_filter_final_post_permalink', array( $this, 'append_lang_prefix' ), 5, 2 );
add_filter( 'permalink_manager_filter_final_term_permalink', array( $this, 'append_lang_prefix' ), 5, 2 );
}
// Translate permastructures
add_filter( 'permalink_manager_filter_permastructure', array( $this, 'translate_permastructure' ), 9, 2 );
// Translate custom permalinks
if ( $this->is_wpml_compatible() ) {
add_filter( 'permalink_manager_filter_final_post_permalink', array( $this, 'translate_permalinks' ), 9, 2 );
}
// Translate post type slug
if ( class_exists( 'WPML_Slug_Translation' ) ) {
add_filter( 'permalink_manager_filter_post_type_slug', array( $this, 'wpml_translate_post_type_slug' ), 9, 3 );
add_filter( 'permalink_manager_filter_taxonomy_slug', array( $this, 'wpml_translate_taxonomy_slug' ), 9, 3 );
}
// Translate "page" endpoint
if ( class_exists( 'PLL_Translate_Slugs_Model' ) ) {
add_filter( 'permalink_manager_endpoints', array( $this, 'pl_translate_pagination_endpoint' ), 9 );
add_filter( 'permalink_manager_detect_uri', array( $this, 'pl_detect_pagination_endpoint' ), 10, 3 );
}
// Translate WooCommerce endpoints
if ( class_exists( 'WCML_Endpoints' ) ) {
add_filter( 'request', array( $this, 'wpml_translate_wc_endpoints' ), 99999 );
}
// Generate custom permalink after WPML's Advanced Translation editor is used
if ( ! empty( $sitepress_settings['translation-management'] ) && ! empty( $sitepress_settings['translation-management']['doc_translation_method'] ) ) {
add_filter( 'icl_job_elements', array( $this, 'wpml_editor_custom_permalink_field' ), 99, 3 );
add_filter( 'wpml_tm_adjust_translation_fields', array( $this, 'wpml_editor_custom_permalink_label' ), 99, 3 );
add_filter( 'wpml_pre_save_pro_translation', array( $this, 'wpml_prevent_uri_save_before_translation_completed' ), 99, 2 );
add_action( 'wpml_translation_editor_save_job_data', array( $this, 'wpml_save_uri_after_wpml_translation_completed' ), 99 );
add_action( 'wpml_pro_translation_completed', array( $this, 'wpml_save_uri_after_wpml_translation_completed' ), 99, 3 );
}
add_action( 'icl_make_duplicate', array( $this, 'wpml_duplicate_uri' ), 999, 4 );
// Allow canonical redirect for default language if "Hide URL language information for default language" is turned on in Polylang settings
if ( ! empty( $polylang ) && ! empty( $polylang->links_model ) && ! empty( $polylang->links_model->options['force_lang'] ) ) {
add_filter( 'permalink_manager_filter_query', array( $this, 'pl_allow_canonical_redirect' ), 3, 5 );
}
}
}
/**
* Let users decide if they want Permalink Manager to force language code in the custom permalinks
*/
public static function is_wpml_compatible() {
global $permalink_manager_options;
// Use the current language if translation is not available but fallback mode is turned on
return ( ! empty( $permalink_manager_options['general']['wpml_support'] ) ) ? $permalink_manager_options['general']['wpml_support'] : false;
}
/**
* Return the language code string for specific post or term
*
* @param string|int|WP_Post|WP_Term $element
*
* @return false|string
*/
public static function get_language_code( $element ) {
global $TRP_LANGUAGE, $icl_adjust_id_url_filter_off, $sitepress, $polylang, $wpml_post_translations, $wpml_term_translations;
// Disable WPML adjust ID filter
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
$icl_adjust_id_url_filter_off_prior = $icl_adjust_id_url_filter_off;
$icl_adjust_id_url_filter_off = true;
// phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
// Fallback
if ( is_string( $element ) && strpos( $element, 'tax-' ) !== false ) {
$element_id = intval( preg_replace( "/[^0-9]/", "", $element ) );
$element = get_term( $element_id );
} else if ( is_numeric( $element ) ) {
$element = get_post( $element );
}
// A. TranslatePress
if ( ! empty( $TRP_LANGUAGE ) ) {
$lang_code = self::get_translatepress_language_code( $TRP_LANGUAGE );
} // B. Polylang
else if ( ! empty( $polylang ) && function_exists( 'pll_get_post_language' ) && function_exists( 'pll_get_term_language' ) ) {
if ( isset( $element->post_type ) ) {
$lang_code = pll_get_post_language( $element->ID, 'slug' );
} else if ( isset( $element->taxonomy ) ) {
$lang_code = pll_get_term_language( $element->term_id, 'slug' );
}
} // C. WPML
else if ( ! empty( $sitepress ) ) {
$is_wpml_compatible = ( method_exists( $sitepress, 'is_display_as_translated_post_type' ) ) ? self::is_wpml_compatible() : false;
if ( isset( $element->post_type ) ) {
$element_id = $element->ID;
$element_type = $element->post_type;
$fallback_lang_on = ( $is_wpml_compatible ) ? $sitepress->is_display_as_translated_post_type( $element_type ) : false;
} else if ( isset( $element->taxonomy ) ) {
$element_id = $element->term_taxonomy_id;
$element_type = $element->taxonomy;
$fallback_lang_on = ( $is_wpml_compatible ) ? $sitepress->is_display_as_translated_taxonomy( $element_type ) : false;
} else {
return false;
}
if ( ! empty( $fallback_lang_on ) && ! is_admin() && ! wp_doing_ajax() && ! defined( 'REST_REQUEST' ) ) {
$current_language = $sitepress->get_current_language();
if ( ! empty( $element->post_type ) ) {
$force_current_lang = $wpml_post_translations->element_id_in( $element_id, $current_language ) ? false : $current_language;
} else if ( ! empty( $element->taxonomy ) ) {
$force_current_lang = $wpml_term_translations->element_id_in( $element_id, $current_language ) ? false : $current_language;
}
}
$lang_code = ( ! empty( $force_current_lang ) ) ? $force_current_lang : apply_filters( 'wpml_element_language_code', null, array( 'element_id' => $element_id, 'element_type' => $element_type ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
}
// Enable WPML adjust ID filter
$icl_adjust_id_url_filter_off = $icl_adjust_id_url_filter_off_prior; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
// Use default language if nothing detected
return ( ! empty( $lang_code ) ) ? $lang_code : self::get_default_language();
}
/**
* Filter the language code of a provided post or term
*
* @param string $lang_code
* @param string|integer $element
*
* @return false|string
*/
public function filter_get_language_code( $lang_code = '', $element = '' ) {
return self::get_language_code( $element );
}
/**
* Extends an SQL query to filter results by specific WPML/Polylang language codes.
*
* @param string $query Original SQL query string.
* @param string $where WHERE clause of the query.
*
* @return string Modified SQL query with WPML filtering applied.
*/
public function filter_query_by_language( $query, $where ) {
global $wpdb, $polylang, $sitepress_settings;
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is already verified in parent/enclosing method
$language_codes = ( ! empty( $_POST['language_filter'] ) && is_array( $_POST['language_filter'] ) ) ? array_map( 'sanitize_key', $_POST['language_filter'] ) : array();
if ( empty( $language_codes ) ) {
return $query;
}
$is_terms_query = strpos( $query, "{$wpdb->terms}" ) !== false;
if ( ! empty( $polylang->links_model->options ) ) {
// 1. Prepare the slugs based on the condition
if ( $is_terms_query ) {
$all_slugs = array_merge( $language_codes, array_map( function ( $code ) {
return 'pll_' . $code;
}, $language_codes ) );
$taxonomy = 'term_language';
$join_on = 't.term_id';
} else {
$all_slugs = $language_codes;
$taxonomy = 'language';
$join_on = 'p.ID';
}
$language_term_ids = get_terms( array(
'taxonomy' => $taxonomy,
'hide_empty' => false,
'slug' => $all_slugs,
'fields' => 'ids'
) );
$join = " JOIN {$wpdb->term_relationships} tr_lang ON tr_lang.object_id = {$join_on}";
if ( ! empty( $language_term_ids ) ) {
$language_ids_in_sql = Permalink_Manager_Helper_Functions::prepare_array_for_sql_in( $language_term_ids );
$where_sql = " AND tr_lang.term_taxonomy_id IN ({$language_ids_in_sql})";
} else {
$where_sql = " AND 1=0"; // Force no results if no terms found
}
} else if ( ! empty( $sitepress_settings ) ) {
$language_codes_in_sql = Permalink_Manager_Helper_Functions::prepare_array_for_sql_in( $language_codes );
if ( $is_terms_query ) {
$element_type = '"tax_%"';
$element_id = 'tt.term_taxonomy_id';
} else {
$element_type = "CONCAT('post_', p.post_type)";
$element_id = "p.ID";
}
$join = " JOIN {$wpdb->prefix}icl_translations AS icl ON icl.element_id = {$element_id} AND icl.element_type LIKE {$element_type}";
if ( ! empty( $language_codes_in_sql ) ) {
$where_sql = " AND icl.language_code IN ({$language_codes_in_sql})";
} else {
$where_sql = " AND 1=0"; // Force no results if no terms found
}
} else {
return $where;
}
if ( stripos( $query, ' WHERE' ) !== false ) {
$query = str_ireplace( ' WHERE', $join . ' WHERE', $query );
} else {
$query .= ' ' . $join;
}
return str_replace( $where, $where . $where_sql, $query );
}
/**
* Adds a WPML language filter field to the Permalink Manager Tools UI.
*
* @param array $fields Existing fields array.
*
* @return array Modified fields array.
*/
public function add_language_field( $fields, $tool_name ) {
$languages = $this->get_all_languages();
if ( empty( $languages ) || ! is_array( $fields ) || ! in_array( $tool_name, array( 'regenerate', 'find_and_replace' ) ) ) {
return $fields;
}
$fields['language_filter'] = array(
'label' => __( 'Select by language', 'permalink-manager' ),
'type' => 'checkbox',
'container' => 'row',
'description' => __( 'Select languages to filter by, or leave blank for all.', 'permalink-manager' ),
'choices' => $languages
);
return $fields;
}
/**
* Return the language URL prefix code for TranslatePress
*
* @param string $lang
*
* @return false|string
*/
public static function get_translatepress_language_code( $lang ) {
$translatepress_settings = self::get_translatepress_settings();
if ( ! empty( $translatepress_settings['url-slugs'] ) ) {
$lang_code = ( ! empty( $translatepress_settings['url-slugs'][ $lang ] ) ) ? $translatepress_settings['url-slugs'][ $lang ] : '';
}
return ( ! empty( $lang_code ) ) ? $lang_code : false;
}
/**
* Get TranslatePress settings
*
* @return array
*/
private static function get_translatepress_settings() {
static $settings = null;
if ( $settings === null ) {
$settings = ( class_exists( 'TRP_Translate_Press' ) ) ? get_option( 'trp_settings', array() ) : array();
}
return $settings;
}
/**
* Return the language code for the default language
*
* @return false|string
*/
public static function get_default_language() {
global $sitepress;
if ( function_exists( 'pll_default_language' ) ) {
$def_lang = pll_default_language( 'slug' );
} else if ( is_object( $sitepress ) ) {
$def_lang = $sitepress->get_default_language();
} else if ( class_exists( 'TRP_Translate_Press' ) ) {
$translatepress_settings = self::get_translatepress_settings();
$def_lang = ( ! empty( $translatepress_settings['default-language'] ) ) ? self::get_translatepress_language_code( $translatepress_settings['default-language'] ) : '';
} else {
$def_lang = '';
}
return $def_lang;
}
/**
* Return the array with all defined languages
*
* @param bool $exclude_default_language
*
* @return array
*/
public static function get_all_languages( $exclude_default_language = false ) {
global $sitepress, $sitepress_settings;
$languages_array = $active_languages = array();
$default_language = self::get_default_language();
if ( ! empty( $sitepress_settings['active_languages'] ) ) {
$languages_array = $sitepress_settings['active_languages'];
} elseif ( function_exists( 'pll_languages_list' ) ) {
$languages_array = pll_languages_list( array( 'fields' => null ) );
}
// Get native language names as value
if ( $languages_array ) {
foreach ( $languages_array as $val ) {
if ( ! empty( $sitepress ) ) {
$lang = $val;
$lang_details = $sitepress->get_language_details( $lang );
$language_name = $lang_details['native_name'];
} else if ( ! empty( $val->name ) ) {
$lang = $val->slug;
$language_name = $val->name;
}
if ( ! empty( $lang ) ) {
$active_languages[ $lang ] = ( ! empty( $language_name ) ) ? sprintf( '%s (%s) ', $language_name, $lang ) : '-';
}
}
// Exclude default language if needed
if ( $exclude_default_language && $default_language && ! empty( $active_languages[ $default_language ] ) ) {
unset( $active_languages[ $default_language ] );
}
}
return $active_languages;
}
/**
* If the requested language code does not match the language of the requested content, modify which post/term is to be loaded
*
* @param string|int $item_id
* @param array $uri_parts
* @param bool $is_term
* @param array|bool $old_query
*
* @return false|int
*/
function fix_language_mismatch( $item_id, $uri_parts, $is_term = false, $old_query = array() ) {
global $permalink_manager_options, $pm_query, $polylang, $icl_adjust_id_url_filter_off;
$mode = ( ! empty( $permalink_manager_options['general']['fix_language_mismatch'] ) ) ? $permalink_manager_options['general']['fix_language_mismatch'] : 0;
// Stop WPML from changing the output of the get_term() and get_post() functions
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
$icl_adjust_id_url_filter_off_prior = $icl_adjust_id_url_filter_off;
$icl_adjust_id_url_filter_off = true;
// phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
if ( $is_term ) {
$element = get_term( $item_id );
if ( ! empty( $element ) && ! is_wp_error( $element ) ) {
$element_id = $element->term_id;
$element_type = $element->taxonomy;
} else {
return false;
}
} else {
$element = get_post( $item_id );
if ( ! empty( $element->post_type ) ) {
$element_id = $item_id;
$element_type = $element->post_type;
}
}
// Stop if no term or post is detected
if ( empty( $element_id ) || empty( $element_type ) ) {
return false;
}
// When url_to_postid() is called, always use the language code declared in $uri_parts
if ( ! empty( $element->post_type ) && $old_query === false ) {
$mode = 1;
$no_query_set = true;
}
// Get the language code of the found post/term
$element_language_code = self::get_language_code( $element );
// Try to get the detected language code if the query is already set
if ( defined( 'ICL_LANGUAGE_CODE' ) && empty( $no_query_set ) ) {
$detected_language_code = ICL_LANGUAGE_CODE;
} else if ( ! empty( $uri_parts['lang'] ) ) {
$detected_language_code = $uri_parts['lang'];
} else {
return $item_id;
}
if ( $detected_language_code !== $element_language_code ) {
// A. Display the content in requested language
// B. Allow the canonical redirect
if ( $mode == 1 || $mode == 2 ) {
if ( ! empty( $polylang ) ) {
if ( function_exists( 'pll_get_post' ) && ! $is_term ) {
$translated_item_id = pll_get_post( $element_id, $detected_language_code );
} else if ( function_exists( 'pll_get_term' ) && $is_term ) {
$translated_item_id = pll_get_term( $element_id, $detected_language_code );
}
$item_id = ( isset( $translated_item_id ) ) ? $translated_item_id : $item_id;
} else if ( ! empty( $no_query_set ) ) {
$item_id = apply_filters( 'wpml_object_id', $element_id, $element_type, false, $detected_language_code ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
} else {
$item_id = apply_filters( 'wpml_object_id', $element_id, $element_type ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
}
// Compare the URIs to prevent the redirect loop
if ( $mode == 2 && ! empty( $item_id ) && $item_id !== $element_id ) {
$detected_element_uri = Permalink_Manager_URI_Functions::get_single_uri( $element_id, false, false, $is_term );
$translated_element_uri = Permalink_Manager_URI_Functions::get_single_uri( $item_id, false, false, $is_term );
if ( ! empty( $detected_element_uri ) && ! empty( $translated_element_uri ) && $detected_element_uri !== $translated_element_uri ) {
$pm_query['flag'] = 'language_mismatch'; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
}
}
} // C. Display "404 error"
else {
$item_id = 0;
}
}
$icl_adjust_id_url_filter_off = $icl_adjust_id_url_filter_off_prior; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
return $item_id;
}
/**
* Fix the language switcher on blog page (WPML bug)
*
* @param array $query
* @param array $old_query
* @param array $uri_parts
* @param array $pm_query
* @param string $content_type
*
* @return array
*/
function fix_posts_page( $query, $old_query, $uri_parts, $pm_query, $content_type ) {
if ( empty( $pm_query['id'] ) || ! is_numeric( $pm_query['id'] ) ) {
return $query;
}
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
$blog_page_id = apply_filters( 'wpml_object_id', get_option( 'page_for_posts' ), 'page' );
$element_id = apply_filters( 'wpml_object_id', $pm_query['id'], 'page' );
// phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
if ( ! empty( $blog_page_id ) && ( $blog_page_id == $element_id ) && ! isset( $query['page'] ) ) {
$query['page'] = '';
}
return $query;
}
/**
* Detect the language of requested content and add it to $uri_parts array
*
* @param array $uri_parts
* @param string $request_url
* @param string $endpoints
*
* @return array
*/
function detect_uri_language( $uri_parts, $request_url, $endpoints ) {
global $sitepress_settings, $polylang;
if ( ! empty( $sitepress_settings['active_languages'] ) ) {
$languages_list = (array) $sitepress_settings['active_languages'];
} else if ( function_exists( 'pll_languages_list' ) ) {
$languages_array = pll_languages_list();
$languages_list = ( is_array( $languages_array ) ) ? $languages_array : "";
} else if ( class_exists( 'TRP_Translate_Press' ) ) {
$translatepress_settings = self::get_translatepress_settings();
$languages_list = $translatepress_settings['url-slugs'];
}
if ( ! empty( $languages_list ) && is_array( $languages_list ) ) {
$languages_list = implode( "|", $languages_list );
} else {
return $uri_parts;
}
// Fix for multidomain language configuration
if ( ( isset( $sitepress_settings['language_negotiation_type'] ) && $sitepress_settings['language_negotiation_type'] == 2 ) || ( ! empty( $polylang->options['force_lang'] ) && $polylang->options['force_lang'] == 3 ) ) {
if ( ! empty( $polylang->options['domains'] ) ) {
$domains = (array) $polylang->options['domains'];
} else if ( ! empty( $sitepress_settings['language_domains'] ) ) {
$domains = (array) $sitepress_settings['language_domains'];
}
if ( ! empty( $domains ) ) {
foreach ( $domains as &$domain ) {
$domain = preg_replace( '/((http(s)?:\/\/(www\.)?)|(www\.))?(.+?)\/?$/', 'http://$6', $domain );
}
$request_url = trim( str_replace( $domains, "", $request_url ), "/" );
}
$default_language = "";
} else {
$default_language = self::get_default_language();
}
if ( ! empty( $languages_list ) ) {
preg_match( "/^(?:({$languages_list})\/)?(.+?)(?|\/({$endpoints})(?|\/(.*)|$)|\/()([\d]+)\/?)?$/i", $request_url, $regex_parts );
$uri_parts['lang'] = ( ! empty( $regex_parts[1] ) ) ? $regex_parts[1] : $default_language;
$uri_parts['uri'] = ( ! empty( $regex_parts[2] ) ) ? $regex_parts[2] : "";
$uri_parts['endpoint'] = ( ! empty( $regex_parts[3] ) ) ? $regex_parts[3] : "";
$uri_parts['endpoint_value'] = ( ! empty( $regex_parts[4] ) ) ? $regex_parts[4] : "";
}
return $uri_parts;
}
/**
* Append the language code to the URL directly after the domain name
*
* @param string $base
* @param string|int|WP_Post|WP_Term $element
* @param string $language_code
*
* @return string
*/
static function prepend_lang_prefix( $base, $element, $language_code = '' ) {
global $sitepress_settings, $polylang;
if ( ! empty( $element ) && empty( $language_code ) ) {
$language_code = self::get_language_code( $element );
// Last instance - use language parameter from &_GET array
$language_code = ( is_admin() && empty( $language_code ) && ! empty( $_GET['lang'] ) ) ? sanitize_key( $_GET['lang'] ) : $language_code; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}
// Adjust URL base
if ( ! empty( $language_code ) ) {
$default_language_code = self::get_default_language();
$home_url = get_home_url();
$translatepress_settings = self::get_translatepress_settings();
// Hide language code if "Use directory for default language" option is enabled
$hide_prefix_in_translatepress = ( ! empty( $translatepress_settings['add-subdirectory-to-default-language'] ) && $translatepress_settings['add-subdirectory-to-default-language'] !== 'yes' ) ? true : false;
$hide_prefix_in_wpml = ( isset( $sitepress_settings['urls']['directory_for_default_language'] ) && $sitepress_settings['urls']['directory_for_default_language'] != 1 ) ? true : false;
$hide_prefix_in_polylang = ( ! empty( $polylang->links_model->options['hide_default'] ) ) ? true : false;
$hide_prefix_for_default_lang = ( $hide_prefix_in_wpml || $hide_prefix_in_polylang || $hide_prefix_in_translatepress ) ? true : false;
// A. Different domain per language
if ( ( isset( $sitepress_settings['language_negotiation_type'] ) && $sitepress_settings['language_negotiation_type'] == 2 ) || ( ! empty( $polylang->options['force_lang'] ) && $polylang->options['force_lang'] == 3 ) ) {
if ( ! empty( $polylang->options['domains'] ) ) {
$domains = $polylang->options['domains'];
} else if ( ! empty( $sitepress_settings['language_domains'] ) ) {
$domains = $sitepress_settings['language_domains'];
}
// Replace the domain name
if ( ! empty( $domains ) && ! empty( $domains[ $language_code ] ) ) {
$base = trim( $domains[ $language_code ], "/" );
// Append URL scheme
if ( ! preg_match( "~^(?:f|ht)tps?://~i", $base ) ) {
$scheme = wp_parse_url( $home_url, PHP_URL_SCHEME );
$base = "{$scheme}://{$base}";
}
}
} // B. Prepend language code
else if ( ! empty( $polylang->options['force_lang'] ) && $polylang->options['force_lang'] == 2 ) {
if ( $hide_prefix_for_default_lang && ( $default_language_code == $language_code ) ) {
return $base;
} else {
$base = preg_replace( '/(https?:\/\/)/', "$1{$language_code}.", $home_url );
}
} // C. Append prefix
else {
if ( $hide_prefix_for_default_lang && ( $default_language_code == $language_code ) ) {
return $base;
} else {
$base .= "/{$language_code}";
}
}
}
return $base;
}
/**
* Append language code as a $_GET parameter to the end of URL
*
* @param string $permalink
* @param string|int|WP_Post|WP_Term $element
*
* @return string
*/
function append_lang_prefix( $permalink, $element ) {
global $sitepress_settings;
$language_code = self::get_language_code( $element );
$default_language_code = self::get_default_language();
// Last instance - use language parameter from &_GET array
if ( is_admin() ) {
$language_code = ( empty( $language_code ) && ! empty( $_GET['lang'] ) ) ? sanitize_key( $_GET['lang'] ) : $language_code; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}
// Append ?lang query parameter
if ( isset( $sitepress_settings['language_negotiation_type'] ) && $sitepress_settings['language_negotiation_type'] == 3 ) {
if ( $default_language_code == $language_code ) {
return $permalink;
} else if ( strpos( $permalink, "lang=" ) === false ) {
$permalink .= "?lang={$language_code}";
}
}
return $permalink;
}
/**
* Display the language code in a table column in Bulk Permalink Editor
*
* @param string $output
* @param string $column
* @param WP_Post|WP_Term $element
*
* @return string
*/
function uri_editor_get_lang_col( $output, $column, $element ) {
$language_code = self::get_language_code( $element );
$output .= ( ! empty( $language_code ) ) ? sprintf( " | %s: %s ", __( 'Language', 'permalink-manager' ), $language_code ) : "";
return $output;
}
/**
* Add extra 'language' filter field to Bulk Permalink Editor
*
* @param $html
* @param $content_type
*
* @return mixed|string
*/
function uri_editor_filter_lang( $html, $content_type ) {
$choices = array();
if ( class_exists( 'SitePress' ) ) {
$languages = apply_filters( 'wpml_active_languages', '' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using WPML API
foreach ( $languages as $l ) {
$choices[ $l['language_code'] ] = $l['translated_name'];
}
} else if ( function_exists( 'pll_the_languages' ) ) {
$languages = pll_the_languages( array( 'show_flags' => 0, 'echo' => 0, 'raw' => 1 ) );
foreach ( $languages as $l ) {
$choices[ $l['slug'] ] = $l['name'];
}
}
if ( ! empty( $choices ) ) {
$choices = array_merge( array( __( 'All languages', 'permalink-manager' ) ), $choices );
$select_field = Permalink_Manager_UI_Elements::generate_option_field( 'langcode', array(
'type' => 'select',
'choices' => $choices,
'value' => ( isset( $_REQUEST['langcode'] ) ) ? esc_attr( sanitize_key( $_REQUEST['langcode'] ) ) : '' // phpcs:ignore WordPress.Security.NonceVerification.Recommended
) );
$html = sprintf( '%s
', $select_field );
}
return $html;
}
/**
* Filter the fetched items by selected language code
*
* @param $sql_query
* @param $sql_parts
* @param $is_taxonomy
*
* @return void
*/
function uri_editor_filter_sql_query( $sql_query, $sql_parts, $is_taxonomy ) {
global $wpdb;
$language_code = ( isset( $_GET['langcode'] ) ) ? esc_sql( sanitize_key( $_GET['langcode'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Data is used for filtering only, no state change.
if ( ! empty( $language_code ) ) {
if ( class_exists( 'SitePress' ) ) {
if ( $is_taxonomy ) {
$join = "{$sql_parts['start']} INNER JOIN {$wpdb->prefix}icl_translations AS icl ON tt.term_taxonomy_id = icl.element_id AND icl.element_type = CONCAT('tax_', tt.taxonomy) AND icl.language_code = '{$language_code}' ";
} else {
$join = "{$sql_parts['start']} JOIN {$wpdb->prefix}icl_translations icl ON p.ID = icl.element_id AND icl.element_type = CONCAT('post_', p.post_type) AND icl.language_code = '{$language_code}' ";
}
} else if ( class_exists( 'Polylang' ) && function_exists( 'PLL' ) ) {
$lang_term = PLL()->model->get_language( $language_code );
$lang_term_ttid = ( $is_taxonomy ) ? $lang_term->get_tax_prop( 'term_language', 'term_taxonomy_id' ) : $lang_term->get_tax_prop( 'language', 'term_taxonomy_id' );
if ( $is_taxonomy ) {
$join = "{$sql_parts['start']} INNER JOIN {$wpdb->term_relationships} AS tr ON tt.term_id = tr.object_id AND tr.term_taxonomy_id = {$lang_term_ttid} ";
} else {
$join = "{$sql_parts['start']} INNER JOIN {$wpdb->term_relationships} AS tr ON p.ID = tr.object_id AND tr.term_taxonomy_id = {$lang_term_ttid} ";
}
}
$sql_query = ( ! empty( $join ) ) ? str_replace( $sql_parts['start'], $join, $sql_query ) : $sql_query;
}
return $sql_query;
}
/**
* Check if requested URL is front page for any language
*
* @param bool $bool
* @param int $page_id
* @param int $front_page_id
*
* @return bool
*/
function wpml_is_front_page( $bool, $page_id, $front_page_id ) {
if ( $bool === false ) {
$default_language_code = self::get_default_language();
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
$page_id = apply_filters( 'wpml_object_id', $page_id, 'page', true, $default_language_code );
$front_page_id = apply_filters( 'wpml_object_id', $front_page_id, 'page', true, $default_language_code );
// phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
}
return ( ! empty( $page_id ) && $page_id == $front_page_id ) ? true : $bool;
}
/**
* Reapply WPML URL hooks and use them for custom permalinks filtered with Permalink Manager
*
* @param string $permalink
* @param int|WP_Post $post
*
* @return string
*/
function translate_permalinks( $permalink, $post ) {
global $wp_query, $wpml_url_filters, $wpml_post_translations, $sitepress;
// Do not translate permalinks inside XML sitemaps (XML Sitemap Generator for Google)
if ( ! empty( $wp_query->query_vars['xml_sitemap'] ) ) {
return $permalink;
}
if ( ! empty( $wpml_url_filters ) && ! empty( $wpml_post_translations ) ) {
$wpml_url_hook_name = _wp_filter_build_unique_id( 'post_link', array( $wpml_url_filters, 'permalink_filter' ), 1 );
$needs_translation = $wpml_post_translations->element_id_in( $post->ID, $sitepress->get_current_language() ) ? 1 : 0;
if ( has_filter( 'post_link', $wpml_url_hook_name ) && $needs_translation ) {
$permalink = $wpml_url_filters->permalink_filter( $permalink, $post );
}
}
return $permalink;
}
/**
* Use the translated permastructure for the default custom permalinks
*
* @param string $permastructure
* @param WP_Post|WP_Term $element
*
* @return string
*/
function translate_permastructure( $permastructure, $element ) {
global $permalink_manager_permastructs, $pagenow;
// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Data is used for filtering only, no state change.
// Get element language code
if ( ! empty( $_REQUEST['data'] ) && is_string( $_REQUEST['data'] ) && strpos( $_REQUEST['data'], "target_lang" ) ) {
$language_code = sanitize_key( preg_replace( '/(.*target_lang=)([^=&]+)(.*)/', '$2', $_REQUEST['data'] ) );
} else if ( in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) && ! empty( $_GET['lang'] ) ) {
$language_code = sanitize_key( $_GET['lang'] );
} else if ( ! empty( $_REQUEST['icl_post_language'] ) ) {
$language_code = sanitize_key( $_REQUEST['icl_post_language'] );
} else if ( ! empty( $_POST['action'] ) && $_POST['action'] == 'pm_save_permalink' && defined( 'ICL_LANGUAGE_CODE' ) ) {
$language_code = ICL_LANGUAGE_CODE;
} else {
$language_code = self::get_language_code( $element );
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( ! empty( $element->ID ) ) {
$translated_permastructure = ( ! empty( $permalink_manager_permastructs["post_types"]["{$element->post_type}_{$language_code}"] ) ) ? $permalink_manager_permastructs["post_types"]["{$element->post_type}_{$language_code}"] : '';
} else if ( ! empty( $element->term_id ) ) {
$translated_permastructure = ( ! empty( $permalink_manager_permastructs["taxonomies"]["{$element->taxonomy}_{$language_code}"] ) ) ? $permalink_manager_permastructs["taxonomies"]["{$element->taxonomy}_{$language_code}"] : '';
}
return ( ! empty( $translated_permastructure ) ) ? $translated_permastructure : $permastructure;
}
/**
* Translate %post_type% tag in custom permastructures
*
* @param string $post_type_slug
* @param int|WP_Post $element
* @param string $post_type
*
* @return string
*/
function wpml_translate_post_type_slug( $post_type_slug, $element, $post_type ) {
$post = ( is_integer( $element ) ) ? get_post( $element ) : $element;
$language_code = self::get_language_code( $post );
return apply_filters( 'wpml_get_translated_slug', $post_type_slug, $post_type, $language_code ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using WPML API
}
/**
* Translate %taxonomy% tag in custom permastructures
*
* @param string $taxonomy_slug
* @param int|WP_Term $element
* @param string $taxonomy
*
* @return string
*/
function wpml_translate_taxonomy_slug( $taxonomy_slug, $element, $taxonomy ) {
$term = ( is_integer( $element ) ) ? get_term( $element ) : $element;
$language_code = self::get_language_code( $term );
return apply_filters( 'wpml_get_translated_slug', $taxonomy_slug, $taxonomy, $language_code, 'taxonomy' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using WPML API
}
/**
* Translate WooCommerce URL endpoints
*
* @param array $request
*
* @return array
*/
function wpml_translate_wc_endpoints( $request ) {
global $woocommerce, $wpdb;
if ( ! empty( $woocommerce->query->query_vars ) ) {
// Get all endpoint translations
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
$endpoint_translations = $wpdb->get_results( "SELECT t.value AS translated_endpoint, t.language, s.value AS endpoint FROM {$wpdb->prefix}icl_string_translations AS t LEFT JOIN {$wpdb->prefix}icl_strings AS s ON t.string_id = s.id WHERE context = 'WP Endpoints'" );
// Replace translate endpoint with its original name
foreach ( $endpoint_translations as $endpoint ) {
if ( isset( $request[ $endpoint->translated_endpoint ] ) && ( $endpoint->endpoint !== $endpoint->translated_endpoint ) ) {
$request[ $endpoint->endpoint ] = $request[ $endpoint->translated_endpoint ];
unset( $request[ $endpoint->translated_endpoint ] );
}
}
}
return $request;
}
/**
* Edit custom URI using WPML Advanced Translation Editor
*
* @param array $elements
* @param int $post_id
* @param int $job_id
*
* @return array
*/
function wpml_editor_custom_permalink_field( $elements, $post_id, $job_id ) {
global $wpdb, $permalink_manager_options;
// Check if the custom permalink field should be translatable
if ( empty( $permalink_manager_options['general']['wpml_translate_mode'] ) ) {
return $elements;
}
if ( is_array( $elements ) ) {
// Get job element
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
$job = $wpdb->get_row( $wpdb->prepare( "SELECT editor, job_id, element_id, element_type, language_code, source_language_code FROM {$wpdb->prefix}icl_translate_job AS tj LEFT JOIN {$wpdb->prefix}icl_translation_status AS ts ON tj.rid = ts.rid LEFT JOIN {$wpdb->prefix}icl_translations AS t ON t.translation_id = ts.translation_id WHERE job_id = %d", $job_id ) );
// Check if the translated element is post or term
if ( ! empty( $job->element_type ) ) {
$translation_id = $job->element_id;
$original_custom_uri = Permalink_Manager_URI_Functions_Post::get_post_uri( $post_id, true );
if ( ! empty( $translation_id ) ) {
$translation_custom_uri = Permalink_Manager_URI_Functions_Post::get_post_uri( $translation_id );
$uri_translation_complete = ( ! empty( $translation_custom_uri ) ) ? 1 : 0;
} else {
$translation_custom_uri = '';
$uri_translation_complete = 0;
}
$custom_uri_element = new stdClass();
$custom_uri_element->tid = 999999;
$custom_uri_element->job_id = $job_id;
$custom_uri_element->content_id = 0;
$custom_uri_element->timestamp = gmdate( 'Y-m-d H:i:s' );
$custom_uri_element->field_type = 'Custom URI';
$custom_uri_element->field_wrap_tag = '';
$custom_uri_element->field_format = 'base64';
$custom_uri_element->field_translate = 1;
$custom_uri_element->field_data = base64_encode( $original_custom_uri );
$custom_uri_element->field_data_translated = base64_encode( $translation_custom_uri );
$custom_uri_element->field_finished = $uri_translation_complete;
$elements[] = $custom_uri_element;
}
}
return $elements;
}
/**
* Add label to custom permalink editor
*
* @param $elements
* @param $post_id
* @param $job_id
*
* @return mixed
*/
function wpml_editor_custom_permalink_label( $elements, $post_id, $job_id ) {
if ( is_array( $elements ) ) {
foreach ( $elements as &$field ) {
if ( isset( $field['field_type'] ) && ( $field['field_type'] === 'custom-uri' || $field['field_type'] === 'Custom URI' ) ) {
$field['title'] = __( 'Custom Permalink', 'permalink-manager' );
}
}
}
return $elements;
}
/**
* Prevents the custom permalink from being saved until the translation is completed and object terms are set
*
* @param array $postarr The post array that is being saved.
* @param stdClass $job The job object.
*
* @return array
*/
function wpml_prevent_uri_save_before_translation_completed( $postarr, $job ) {
add_filter( 'permalink_manager_allow_new_post_uri', '__return_false' );
return $postarr;
}
/**
* Generate custom permalink after WPML's Advanced Translation editor is used
*
* @param array|int|string $in
* @param array $postdata
* @param stdClass $job
*
* @return array|int|string
*/
function wpml_save_uri_after_wpml_translation_completed( $in, $postdata = '', $job = '' ) {
global $permalink_manager_options;
// Save the URI also when the translation is uncompleted
if ( ! empty( $in['fields'] ) && empty( $postdata ) && empty( $job->automatic ) ) {
$data = $in['fields'];
$original_id = $in['job_post_id'];
$element_type = ( strpos( $in['job_post_type'], 'post_' ) !== false ) ? preg_replace( '/^(post_)/', '', $in['job_post_type'] ) : '';
$translation_id = apply_filters( 'wpml_object_id', $original_id, $element_type, false, $in['target_lang'] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using WPML API
} else if ( is_numeric( $in ) ) {
$translation_id = $in;
$data = $postdata;
} else {
return $in;
}
$default_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri( $translation_id );
$current_uri = Permalink_Manager_URI_Functions::get_single_uri( $translation_id, false, true, null );
// A. Use the manually translated custom permalink (if available)
if ( empty( $job->automatic ) && ( isset( $data['custom-uri']['data'] ) || isset( $postdata['Custom URI'] ) ) ) {
if ( ! empty( $postdata['Custom URI'] ) ) {
$new_uri = Permalink_Manager_Helper_Functions::sanitize_title( $postdata['Custom URI']['data'] );
} else if ( ! empty( $data['custom-uri']['data'] ) ) {
$new_uri = Permalink_Manager_Helper_Functions::sanitize_title( $data['custom-uri']['data'], true );
}
$new_uri = ( empty( $new_uri ) || in_array( $new_uri, array( '-', 'auto' ) ) ) ? $default_uri : $new_uri;
} // B. Generate the new custom permalink (if not set earlier)
else if ( empty( $current_uri ) ) {
$new_uri = $default_uri;
} // C. Auto-update custom permalink
else if ( ! empty( $job->original_doc_id ) ) {
$auto_update_uri = get_post_meta( $job->original_doc_id, 'auto_update_uri', true );
$auto_update_uri = ( ! empty( $auto_update_uri ) ) ? $auto_update_uri : $permalink_manager_options['general']['auto_update_uris'];
if ( $auto_update_uri == 1 ) {
$new_uri = $default_uri;
}
}
// Save the custom permalink
if ( ! empty( $new_uri ) ) {
// Delay the auto-translated posts to make sure that the linked categories are also translated
if ( ! empty( $job->automatic ) ) {
sleep( 5 );
}
Permalink_Manager_URI_Functions_Post::save_uri( $translation_id, $new_uri, false );
}
return $in;
}
/**
* Clone the custom permalink if post is duplicated with WPML
*
* @param int $master_post_id
* @param string $lang
* @param array $post_array
* @param int $id
*/
function wpml_duplicate_uri( $master_post_id, $lang, $post_array, $id ) {
// Trigger the function only if duplicate is created in the metabox
if ( empty( $_POST['action'] ) || $_POST['action'] !== 'make_duplicates' ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
return;
}
$new_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri( $id );
Permalink_Manager_URI_Functions::save_single_uri( $id, $new_uri, false, true );
}
/**
* Allow the canonical redirect for default language if "Hide URL language information for default language" is turned on in Polylang settings
*
* @param array $query
* @param array $old_query
* @param array $uri_parts
* @param array $pm_query
* @param string $content_type
*
* @return array
*/
function pl_allow_canonical_redirect( $query, $old_query, $uri_parts, $pm_query, $content_type ) {
global $polylang;
// Run only if "Hide URL language information for default language" is available in Polylang settings
if ( ! empty( $pm_query['id'] ) && ! empty( $pm_query['lang'] ) && function_exists( 'pll_default_language' ) ) {
$url_lang = $polylang->links_model->get_language_from_url();
$def_lang = pll_default_language( 'slug' );
// A. Check if the slug of default language is present in the requested URL + "Hide URL language information for default language" is turned on OR the language code is present in the URL if the language is set from different domains
if ( ( $url_lang == $def_lang && ! empty( $polylang->links_model->options['hide_default'] ) ) || $polylang->options['force_lang'] == 3 ) {
unset( $query['do_not_redirect'] );
} // B. Check if the slug of default language is NOT present in the requested URL + "Hide URL language information for default language" is turned off
else if ( empty( $url_lang ) && empty( $polylang->links_model->options['hide_default'] ) ) {
unset( $query['do_not_redirect'] );
}
}
return $query;
}
/**
* Support the endpoints translated by Polylang
*
* @param string $endpoints
*
* @return string
*/
function pl_translate_pagination_endpoint( $endpoints ) {
$pagination_endpoint = $this->pl_get_translated_slugs( 'paged' );
if ( ! empty( $pagination_endpoint ) && ! empty( $pagination_endpoint['translations'] ) && function_exists( 'pll_current_language' ) ) {
$current_language = pll_current_language();
if ( ! empty( $current_language ) && ! empty( $pagination_endpoint['translations'][ $current_language ] ) ) {
$endpoints .= "|" . $pagination_endpoint['translations'][ $current_language ];
}
}
return $endpoints;
}
/**
* Get the translated slugs array
*
* @param string $slug
*
* @return array
*/
function pl_get_translated_slugs( $slug = '' ) {
$translated_slugs = get_transient( 'pll_translated_slugs' );
if ( is_array( $translated_slugs ) ) {
if ( ! empty( $slug ) && ! empty( $translated_slugs[ $slug ] ) ) {
$translated_slug = $translated_slugs[ $slug ];
} else {
$translated_slug = $translated_slugs;
}
} else {
$translated_slug = array();
}
return $translated_slug;
}
/**
* Get back the original name of the translated endpoint
*
* @param array $uri_parts
*
* @return array
*/
function pl_detect_pagination_endpoint( $uri_parts, $request_url, $endpoints ) {
if ( ! empty( $uri_parts['endpoint'] ) ) {
$pagination_endpoint = $this->pl_get_translated_slugs( 'paged' );
if ( ! empty( $pagination_endpoint['translations'] ) && in_array( $uri_parts['endpoint'], $pagination_endpoint['translations'] ) ) {
$uri_parts['endpoint'] = $pagination_endpoint['slug'];
}
}
return $uri_parts;
}
}
integrations/permalink-manager-seo-plugins.php 0000644 00000035066 15220204736 0015633 0 ustar 00 =' ) ) {
add_action( 'permalink_manager_updated_post_uri', array( $this, 'yoast_update_indexable_permalink' ), 10, 3 );
add_action( 'permalink_manager_updated_term_uri', array( $this, 'yoast_update_indexable_permalink' ), 10, 3 );
add_filter( 'wpseo_canonical', array( $this, 'yoast_fix_canonical' ), 10 );
add_filter( 'wpseo_opengraph_url', array( $this, 'yoast_fix_canonical' ), 10 );
add_filter( 'wpseo_dynamic_permalinks_enabled', '__return_true', 5 );
}
// Breadcrumbs
add_filter( 'wpseo_breadcrumb_links', array( $this, 'filter_breadcrumbs' ), 9 );
add_filter( 'rank_math/frontend/breadcrumb/items', array( $this, 'filter_breadcrumbs' ), 9 );
add_filter( 'seopress_pro_breadcrumbs_crumbs', array( $this, 'filter_breadcrumbs' ), 9 );
add_filter( 'woocommerce_get_breadcrumb', array( $this, 'filter_breadcrumbs' ), 9 );
add_filter( 'slim_seo_breadcrumbs_links', array( $this, 'filter_breadcrumbs' ), 9 );
add_filter( 'aioseo_breadcrumbs_trail', array( $this, 'filter_breadcrumbs' ), 9 );
add_filter( 'avia_breadcrumbs_trail', array( $this, 'filter_breadcrumbs' ), 100 );
}
/**
* Get the HTTP protocol of the home URL and use it in Yoast SEO sitemap permalinks
*
* @param string $permalink The permalink in the sitemap
*
* @return string The sitemap's permalink
*/
function yoast_fix_sitemap_urls( $permalink ) {
if ( class_exists( 'WPSEO_Utils' ) ) {
$home_url = WPSEO_Utils::home_url();
$home_protocol = parse_url( $home_url, PHP_URL_SCHEME );
$permalink = preg_replace( "/^http(s)?/", $home_protocol, $permalink );
}
return $permalink;
}
/**
* Update the permalink in the Yoast SEO indexable table when the permalink is changed
*
* @param int $element_id The ID of the post/term element that was updated.
* @param string $new_uri The new URI of the element.
* @param string $old_uri The old URI of the element.
*/
function yoast_update_indexable_permalink( $element_id, $new_uri, $old_uri ) {
global $wpdb;
if ( ! empty( $new_uri ) && ! empty( $old_uri ) && $new_uri !== $old_uri ) {
if ( current_filter() == 'permalink_manager_updated_term_uri' ) {
$permalink = get_term_link( (int) $element_id );
$object_type = 'term';
} else {
$permalink = get_permalink( $element_id );
$object_type = 'post';
}
if ( ! empty( $permalink ) ) {
$permalink_hash = strlen( $permalink ) . ':' . md5( $permalink );
$wpdb->update( "{$wpdb->prefix}yoast_indexable", array( 'permalink' => $permalink, 'permalink_hash' => $permalink_hash ), array( 'object_id' => $element_id, 'object_type' => $object_type ), array( '%s', '%s' ), array( '%d', '%s' ) );
}
}
}
/**
* Filter the canonical permalink used by SEO using 'wpseo_canonical' & 'wpseo_opengraph_url' hooks
*
* @param string $url The canonical URL that Yoast SEO has generated.
*
* @return string the URL.
*/
function yoast_fix_canonical( $url ) {
global $pm_query, $wp_rewrite;
if ( ! empty( $pm_query['id'] ) ) {
$element = get_queried_object();
if ( ! empty( $element->ID ) && ! empty( $element->post_type ) ) {
$new_url = get_permalink( $element->ID );
// Do not filter if custom canonical URL is set
$yoast_canonical_url = get_post_meta( $element->ID, '_yoast_wpseo_canonical', true );
if ( ! empty( $yoast_canonical_url ) ) {
return $url;
}
if ( is_home() ) {
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
$new_url = ( $paged > 1 ) ? sprintf( '%s/%s/%d', trim( $new_url, '/' ), $wp_rewrite->pagination_base, $paged ) : $new_url;
} else {
$paged = ( get_query_var( 'page' ) ) ? get_query_var( 'page' ) : 1;
$new_url = ( $paged > 1 ) ? sprintf( '%s/%d', trim( $new_url, '/' ), $paged ) : $new_url;
}
} else if ( ! empty( $element->taxonomy ) && ! empty( $element->term_id ) ) {
$new_url = get_term_link( $element, $element->taxonomy );
// Do not filter if custom canonical URL is set
if ( class_exists( 'WPSEO_Taxonomy_Meta' ) ) {
$yoast_canonical_url = WPSEO_Taxonomy_Meta::get_term_meta( $element, $element->taxonomy, 'canonical' );
if ( ! empty( $yoast_canonical_url ) ) {
return $url;
}
}
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
if ( $paged > 1 ) {
$new_url = sprintf( '%s/%s/%d', trim( $new_url, '/' ), $wp_rewrite->pagination_base, $paged );
}
}
$url = ( ! empty( $new_url ) ) ? $new_url : $url;
$url = Permalink_Manager_Core_Functions::control_trailing_slashes( $url );
}
return $url;
}
/**
* Filter the breadcrumbs array to match the structure of currently requested URL
*
* @param array $links The current breadcrumb links.
*
* @return array The $links array.
*/
function filter_breadcrumbs( $links ) {
// Get post type permastructure settings
global $permalink_manager_options, $post, $wpdb, $wp, $wp_current_filter;
// Check if the filter should be activated
if ( empty( $permalink_manager_options['general']['yoast_breadcrumbs'] ) ) {
return $links;
}
// Get current post/page/term (if available)
$queried_element = get_queried_object();
if ( ! empty( $queried_element->ID ) ) {
$element_id = $queried_element->ID;
} else if ( ! empty( $queried_element->term_id ) ) {
$element_id = "tax-{$queried_element->term_id}";
} else if ( defined( 'REST_REQUEST' ) && ! empty( $post->ID ) ) {
$element_id = $post->ID;
}
// Get the custom permalink (if available) or the current request URL (if unavailable)
$custom_uri = ( ! empty( $element_id ) ) ? Permalink_Manager_URI_Functions::get_single_uri( $element_id, false, true, null ) : '';
if ( ! empty( $custom_uri ) ) {
$custom_uri = preg_replace( "/([^\/]+)$/", '', $custom_uri );
} else {
return $links;
}
$custom_uri_parts = explode( '/', trim( $custom_uri ) );
$breadcrumbs = array();
$snowball = '';
$available_taxonomies = Permalink_Manager_Helper_Functions::get_taxonomies_array( null, null, true );
$available_post_types = Permalink_Manager_Helper_Functions::get_post_types_array( null, null, true );
$available_post_types_archive = Permalink_Manager_Helper_Functions::get_post_types_array( 'archive_slug', null, true );
$current_filter = end( $wp_current_filter );
// Get Yoast Meta (the breadcrumbs titles can be changed in Yoast metabox)
$yoast_meta_terms = get_option( 'wpseo_taxonomy_meta' );
// Check what array keys should be used for breadcrumbs ("All In One SEO" uses a more complicated schema)
if ( $current_filter == 'aioseo_breadcrumbs_trail' ) {
$breadcrumb_key_text = 'label';
$breadcrumb_key_url = 'link';
$is_aioseo = true;
} else if ( in_array( $current_filter, array( 'wpseo_breadcrumb_links', 'slim_seo_breadcrumbs_links' ) ) ) {
$breadcrumb_key_text = 'text';
$breadcrumb_key_url = 'url';
$is_aioseo = false;
} else {
$breadcrumb_key_text = 0;
$breadcrumb_key_url = 1;
$is_aioseo = false;
}
// Get internal breadcrumb elements
foreach ( $custom_uri_parts as $slug ) {
if ( empty( $slug ) ) {
continue;
}
$snowball = ( empty( $snowball ) ) ? $slug : "{$snowball}/{$slug}";
// 1A. Try to match any custom URI
$uri = trim( $snowball, "/" );
$element = Permalink_Manager_URI_Functions::find_uri( $uri, true );
if ( ! empty( $element ) && strpos( $element, 'tax-' ) !== false ) {
$element_id = intval( preg_replace( "/[^0-9]/", "", $element ) );
$element = get_term( $element_id );
} else if ( is_numeric( $element ) ) {
$element = get_post( $element );
}
// 1B. Try to get term
if ( empty( $element ) && ! empty( $available_taxonomies ) ) {
$sql = sprintf( "SELECT t.term_id, t.name, tt.taxonomy FROM {$wpdb->terms} AS t LEFT JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE slug = '%s' AND tt.taxonomy IN ('%s') LIMIT 1", esc_sql( $slug ), implode( "','", array_keys( $available_taxonomies ) ) );
$element = $wpdb->get_row( $sql );
}
// 1C. Try to get page/post
if ( empty( $element ) && ! empty( $available_post_types ) ) {
$sql = sprintf( "SELECT ID, post_title, post_type FROM {$wpdb->posts} WHERE post_name = '%s' AND post_status = 'publish' AND post_type IN ('%s') AND post_type != 'attachment' LIMIT 1", esc_sql( $slug ), implode( "','", array_keys( $available_post_types ) ) );
$element = $wpdb->get_row( $sql );
}
// 1D. Try to get post type archive
if ( empty( $element ) && ! empty( $available_post_types_archive ) && in_array( $snowball, $available_post_types_archive ) ) {
$post_type_slug = array_search( $snowball, $available_post_types_archive );
$element = get_post_type_object( $post_type_slug );
}
// 2A. When the term is found, we can add it to the breadcrumbs
if ( ! empty( $element->term_id ) ) {
$term_id = apply_filters( 'wpml_object_id', $element->term_id, $element->taxonomy, true );
$term = ( ( $element->term_id !== $term_id ) || $is_aioseo ) ? get_term( $term_id ) : $element;
// Alternative title
if ( $current_filter == 'wpseo_breadcrumb_links' ) {
$alt_title = ( ! empty( $yoast_meta_terms[ $term->taxonomy ][ $term->term_id ]['wpseo_bctitle'] ) ) ? $yoast_meta_terms[ $term->taxonomy ][ $term->term_id ]['wpseo_bctitle'] : '';
} else if ( $current_filter == 'seopress_pro_breadcrumbs_crumbs' ) {
$alt_title = get_term_meta( $term->term_id, '_seopress_robots_breadcrumbs', true );
} else if ( $current_filter == 'rank_math/frontend/breadcrumb/items' ) {
$alt_title = get_term_meta( $term->term_id, 'rank_math_breadcrumb_title', true );
}
$title = ( ! empty( $alt_title ) ) ? $alt_title : $term->name;
if ( $is_aioseo ) {
$breadcrumbs[] = array(
$breadcrumb_key_text => wp_strip_all_tags( $title ),
$breadcrumb_key_url => get_term_link( (int) $term->term_id, $term->taxonomy ),
'type' => 'taxonomy',
'subType' => 'parent',
'reference' => $term,
);
} else {
$breadcrumbs[] = array(
$breadcrumb_key_text => wp_strip_all_tags( $title ),
$breadcrumb_key_url => get_term_link( (int) $term->term_id, $term->taxonomy )
);
}
} // 2B. When the post/page is found, we can add it to the breadcrumbs
else if ( ! empty( $element->ID ) ) {
$page_id = apply_filters( 'wpml_object_id', $element->ID, $element->post_type, true );
$page = ( ( $element->ID !== $page_id ) || $is_aioseo ) ? get_post( $page_id ) : $element;
// Alternative title
if ( $current_filter == 'wpseo_breadcrumb_links' ) {
$alt_title = get_post_meta( $page->ID, '_yoast_wpseo_bctitle', true );
} else if ( $current_filter == 'seopress_pro_breadcrumbs_crumbs' ) {
$alt_title = get_post_meta( $page->ID, '_seopress_robots_breadcrumbs', true );
} else if ( $current_filter == 'rank_math/frontend/breadcrumb/items' ) {
$alt_title = get_post_meta( $page->ID, 'rank_math_breadcrumb_title', true );
}
$title = ( ! empty( $alt_title ) ) ? $alt_title : $page->post_title;
if ( $is_aioseo ) {
$breadcrumbs[] = array(
$breadcrumb_key_text => wp_strip_all_tags( $title ),
$breadcrumb_key_url => get_permalink( $page->ID ),
'type' => 'single',
'subType' => '',
'reference' => $page
);
} else {
$breadcrumbs[] = array(
$breadcrumb_key_text => wp_strip_all_tags( $title ),
$breadcrumb_key_url => get_permalink( $page->ID )
);
}
} // 2C. When the post archive is found, we can add it to the breadcrumbs
else if ( ! empty( $element->rewrite ) && ( ! empty( $element->labels->name ) ) ) {
if ( $is_aioseo ) {
$breadcrumbs[] = array(
$breadcrumb_key_text => apply_filters( 'post_type_archive_title', $element->labels->name, $element->name ),
$breadcrumb_key_url => get_post_type_archive_link( $element->name ),
'type' => 'postTypeArchive',
'subType' => '',
'reference' => $element
);
} else {
$breadcrumbs[] = array(
$breadcrumb_key_text => apply_filters( 'post_type_archive_title', $element->labels->name, $element->name ),
$breadcrumb_key_url => get_post_type_archive_link( $element->name )
);
}
}
}
// Add new links to current breadcrumbs array
if ( ! empty( $links ) && is_array( $links ) ) {
$first_element = reset( $links );
$last_element = end( $links );
$b_last_element = ( count( $links ) > 2 && ( ! is_singular() || is_home() ) ) ? prev( $links ) : "";
$breadcrumbs = ( ! empty( $breadcrumbs ) ) ? $breadcrumbs : array();
// Support RankMath/SEOPress/WooCommerce/Slim SEO/AIOSEO breadcrumbs
if ( in_array( $current_filter, array( 'wpseo_breadcrumb_links', 'rank_math/frontend/breadcrumb/items', 'seopress_pro_breadcrumbs_crumbs', 'woocommerce_get_breadcrumb', 'slim_seo_breadcrumbs_links', 'aioseo_breadcrumbs_trail' ) ) ) {
if ( $current_filter == 'slim_seo_breadcrumbs_links' ) {
$links = array_merge( array( $first_element ), $breadcrumbs );
} // Append the element before the last element if the last breadcrumb does not have a URL set (e.g. if the /page/ endpoint is used)
else if ( ! in_array( $current_filter, array( 'aioseo_breadcrumbs_trail', 'slim_seo_breadcrumbs_links' ) ) && ! empty( $wp->query_vars['paged'] ) && $wp->query_vars['paged'] > 1 && ! empty( $b_last_element[ $breadcrumb_key_url ] ) ) {
$links = array_merge( array( $first_element ), $breadcrumbs, array( $b_last_element ), array( $last_element ) );
} else {
$links = array_merge( array( $first_element ), $breadcrumbs, array( $last_element ) );
}
} // Support Avia/Enfold breadcrumbs
else if ( $current_filter == 'avia_breadcrumbs_trail' ) {
foreach ( $breadcrumbs as &$breadcrumb ) {
if ( isset( $breadcrumb[ $breadcrumb_key_text ] ) ) {
$breadcrumb = sprintf( '%2$s ', esc_attr( $breadcrumb[ $breadcrumb_key_url ] ), esc_attr( $breadcrumb[ $breadcrumb_key_text ] ) );
}
}
$links = array_merge( array( $first_element ), $breadcrumbs, array( 'trail_end' => $last_element ) );
}
}
return array_filter( $links );
}
} integrations/permalink-manager-woocommerce.php 0000644 00000037734 15220204736 0015711 0 ustar 00 query->query_vars ) ) {
$query_vars = $woocommerce->query->query_vars;
foreach ( $query_vars as $key => $val ) {
if ( isset( $query[ $key ] ) ) {
$query['do_not_redirect'] = 1;
break;
}
}
}
return $query;
}
/**
* Redirects the user from the Shop archive to the Shop page if the user is not searching for anything
* Disable canonical redirect on "thank you" & another WooCommerce pages
*/
function woocommerce_checkout_fix() {
global $wp_query, $pm_query, $permalink_manager_options;
// Redirect from Shop archive to selected page
if ( is_shop() && empty( $pm_query['id'] ) ) {
$redirect_mode = ( ! empty( $permalink_manager_options['general']['redirect'] ) ) ? $permalink_manager_options['general']['redirect'] : false;
$redirect_shop = apply_filters( 'permalink_manager_redirect_shop_archive', false );
$shop_page = get_option( 'woocommerce_shop_page_id' );
if ( $redirect_mode && $redirect_shop && $shop_page && empty( $wp_query->query_vars['s'] ) ) {
$shop_url = get_permalink( $shop_page );
wp_safe_redirect( $shop_url, $redirect_mode );
exit();
}
}
if ( is_checkout() || ( function_exists( 'is_wc_endpoint_url' ) && is_wc_endpoint_url() ) ) {
$wp_query->query_vars['do_not_redirect'] = 1;
}
}
/**
* Separate WooCommerce custom post types & taxonomies in "Permastructures" screen
*
* @param array $fields
*
* @return array
*/
public function woocommerce_permastructs_fields( $fields ) {
if ( is_array( $fields ) ) {
$woocommerce_fields = array( 'product' => 'post_types', 'product_tag' => 'taxonomies', 'product_cat' => 'taxonomies', 'product_brand' => 'taxonomies' );
$woocommerce_attributes = wc_get_attribute_taxonomies();
foreach ( $woocommerce_attributes as $woocommerce_attribute ) {
$woocommerce_fields["pa_{$woocommerce_attribute->attribute_name}"] = 'taxonomies';
}
foreach ( $woocommerce_fields as $field => $field_type ) {
if ( empty( $fields[ $field_type ]["fields"][ $field ] ) ) {
continue;
}
$fields["woocommerce"]["fields"][ $field ] = $fields[ $field_type ]["fields"][ $field ];
unset( $fields[ $field_type ]["fields"][ $field ] );
}
}
return $fields;
}
/**
* Add "Coupons" to the list of excluded post types
*
* @param array $post_types
*
* @return array
*/
public function woocommerce_coupon_uris( $post_types ) {
if ( is_array( $post_types ) ) {
$post_types[] = 'shop_coupon';
}
return $post_types;
}
/**
* Generate a new custom permalink for duplicated product
*
* @param WC_Product $new_product The new product object.
* @param WC_Product $old_product The product that was duplicated.
*/
function woocommerce_generate_permalinks_after_duplicate( $new_product, $old_product ) {
if ( ! empty( $new_product ) ) {
$product_id = $new_product->get_id();
// Ignore variations
if ( $new_product->get_type() === 'variation' || Permalink_Manager_Helper_Functions::is_post_excluded( $product_id, true ) ) {
return;
}
$custom_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri( $product_id, false, true );
Permalink_Manager_URI_Functions::save_single_uri( $product_id, $custom_uri, false, true );
}
}
/**
* If the URI contains %pa_attribute_name% tag, replace it with the value of the attribute
*
* @param string $default_uri The default custom permalink that WordPress would use for the post.
* @param string $slug The post slug.
* @param WP_Post $post The post object.
* @param string $post_name The post slug.
* @param bool $native_uri true if the URI is a native URI, false if it's a custom URI
*
* @return string The default custom permalink
*/
function woocommerce_product_attributes( $default_uri, $slug, $post, $post_name, $native_uri ) {
// Do not affect native URIs
if ( $native_uri ) {
return $default_uri;
}
// Use only for products
if ( empty( $post->post_type ) || $post->post_type !== 'product' ) {
return $default_uri;
}
preg_match_all( "/%pa_(.[^\%]+)%/", $default_uri, $custom_fields );
if ( ! empty( $custom_fields[1] ) ) {
$product = wc_get_product( $post->ID );
foreach ( $custom_fields[1] as $i => $custom_field ) {
$attribute_name = sanitize_title( $custom_field );
$attribute_value = $product->get_attribute( $attribute_name );
$default_uri = str_replace( $custom_fields[0][ $i ], Permalink_Manager_Helper_Functions::sanitize_title( $attribute_value ), $default_uri );
}
}
return $default_uri;
}
/**
* Stop the plugin from generating the custom permalink for new WooCommerce product prematurely.
*
* @param WC_Data $data The product object.
* @param WP_REST_Request $request The REST API request object.
* @param bool $is_new_post Whether the product is newly created.
*/
function woocommerce_delay_product_custom_permalink( $data, $request, $is_new_post ) {
add_filter( 'permalink_manager_pre_update_post_uri', '__return_null', 100 );
return $data;
}
/**
* Sets a custom permalink for a WooCommerce product after it is inserted via the REST API.
*
* @param WC_Product $product The product object.
* @param WP_REST_Request $request The REST API request object.
* @param bool $is_new_post Whether the product is newly created.
*/
function woocommerce_set_custom_uri_after_rest_insert( $product, $request, $is_new_post ) {
if ( $is_new_post && class_exists( 'Permalink_Manager_URI_Functions_Post' ) && method_exists( $product, 'get_id' ) ) {
$product_id = $product->get_id();
remove_filter( 'permalink_manager_pre_update_post_uri', '__return_null', 100 );
$new_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri( $product_id );
Permalink_Manager_URI_Functions_Post::save_uri( $product_id, $new_uri, $is_new_post );
}
}
/**
* Check the current request is a WooCommerce AJAX request. If it is, check the translated page's URL should be returned
*
* @param string $permalink The full URL of the post
* @param WP_Post $post The post object
* @param string $old_permalink The original URL of the post.
*
* @return string The permalink is being returned.
*/
function woocommerce_translate_ajax_fragments_urls( $permalink, $post, $old_permalink ) {
// Use it only if the permalinks are different
if ( $permalink == $old_permalink || $post->post_type !== 'page' ) {
return $permalink;
}
// A. Native WooCommerce AJAX events
if ( ! empty( $_REQUEST['wc-ajax'] ) ) {
$action = sanitize_title( $_REQUEST['wc-ajax'] );
} // B. Shoptimizer theme
else if ( ! empty( $_REQUEST['action'] ) ) {
$action = sanitize_title( $_REQUEST['action'] );
}
// Allowed action names
$allowed_actions = array( 'shoptimizer_pdp_ajax_atc', 'get_refreshed_fragments' );
if ( ! empty( $action ) && in_array( $action, $allowed_actions ) ) {
$translated_post_id = apply_filters( 'wpml_object_id', $post->ID, 'page' );
$permalink = ( $translated_post_id !== $post->ID ) ? get_permalink( $translated_post_id ) : $permalink;
}
return $permalink;
}
/**
* 4FA. Add a new column to the WooCommerce CSV Import/Export tool
*
* @param array $columns The array of columns to be displayed.
*
* @return array The $columns array.
*/
function woocommerce_csv_custom_uri_column( $columns ) {
if ( ! is_array( $columns ) ) {
return $columns;
}
$label = __( 'Custom URI', 'permalink-manager' );
$key = 'custom_uri';
if ( current_filter() == 'woocommerce_csv_product_import_mapping_default_columns' ) {
$columns[ $label ] = $key;
} else {
$columns[ $key ] = $label;
}
return $columns;
}
/**
* 4FB. Return the custom permalink of the product if it exists, otherwise return the default URI
*
* @param string $value The value of the column.
* @param WC_Product $product The product object.
* @param mixed $column_id The column ID.
*
* @return string The custom permalink or default permalink
*/
function woocommerce_export_custom_uri_value( $value, $product, $column_id ) {
if ( empty( $value ) && ! empty( $product ) ) {
$product_id = $product->get_id();
// Get custom permalink or default permalink
$value = Permalink_Manager_URI_Functions_Post::get_post_uri( $product_id );
}
return $value;
}
/**
* 4FC. Set the custom URI for the product using the value from CSV file, if not set use the default permalink
*
* @param WC_Product $product The product object.
* @param array $data The data array for the current row being imported.
*/
function woocommerce_csv_import_custom_uri( $product, $data ) {
if ( ! empty( $product ) ) {
$product_id = $product->get_id();
// Ignore variations
if ( $product->get_type() == 'variation' ) {
return;
}
$current_uri = Permalink_Manager_URI_Functions::get_single_uri( $product_id, false, true );
// A. Use default permalink if "Custom URI" is not set and did not exist before
if ( empty( $current_uri ) && empty( $data['custom_uri'] ) ) {
$custom_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri( $product_id, false, true );
} else if ( ! empty( $data['custom_uri'] ) ) {
$custom_uri = Permalink_Manager_Helper_Functions::sanitize_title( $data['custom_uri'] );
} else {
return;
}
Permalink_Manager_URI_Functions::save_single_uri( $product_id, $custom_uri, false, true );
}
}
/**
* Declare support for 'High-Performance order storage (COT)' and other features in WooCommerce
*/
function woocommerce_declare_compatibility() {
$features_util_class = '\Automattic\WooCommerce\Utilities\FeaturesUtil';
if ( class_exists( $features_util_class ) && method_exists( $features_util_class, 'declare_compatibility' ) ) {
$features = method_exists( $features_util_class, 'get_features' ) ? $features_util_class::get_features( true ) : array();
foreach ( array_keys( $features ) as $feature ) {
$features_util_class::declare_compatibility( $feature, PERMALINK_MANAGER_BASENAME );
}
}
}
/**
* Extract the Wishlist ID from the URI and add it to the $uri_parts array (WooCommerce Wishlist Plugin)
*
* @param array $uri_parts An array of the URI parts.
* @param string $request_url The URL that was requested.
* @param array $endpoints An array of all the endpoints that are currently registered.
*
* @return array The URI parts.
*/
function ti_woocommerce_wishlist_uris( $uri_parts, $request_url, $endpoints ) {
$wishlist_pid = function_exists( 'tinv_get_option' ) ? tinv_get_option( 'general', 'page_wishlist' ) : '';
// Find the Wishlist page URI
if ( is_numeric( $wishlist_pid ) ) {
$current_uri = Permalink_Manager_URI_Functions::get_single_uri( $wishlist_pid, false, true );
if ( ! empty( $current_uri ) ) {
$wishlist_uri = preg_quote( $current_uri, '/' );
// Extract the Wishlist ID
preg_match( "/^({$wishlist_uri})\/([^\/]+)\/?$/", $uri_parts['uri'], $output_array );
if ( ! empty( $output_array[2] ) ) {
$uri_parts['uri'] = $output_array[1];
$uri_parts['endpoint'] = 'tinvwlID';
$uri_parts['endpoint_value'] = $output_array[2];
}
}
}
return $uri_parts;
}
/**
* Keep the query strings appended to the product permalinks by WooCommerce Subscriptions
*
* @param string $permalink
* @param WP_Post $post
* @param string $old_permalink
*
* @return string
*/
function wcs_fix_subscription_links( $permalink, $post, $old_permalink ) {
if ( ! empty( $post->post_type ) && $post->post_type == 'product' && strpos( $old_permalink, 'switch-subscription=' ) !== false ) {
$query_arg = parse_url( $old_permalink, PHP_URL_QUERY );
$permalink = "{$permalink}?{$query_arg}";
}
return $permalink;
}
} class-wp-application-passwords-list-table.php 0000644 00000015445 15220264612 0015401 0 ustar 00 __( 'Name' ),
'created' => __( 'Created' ),
'last_used' => __( 'Last Used' ),
'last_ip' => __( 'Last IP' ),
'revoke' => __( 'Revoke' ),
);
}
/**
* Prepares the list of items for displaying.
*
* @since 5.6.0
*
* @global int $user_id User ID.
*/
public function prepare_items() {
global $user_id;
$this->items = array_reverse( WP_Application_Passwords::get_user_application_passwords( $user_id ) );
}
/**
* Handles the name column output.
*
* @since 5.6.0
*
* @param array $item The current application password item.
*/
public function column_name( $item ) {
echo esc_html( $item['name'] );
}
/**
* Handles the created column output.
*
* @since 5.6.0
*
* @param array $item The current application password item.
*/
public function column_created( $item ) {
if ( empty( $item['created'] ) ) {
echo '—';
} else {
echo date_i18n( __( 'F j, Y' ), $item['created'] );
}
}
/**
* Handles the last used column output.
*
* @since 5.6.0
*
* @param array $item The current application password item.
*/
public function column_last_used( $item ) {
if ( empty( $item['last_used'] ) ) {
echo '—';
} else {
echo date_i18n( __( 'F j, Y' ), $item['last_used'] );
}
}
/**
* Handles the last ip column output.
*
* @since 5.6.0
*
* @param array $item The current application password item.
*/
public function column_last_ip( $item ) {
if ( empty( $item['last_ip'] ) ) {
echo '—';
} else {
echo $item['last_ip'];
}
}
/**
* Handles the revoke column output.
*
* @since 5.6.0
*
* @param array $item The current application password item.
*/
public function column_revoke( $item ) {
$name = 'revoke-application-password-' . $item['uuid'];
printf(
'%3$s ',
esc_attr( $name ),
/* translators: %s: the application password's given name. */
esc_attr( sprintf( __( 'Revoke "%s"' ), $item['name'] ) ),
__( 'Revoke' )
);
}
/**
* Generates content for a single row of the table
*
* @since 5.6.0
*
* @param array $item The current item.
* @param string $column_name The current column name.
*/
protected function column_default( $item, $column_name ) {
/**
* Fires for each custom column in the Application Passwords list table.
*
* Custom columns are registered using the {@see 'manage_application-passwords-user_columns'} filter.
*
* @since 5.6.0
*
* @param string $column_name Name of the custom column.
* @param array $item The application password item.
*/
do_action( "manage_{$this->screen->id}_custom_column", $column_name, $item );
}
/**
* Generates custom table navigation to prevent conflicting nonces.
*
* @since 5.6.0
*
* @param string $which The location of the bulk actions: Either 'top' or 'bottom'.
*/
protected function display_tablenav( $which ) {
?>
bulk_actions( $which ); ?>
extra_tablenav( $which );
$this->pagination( $which );
?>
';
$this->single_row_columns( $item );
echo '';
}
/**
* Gets the name of the default primary column.
*
* @since 5.6.0
*
* @return string Name of the default primary column, in this case, 'name'.
*/
protected function get_default_primary_column_name() {
return 'name';
}
/**
* Prints the JavaScript template for the new row item.
*
* @since 5.6.0
*/
public function print_js_template_row() {
list( $columns, $hidden, , $primary ) = $this->get_column_info();
echo '';
foreach ( $columns as $column_name => $display_name ) {
$is_primary = $primary === $column_name;
$classes = "{$column_name} column-{$column_name}";
if ( $is_primary ) {
$classes .= ' has-row-actions column-primary';
}
if ( in_array( $column_name, $hidden, true ) ) {
$classes .= ' hidden';
}
printf( '', esc_attr( $classes ), esc_attr( wp_strip_all_tags( $display_name ) ) );
switch ( $column_name ) {
case 'name':
echo '{{ data.name }}';
break;
case 'created':
// JSON encoding automatically doubles backslashes to ensure they don't get lost when printing the inline JS.
echo '<# print( wp.date.dateI18n( ' . wp_json_encode( __( 'F j, Y' ) ) . ', data.created ) ) #>';
break;
case 'last_used':
echo '<# print( data.last_used !== null ? wp.date.dateI18n( ' . wp_json_encode( __( 'F j, Y' ) ) . ", data.last_used ) : '—' ) #>";
break;
case 'last_ip':
echo "{{ data.last_ip || '—' }}";
break;
case 'revoke':
printf(
'%2$s ',
/* translators: %s: the application password's given name. */
esc_attr( sprintf( __( 'Revoke "%s"' ), '{{ data.name }}' ) ),
esc_html__( 'Revoke' )
);
break;
default:
/**
* Fires in the JavaScript row template for each custom column in the Application Passwords list table.
*
* Custom columns are registered using the {@see 'manage_application-passwords-user_columns'} filter.
*
* @since 5.6.0
*
* @param string $column_name Name of the custom column.
*/
do_action( "manage_{$this->screen->id}_custom_column_js_template", $column_name );
break;
}
if ( $is_primary ) {
echo '' .
/* translators: Hidden accessibility text. */
__( 'Show more details' ) .
' ';
}
echo ' ';
}
echo ' ';
}
}
class-bulk-theme-upgrader-skin.php 0000644 00000005144 15220264612 0013172 0 ustar 00 upgrader->strings['skin_before_update_header'] = __( 'Updating Theme %1$s (%2$d/%3$d)' );
}
/**
* Performs an action before a bulk theme update.
*
* @since 3.0.0
*
* @param string $title
*/
public function before( $title = '' ) {
parent::before( $this->theme_info->display( 'Name' ) );
}
/**
* Performs an action following a bulk theme update.
*
* @since 3.0.0
*
* @param string $title
*/
public function after( $title = '' ) {
parent::after( $this->theme_info->display( 'Name' ) );
$this->decrement_update_count( 'theme' );
}
/**
* Displays the footer following the bulk update process.
*
* @since 3.0.0
*/
public function bulk_footer() {
parent::bulk_footer();
$update_actions = array(
'themes_page' => sprintf(
'%s ',
self_admin_url( 'themes.php' ),
__( 'Go to Themes page' )
),
'updates_page' => sprintf(
'%s ',
self_admin_url( 'update-core.php' ),
__( 'Go to WordPress Updates page' )
),
);
if ( ! current_user_can( 'switch_themes' ) && ! current_user_can( 'edit_theme_options' ) ) {
unset( $update_actions['themes_page'] );
}
/**
* Filters the list of action links available following bulk theme updates.
*
* @since 3.0.0
*
* @param string[] $update_actions Array of theme action links.
* @param WP_Theme $theme_info Theme object for the last-updated theme.
*/
$update_actions = apply_filters( 'update_bulk_theme_complete_actions', $update_actions, $this->theme_info );
if ( ! empty( $update_actions ) ) {
$this->feedback( implode( ' | ', (array) $update_actions ) );
}
}
}
class-plugin-upgrader.php 0000644 00000055321 15220264612 0011473 0 ustar 00 strings['up_to_date'] = __( 'The plugin is at the latest version.' );
$this->strings['no_package'] = __( 'Update package not available.' );
/* translators: %s: Package URL. */
$this->strings['downloading_package'] = sprintf( __( 'Downloading update from %s…' ), '%s ' );
$this->strings['unpack_package'] = __( 'Unpacking the update…' );
$this->strings['remove_old'] = __( 'Removing the old version of the plugin…' );
$this->strings['remove_old_failed'] = __( 'Could not remove the old plugin.' );
$this->strings['process_failed'] = __( 'Plugin update failed.' );
$this->strings['process_success'] = __( 'Plugin updated successfully.' );
$this->strings['process_bulk_success'] = __( 'Plugins updated successfully.' );
}
/**
* Initializes the installation strings.
*
* @since 2.8.0
*/
public function install_strings() {
$this->strings['no_package'] = __( 'Installation package not available.' );
/* translators: %s: Package URL. */
$this->strings['downloading_package'] = sprintf( __( 'Downloading installation package from %s…' ), '%s ' );
$this->strings['unpack_package'] = __( 'Unpacking the package…' );
$this->strings['installing_package'] = __( 'Installing the plugin…' );
$this->strings['remove_old'] = __( 'Removing the current plugin…' );
$this->strings['remove_old_failed'] = __( 'Could not remove the current plugin.' );
$this->strings['no_files'] = __( 'The plugin contains no files.' );
$this->strings['process_failed'] = __( 'Plugin installation failed.' );
$this->strings['process_success'] = __( 'Plugin installed successfully.' );
/* translators: 1: Plugin name, 2: Plugin version. */
$this->strings['process_success_specific'] = __( 'Successfully installed the plugin %1$s %2$s .' );
if ( ! empty( $this->skin->overwrite ) ) {
if ( 'update-plugin' === $this->skin->overwrite ) {
$this->strings['installing_package'] = __( 'Updating the plugin…' );
$this->strings['process_failed'] = __( 'Plugin update failed.' );
$this->strings['process_success'] = __( 'Plugin updated successfully.' );
}
if ( 'downgrade-plugin' === $this->skin->overwrite ) {
$this->strings['installing_package'] = __( 'Downgrading the plugin…' );
$this->strings['process_failed'] = __( 'Plugin downgrade failed.' );
$this->strings['process_success'] = __( 'Plugin downgraded successfully.' );
}
}
}
/**
* Install a plugin package.
*
* @since 2.8.0
* @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional.
*
* @param string $package The full local path or URI of the package.
* @param array $args {
* Optional. Other arguments for installing a plugin package. Default empty array.
*
* @type bool $clear_update_cache Whether to clear the plugin updates cache if successful.
* Default true.
* }
* @return bool|WP_Error True if the installation was successful, false or a WP_Error otherwise.
*/
public function install( $package, $args = array() ) {
$defaults = array(
'clear_update_cache' => true,
'overwrite_package' => false, // Do not overwrite files.
);
$parsed_args = wp_parse_args( $args, $defaults );
$this->init();
$this->install_strings();
add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
if ( $parsed_args['clear_update_cache'] ) {
// Clear cache so wp_update_plugins() knows about the new plugin.
add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 );
}
$this->run(
array(
'package' => $package,
'destination' => WP_PLUGIN_DIR,
'clear_destination' => $parsed_args['overwrite_package'],
'clear_working' => true,
'hook_extra' => array(
'type' => 'plugin',
'action' => 'install',
),
)
);
remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 );
remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
if ( ! $this->result || is_wp_error( $this->result ) ) {
return $this->result;
}
// Force refresh of plugin update information.
wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
if ( $parsed_args['overwrite_package'] ) {
/**
* Fires when the upgrader has successfully overwritten a currently installed
* plugin or theme with an uploaded zip package.
*
* @since 5.5.0
*
* @param string $package The package file.
* @param array $data The new plugin or theme data.
* @param string $package_type The package type ('plugin' or 'theme').
*/
do_action( 'upgrader_overwrote_package', $package, $this->new_plugin_data, 'plugin' );
}
return true;
}
/**
* Upgrades a plugin.
*
* @since 2.8.0
* @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional.
*
* @param string $plugin Path to the plugin file relative to the plugins directory.
* @param array $args {
* Optional. Other arguments for upgrading a plugin package. Default empty array.
*
* @type bool $clear_update_cache Whether to clear the plugin updates cache if successful.
* Default true.
* }
* @return bool|WP_Error True if the upgrade was successful, false or a WP_Error object otherwise.
*/
public function upgrade( $plugin, $args = array() ) {
$defaults = array(
'clear_update_cache' => true,
);
$parsed_args = wp_parse_args( $args, $defaults );
$this->init();
$this->upgrade_strings();
$current = get_site_transient( 'update_plugins' );
if ( ! isset( $current->response[ $plugin ] ) ) {
$this->skin->before();
$this->skin->set_result( false );
$this->skin->error( 'up_to_date' );
$this->skin->after();
return false;
}
// Get the URL to the zip file.
$upgrade_data = $current->response[ $plugin ];
add_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ), 10, 2 );
add_filter( 'upgrader_pre_install', array( $this, 'active_before' ), 10, 2 );
add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ), 10, 4 );
add_filter( 'upgrader_post_install', array( $this, 'active_after' ), 10, 2 );
/*
* There's a Trac ticket to move up the directory for zips which are made a bit differently, useful for non-.org plugins.
* 'source_selection' => array( $this, 'source_selection' ),
*/
if ( $parsed_args['clear_update_cache'] ) {
// Clear cache so wp_update_plugins() knows about the new plugin.
add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 );
}
$this->run(
array(
'package' => $upgrade_data->package,
'destination' => WP_PLUGIN_DIR,
'clear_destination' => true,
'clear_working' => true,
'hook_extra' => array(
'plugin' => $plugin,
'type' => 'plugin',
'action' => 'update',
'temp_backup' => array(
'slug' => dirname( $plugin ),
'src' => WP_PLUGIN_DIR,
'dir' => 'plugins',
),
),
)
);
// Cleanup our hooks, in case something else does an upgrade on this connection.
remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 );
remove_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ) );
remove_filter( 'upgrader_pre_install', array( $this, 'active_before' ) );
remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) );
remove_filter( 'upgrader_post_install', array( $this, 'active_after' ) );
if ( ! $this->result || is_wp_error( $this->result ) ) {
return $this->result;
}
// Force refresh of plugin update information.
wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
/*
* Ensure any future auto-update failures trigger a failure email by removing
* the last failure notification from the list when plugins update successfully.
*/
$past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() );
if ( isset( $past_failure_emails[ $plugin ] ) ) {
unset( $past_failure_emails[ $plugin ] );
update_option( 'auto_plugin_theme_update_emails', $past_failure_emails );
}
return true;
}
/**
* Upgrades several plugins at once.
*
* @since 2.8.0
* @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional.
*
* @param string[] $plugins Array of paths to plugin files relative to the plugins directory.
* @param array $args {
* Optional. Other arguments for upgrading several plugins at once.
*
* @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. Default true.
* }
* @return array|false An array of results indexed by plugin file, or false if unable to connect to the filesystem.
*/
public function bulk_upgrade( $plugins, $args = array() ) {
$wp_version = wp_get_wp_version();
$defaults = array(
'clear_update_cache' => true,
);
$parsed_args = wp_parse_args( $args, $defaults );
$this->init();
$this->bulk = true;
$this->upgrade_strings();
$current = get_site_transient( 'update_plugins' );
add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ), 10, 4 );
$this->skin->header();
// Connect to the filesystem first.
$connected = $this->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) );
if ( ! $connected ) {
$this->skin->footer();
return false;
}
$this->skin->bulk_header();
/*
* Only start maintenance mode if:
* - running Multisite and there are one or more plugins specified, OR
* - a plugin with an update available is currently active.
* @todo For multisite, maintenance mode should only kick in for individual sites if at all possible.
*/
$maintenance = ( is_multisite() && ! empty( $plugins ) );
foreach ( $plugins as $plugin ) {
$maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin ] ) );
}
if ( $maintenance ) {
$this->maintenance_mode( true );
}
$results = array();
$this->update_count = count( $plugins );
$this->update_current = 0;
foreach ( $plugins as $plugin ) {
++$this->update_current;
$this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true );
if ( ! isset( $current->response[ $plugin ] ) ) {
$this->skin->set_result( 'up_to_date' );
$this->skin->before();
$this->skin->feedback( 'up_to_date' );
$this->skin->after();
$results[ $plugin ] = true;
continue;
}
// Get the URL to the zip file.
$upgrade_data = $current->response[ $plugin ];
$this->skin->plugin_active = is_plugin_active( $plugin );
if ( isset( $upgrade_data->requires ) && ! is_wp_version_compatible( $upgrade_data->requires ) ) {
$result = new WP_Error(
'incompatible_wp_required_version',
sprintf(
/* translators: 1: Current WordPress version, 2: WordPress version required by the new plugin version. */
__( 'Your WordPress version is %1$s, however the new plugin version requires %2$s.' ),
$wp_version,
$upgrade_data->requires
)
);
$this->skin->before( $result );
$this->skin->error( $result );
$this->skin->after();
} elseif ( isset( $upgrade_data->requires_php ) && ! is_php_version_compatible( $upgrade_data->requires_php ) ) {
$result = new WP_Error(
'incompatible_php_required_version',
sprintf(
/* translators: 1: Current PHP version, 2: PHP version required by the new plugin version. */
__( 'The PHP version on your server is %1$s, however the new plugin version requires %2$s.' ),
PHP_VERSION,
$upgrade_data->requires_php
)
);
$this->skin->before( $result );
$this->skin->error( $result );
$this->skin->after();
} else {
add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
$result = $this->run(
array(
'package' => $upgrade_data->package,
'destination' => WP_PLUGIN_DIR,
'clear_destination' => true,
'clear_working' => true,
'is_multi' => true,
'hook_extra' => array(
'plugin' => $plugin,
'temp_backup' => array(
'slug' => dirname( $plugin ),
'src' => WP_PLUGIN_DIR,
'dir' => 'plugins',
),
),
)
);
remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) );
}
$results[ $plugin ] = $result;
// Prevent credentials auth screen from displaying multiple times.
if ( false === $result ) {
break;
}
} // End foreach $plugins.
$this->maintenance_mode( false );
// Force refresh of plugin update information.
wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
/** This action is documented in wp-admin/includes/class-wp-upgrader.php */
do_action(
'upgrader_process_complete',
$this,
array(
'action' => 'update',
'type' => 'plugin',
'bulk' => true,
'plugins' => $plugins,
)
);
$this->skin->bulk_footer();
$this->skin->footer();
// Cleanup our hooks, in case something else does an upgrade on this connection.
remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) );
/*
* Ensure any future auto-update failures trigger a failure email by removing
* the last failure notification from the list when plugins update successfully.
*/
$past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() );
foreach ( $results as $plugin => $result ) {
// Maintain last failure notification when plugins failed to update manually.
if ( ! $result || is_wp_error( $result ) || ! isset( $past_failure_emails[ $plugin ] ) ) {
continue;
}
unset( $past_failure_emails[ $plugin ] );
}
update_option( 'auto_plugin_theme_update_emails', $past_failure_emails );
return $results;
}
/**
* Checks that the source package contains a valid plugin.
*
* Hooked to the {@see 'upgrader_source_selection'} filter by Plugin_Upgrader::install().
*
* @since 3.3.0
*
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
*
* @param string $source The path to the downloaded package source.
* @return string|WP_Error The source as passed, or a WP_Error object on failure.
*/
public function check_package( $source ) {
global $wp_filesystem;
$wp_version = wp_get_wp_version();
$this->new_plugin_data = array();
if ( is_wp_error( $source ) ) {
return $source;
}
$working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit( WP_CONTENT_DIR ), $source );
if ( ! is_dir( $working_directory ) ) { // Confidence check, if the above fails, let's not prevent installation.
return $source;
}
// Check that the folder contains at least 1 valid plugin.
$files = glob( $working_directory . '*.php' );
if ( $files ) {
foreach ( $files as $file ) {
$new_plugin_data = get_plugin_data( $file, false, false );
if ( ! empty( $new_plugin_data['Name'] ) ) {
$this->new_plugin_data = $new_plugin_data;
break;
}
}
}
if ( empty( $this->new_plugin_data ) ) {
return new WP_Error( 'incompatible_archive_no_plugins', $this->strings['incompatible_archive'], __( 'No valid plugins were found.' ) );
}
$requires_php = $new_plugin_data['RequiresPHP'] ?? null;
$requires_wp = $new_plugin_data['RequiresWP'] ?? null;
if ( ! is_php_version_compatible( $requires_php ) ) {
$error = sprintf(
/* translators: 1: Current PHP version, 2: Version required by the uploaded plugin. */
__( 'The PHP version on your server is %1$s, however the uploaded plugin requires %2$s.' ),
PHP_VERSION,
$requires_php
);
return new WP_Error( 'incompatible_php_required_version', $this->strings['incompatible_archive'], $error );
}
if ( ! is_wp_version_compatible( $requires_wp ) ) {
$error = sprintf(
/* translators: 1: Current WordPress version, 2: Version required by the uploaded plugin. */
__( 'Your WordPress version is %1$s, however the uploaded plugin requires %2$s.' ),
$wp_version,
$requires_wp
);
return new WP_Error( 'incompatible_wp_required_version', $this->strings['incompatible_archive'], $error );
}
return $source;
}
/**
* Retrieves the path to the file that contains the plugin info.
*
* This isn't used internally in the class, but is called by the skins.
*
* @since 2.8.0
*
* @return string|false The full path to the main plugin file, or false.
*/
public function plugin_info() {
if ( ! is_array( $this->result ) ) {
return false;
}
if ( empty( $this->result['destination_name'] ) ) {
return false;
}
// Ensure to pass with leading slash.
$plugin = get_plugins( '/' . $this->result['destination_name'] );
if ( empty( $plugin ) ) {
return false;
}
// Assume the requested plugin is the first in the list.
$plugin_files = array_keys( $plugin );
return $this->result['destination_name'] . '/' . $plugin_files[0];
}
/**
* Deactivates a plugin before it is upgraded.
*
* Hooked to the {@see 'upgrader_pre_install'} filter by Plugin_Upgrader::upgrade().
*
* @since 2.8.0
* @since 4.1.0 Added a return value.
*
* @param bool|WP_Error $response The installation response before the installation has started.
* @param array $plugin Plugin package arguments.
* @return bool|WP_Error The original `$response` parameter or WP_Error.
*/
public function deactivate_plugin_before_upgrade( $response, $plugin ) {
if ( is_wp_error( $response ) ) { // Bypass.
return $response;
}
// When in cron (background updates) don't deactivate the plugin, as we require a browser to reactivate it.
if ( wp_doing_cron() ) {
return $response;
}
$plugin = $plugin['plugin'] ?? '';
if ( empty( $plugin ) ) {
return new WP_Error( 'bad_request', $this->strings['bad_request'] );
}
if ( is_plugin_active( $plugin ) ) {
// Deactivate the plugin silently, Prevent deactivation hooks from running.
deactivate_plugins( $plugin, true );
}
return $response;
}
/**
* Turns on maintenance mode before attempting to background update an active plugin.
*
* Hooked to the {@see 'upgrader_pre_install'} filter by Plugin_Upgrader::upgrade().
*
* @since 5.4.0
*
* @param bool|WP_Error $response The installation response before the installation has started.
* @param array $plugin Plugin package arguments.
* @return bool|WP_Error The original `$response` parameter or WP_Error.
*/
public function active_before( $response, $plugin ) {
if ( is_wp_error( $response ) ) {
return $response;
}
// Only enable maintenance mode when in cron (background update).
if ( ! wp_doing_cron() ) {
return $response;
}
$plugin = $plugin['plugin'] ?? '';
// Only run if plugin is active.
if ( ! is_plugin_active( $plugin ) ) {
return $response;
}
// Change to maintenance mode. Bulk edit handles this separately.
if ( ! $this->bulk ) {
$this->maintenance_mode( true );
}
return $response;
}
/**
* Turns off maintenance mode after upgrading an active plugin.
*
* Hooked to the {@see 'upgrader_post_install'} filter by Plugin_Upgrader::upgrade().
*
* @since 5.4.0
*
* @param bool|WP_Error $response The installation response after the installation has finished.
* @param array $plugin Plugin package arguments.
* @return bool|WP_Error The original `$response` parameter or WP_Error.
*/
public function active_after( $response, $plugin ) {
if ( is_wp_error( $response ) ) {
return $response;
}
// Only disable maintenance mode when in cron (background update).
if ( ! wp_doing_cron() ) {
return $response;
}
$plugin = $plugin['plugin'] ?? '';
// Only run if plugin is active.
if ( ! is_plugin_active( $plugin ) ) {
return $response;
}
// Time to remove maintenance mode. Bulk edit handles this separately.
if ( ! $this->bulk ) {
$this->maintenance_mode( false );
}
return $response;
}
/**
* Deletes the old plugin during an upgrade.
*
* Hooked to the {@see 'upgrader_clear_destination'} filter by
* Plugin_Upgrader::upgrade() and Plugin_Upgrader::bulk_upgrade().
*
* @since 2.8.0
*
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
*
* @param bool|WP_Error $removed Whether the destination was cleared.
* True on success, WP_Error on failure.
* @param string $local_destination The local package destination.
* @param string $remote_destination The remote package destination.
* @param array $plugin Extra arguments passed to hooked filters.
* @return bool|WP_Error
*/
public function delete_old_plugin( $removed, $local_destination, $remote_destination, $plugin ) {
global $wp_filesystem;
if ( is_wp_error( $removed ) ) {
return $removed; // Pass errors through.
}
$plugin = $plugin['plugin'] ?? '';
if ( empty( $plugin ) ) {
return new WP_Error( 'bad_request', $this->strings['bad_request'] );
}
$plugins_dir = $wp_filesystem->wp_plugins_dir();
$this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin ) );
if ( ! $wp_filesystem->exists( $this_plugin_dir ) ) { // If it's already vanished.
return $removed;
}
/*
* If plugin is in its own directory, recursively delete the directory.
* Base check on if plugin includes directory separator AND that it's not the root plugin folder.
*/
if ( strpos( $plugin, '/' ) && $this_plugin_dir !== $plugins_dir ) {
$deleted = $wp_filesystem->delete( $this_plugin_dir, true );
} else {
$deleted = $wp_filesystem->delete( $plugins_dir . $plugin );
}
if ( ! $deleted ) {
return new WP_Error( 'remove_old_failed', $this->strings['remove_old_failed'] );
}
return true;
}
}
class-wp-privacy-policy-content.php 0000644 00000077631 15220264612 0013444 0 ustar 00 $plugin_name,
'policy_text' => $policy_text,
);
if ( ! in_array( $data, self::$policy_content, true ) ) {
self::$policy_content[] = $data;
}
}
/**
* Performs a quick check to determine whether any privacy info has changed.
*
* @since 4.9.6
*/
public static function text_change_check() {
$policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
// The site doesn't have a privacy policy.
if ( empty( $policy_page_id ) ) {
return false;
}
if ( ! current_user_can( 'edit_post', $policy_page_id ) ) {
return false;
}
$old = (array) get_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' );
// Updates are not relevant if the user has not reviewed any suggestions yet.
if ( empty( $old ) ) {
return false;
}
$cached = get_option( '_wp_suggested_policy_text_has_changed' );
/*
* When this function is called before `admin_init`, `self::$policy_content`
* has not been populated yet, so use the cached result from the last
* execution instead.
*/
if ( ! did_action( 'admin_init' ) ) {
return 'changed' === $cached;
}
$new = self::$policy_content;
// Remove the extra values added to the meta.
foreach ( $old as $key => $data ) {
if ( ! is_array( $data ) || ! empty( $data['removed'] ) ) {
unset( $old[ $key ] );
continue;
}
$old[ $key ] = array(
'plugin_name' => $data['plugin_name'],
'policy_text' => $data['policy_text'],
);
}
// Normalize the order of texts, to facilitate comparison.
sort( $old );
sort( $new );
/*
* The == operator (equal, not identical) was used intentionally.
* See https://www.php.net/manual/en/language.operators.array.php
*/
if ( $new != $old ) {
/*
* A plugin was activated or deactivated, or some policy text has changed.
* Show a notice on the relevant screens to inform the admin.
*/
add_action( 'admin_notices', array( 'WP_Privacy_Policy_Content', 'policy_text_changed_notice' ) );
$state = 'changed';
} else {
$state = 'not-changed';
}
// Cache the result for use before `admin_init` (see above).
if ( $cached !== $state ) {
update_option( '_wp_suggested_policy_text_has_changed', $state, false );
}
return 'changed' === $state;
}
/**
* Outputs a warning when some privacy info has changed.
*
* @since 4.9.6
*/
public static function policy_text_changed_notice() {
$screen = get_current_screen()->id;
if ( 'privacy' !== $screen ) {
return;
}
$privacy_message = sprintf(
/* translators: %s: Privacy Policy Guide URL. */
__( 'The suggested privacy policy text has changed. Please review the guide and update your privacy policy.' ),
esc_url( admin_url( 'privacy-policy-guide.php?tab=policyguide' ) )
);
wp_admin_notice(
$privacy_message,
array(
'type' => 'warning',
'additional_classes' => array( 'policy-text-updated' ),
'dismissible' => true,
)
);
}
/**
* Updates the cached policy info when the policy page is updated.
*
* @since 4.9.6
* @access private
*
* @param int $post_id The ID of the updated post.
*/
public static function _policy_page_updated( $post_id ) {
$policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
if ( ! $policy_page_id || $policy_page_id !== (int) $post_id ) {
return;
}
// Remove updated|removed status.
$old = (array) get_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' );
$done = array();
$update_cache = false;
foreach ( $old as $old_key => $old_data ) {
if ( ! empty( $old_data['removed'] ) ) {
// Remove the old policy text.
$update_cache = true;
continue;
}
if ( ! empty( $old_data['updated'] ) ) {
// 'updated' is now 'added'.
$done[] = array(
'plugin_name' => $old_data['plugin_name'],
'policy_text' => $old_data['policy_text'],
'added' => $old_data['updated'],
);
$update_cache = true;
} else {
$done[] = $old_data;
}
}
if ( $update_cache ) {
delete_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' );
// Update the cache.
foreach ( $done as $data ) {
add_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content', $data );
}
}
}
/**
* Checks for updated, added or removed privacy policy information from plugins.
*
* Caches the current info in post_meta of the policy page.
*
* @since 4.9.6
*
* @return array The privacy policy text/information added by core and plugins.
*/
public static function get_suggested_policy_text() {
$policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
$checked = array();
$time = time();
$update_cache = false;
$new = self::$policy_content;
$old = array();
if ( $policy_page_id ) {
$old = (array) get_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' );
}
// Check for no-changes and updates.
foreach ( $new as $new_key => $new_data ) {
foreach ( $old as $old_key => $old_data ) {
$found = false;
if ( $new_data['policy_text'] === $old_data['policy_text'] ) {
// Use the new plugin name in case it was changed, translated, etc.
if ( $old_data['plugin_name'] !== $new_data['plugin_name'] ) {
$old_data['plugin_name'] = $new_data['plugin_name'];
$update_cache = true;
}
// A plugin was re-activated.
if ( ! empty( $old_data['removed'] ) ) {
unset( $old_data['removed'] );
$old_data['added'] = $time;
$update_cache = true;
}
$checked[] = $old_data;
$found = true;
} elseif ( $new_data['plugin_name'] === $old_data['plugin_name'] ) {
// The info for the policy was updated.
$checked[] = array(
'plugin_name' => $new_data['plugin_name'],
'policy_text' => $new_data['policy_text'],
'updated' => $time,
);
$found = true;
$update_cache = true;
}
if ( $found ) {
unset( $new[ $new_key ], $old[ $old_key ] );
continue 2;
}
}
}
if ( ! empty( $new ) ) {
// A plugin was activated.
foreach ( $new as $new_data ) {
if ( ! empty( $new_data['plugin_name'] ) && ! empty( $new_data['policy_text'] ) ) {
$new_data['added'] = $time;
$checked[] = $new_data;
}
}
$update_cache = true;
}
if ( ! empty( $old ) ) {
// A plugin was deactivated.
foreach ( $old as $old_data ) {
if ( ! empty( $old_data['plugin_name'] ) && ! empty( $old_data['policy_text'] ) ) {
$data = array(
'plugin_name' => $old_data['plugin_name'],
'policy_text' => $old_data['policy_text'],
'removed' => $time,
);
$checked[] = $data;
}
}
$update_cache = true;
}
if ( $update_cache && $policy_page_id ) {
delete_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' );
// Update the cache.
foreach ( $checked as $data ) {
add_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content', $data );
}
}
return $checked;
}
/**
* Adds a notice with a link to the guide when editing the privacy policy page.
*
* @since 4.9.6
* @since 5.0.0 The `$post` parameter was made optional.
*
* @global WP_Post $post Global post object.
*
* @param WP_Post|null $post The currently edited post. Default null.
*/
public static function notice( $post = null ) {
if ( is_null( $post ) ) {
global $post;
} else {
$post = get_post( $post );
}
if ( ! ( $post instanceof WP_Post ) ) {
return;
}
if ( ! current_user_can( 'manage_privacy_options' ) ) {
return;
}
$current_screen = get_current_screen();
$policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
if ( 'post' !== $current_screen->base || $policy_page_id !== $post->ID ) {
return;
}
$message = __( 'Need help putting together your new Privacy Policy page? Check out the guide for recommendations on what content to include, along with policies suggested by your plugins and theme.' );
$url = esc_url( admin_url( 'options-privacy.php?tab=policyguide' ) );
$label = __( 'View Privacy Policy Guide.' );
if ( get_current_screen()->is_block_editor() ) {
wp_enqueue_script( 'wp-notices' );
$action = array(
'url' => $url,
'label' => $label,
);
wp_add_inline_script(
'wp-notices',
sprintf(
'wp.data.dispatch( "core/notices" ).createWarningNotice( "%s", { actions: [ %s ], isDismissible: false } )',
$message,
wp_json_encode( $action, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES )
),
'after'
);
} else {
$message .= sprintf(
' %s %s ',
$url,
$label,
/* translators: Hidden accessibility text. */
__( '(opens in a new tab)' )
);
wp_admin_notice(
$message,
array(
'type' => 'warning',
'additional_classes' => array( 'inline', 'wp-pp-notice' ),
)
);
}
}
/**
* Outputs the privacy policy guide together with content from the theme and plugins.
*
* @since 4.9.6
*/
public static function privacy_policy_guide() {
$content_array = self::get_suggested_policy_text();
$date_format = __( 'F j, Y' );
$i = 0;
foreach ( $content_array as $section ) {
++$i;
$removed = '';
if ( ! empty( $section['removed'] ) ) {
$badge_class = ' red';
$date = date_i18n( $date_format, $section['removed'] );
/* translators: %s: Date of plugin deactivation. */
$badge_title = sprintf( __( 'Removed %s.' ), $date );
/* translators: %s: Date of plugin deactivation. */
$removed = sprintf( __( 'You deactivated this plugin on %s and may no longer need this policy.' ), $date );
$removed = wp_get_admin_notice(
$removed,
array(
'type' => 'info',
'additional_classes' => array( 'inline' ),
)
);
} elseif ( ! empty( $section['updated'] ) ) {
$badge_class = ' blue';
$date = date_i18n( $date_format, $section['updated'] );
/* translators: %s: Date of privacy policy text update. */
$badge_title = sprintf( __( 'Updated %s.' ), $date );
}
$plugin_name = esc_html( $section['plugin_name'] );
?>
' . __( 'Suggested text:' ) . ' ';
$content = '';
$strings = array();
// Start of the suggested privacy policy text.
if ( $description ) {
$strings[] = '';
}
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Who we are' ) . ' ';
if ( $description ) {
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this section you should note your site URL, as well as the name of the company, organization, or individual behind it, and some accurate contact information.' ) . '
';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'The amount of information you may be required to show will vary depending on your local or national business regulations. You may, for example, be required to display a physical address, a registered address, or your company registration number.' ) . '
';
} else {
/* translators: Default privacy policy text. %s: Site URL. */
$strings[] = '
' . $suggested_text . sprintf( __( 'Our website address is: %s.' ), get_bloginfo( 'url', 'display' ) ) . '
';
}
if ( $description ) {
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'What personal data we collect and why we collect it' ) . ' ';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this section you should note what personal data you collect from users and site visitors. This may include personal data, such as name, email address, personal account preferences; transactional data, such as purchase information; and technical data, such as information about cookies.' ) . '
';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'You should also note any collection and retention of sensitive personal data, such as data concerning health.' ) . '
';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In addition to listing what personal data you collect, you need to note why you collect it. These explanations must note either the legal basis for your data collection and retention or the active consent the user has given.' ) . '
';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'Personal data is not just created by a user’s interactions with your site. Personal data is also generated from technical processes such as contact forms, comments, cookies, analytics, and third party embeds.' ) . '
';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'By default WordPress does not collect any personal data about visitors, and only collects the data shown on the User Profile screen from registered users. However some of your plugins may collect personal data. You should add the relevant information below.' ) . '
';
}
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Comments' ) . ' ';
if ( $description ) {
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this subsection you should note what information is captured through comments. We have noted the data which WordPress collects by default.' ) . '
';
} else {
/* translators: Default privacy policy text. */
$strings[] = '
' . $suggested_text . __( 'When visitors leave comments on the site we collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.' ) . '
';
/* translators: Default privacy policy text. */
$strings[] = '
' . __( 'An anonymized string created from your email address (also called a hash) may be provided to the Gravatar service to see if you are using it. The Gravatar service privacy policy is available here: https://automattic.com/privacy/. After approval of your comment, your profile picture is visible to the public in the context of your comment.' ) . '
';
}
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Media' ) . ' ';
if ( $description ) {
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this subsection you should note what information may be disclosed by users who can upload media files. All uploaded files are usually publicly accessible.' ) . '
';
} else {
/* translators: Default privacy policy text. */
$strings[] = '
' . $suggested_text . __( 'If you upload images to the website, you should avoid uploading images with embedded location data (EXIF GPS) included. Visitors to the website can download and extract any location data from images on the website.' ) . '
';
}
if ( $description ) {
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Contact forms' ) . ' ';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'By default, WordPress does not include a contact form. If you use a contact form plugin, use this subsection to note what personal data is captured when someone submits a contact form, and how long you keep it. For example, you may note that you keep contact form submissions for a certain period for customer service purposes, but you do not use the information submitted through them for marketing purposes.' ) . '
';
}
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Cookies' ) . ' ';
if ( $description ) {
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this subsection you should list the cookies your website uses, including those set by your plugins, social media, and analytics. We have provided the cookies which WordPress installs by default.' ) . '
';
} else {
/* translators: Default privacy policy text. */
$strings[] = '
' . $suggested_text . __( 'If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment. These cookies will last for one year.' ) . '
';
/* translators: Default privacy policy text. */
$strings[] = '
' . __( 'If you visit our login page, we will set a temporary cookie to determine if your browser accepts cookies. This cookie contains no personal data and is discarded when you close your browser.' ) . '
';
/* translators: Default privacy policy text. */
$strings[] = '
' . __( 'When you log in, we will also set up several cookies to save your login information and your screen display choices. Login cookies last for two days, and screen options cookies last for a year. If you select "Remember Me", your login will persist for two weeks. If you log out of your account, the login cookies will be removed.' ) . '
';
/* translators: Default privacy policy text. */
$strings[] = '
' . __( 'If you edit or publish an article, an additional cookie will be saved in your browser. This cookie includes no personal data and simply indicates the post ID of the article you just edited. It expires after 1 day.' ) . '
';
}
if ( ! $description ) {
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Embedded content from other websites' ) . ' ';
/* translators: Default privacy policy text. */
$strings[] = '
' . $suggested_text . __( 'Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.' ) . '
';
/* translators: Default privacy policy text. */
$strings[] = '
' . __( 'These websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content, including tracking your interaction with the embedded content if you have an account and are logged in to that website.' ) . '
';
}
if ( $description ) {
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Analytics' ) . ' ';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this subsection you should note what analytics package you use, how users can opt out of analytics tracking, and a link to your analytics provider’s privacy policy, if any.' ) . '
';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'By default WordPress does not collect any analytics data. However, many web hosting accounts collect some anonymous analytics data. You may also have installed a WordPress plugin that provides analytics services. In that case, add information from that plugin here.' ) . '
';
}
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Who we share your data with' ) . ' ';
if ( $description ) {
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this section you should name and list all third party providers with whom you share site data, including partners, cloud-based services, payment processors, and third party service providers, and note what data you share with them and why. Link to their own privacy policies if possible.' ) . '
';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'By default WordPress does not share any personal data with anyone.' ) . '
';
} else {
/* translators: Default privacy policy text. */
$strings[] = '
' . $suggested_text . __( 'If you request a password reset, your IP address will be included in the reset email.' ) . '
';
}
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'How long we retain your data' ) . ' ';
if ( $description ) {
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this section you should explain how long you retain personal data collected or processed by the website. While it is your responsibility to come up with the schedule of how long you keep each dataset for and why you keep it, that information does need to be listed here. For example, you may want to say that you keep contact form entries for six months, analytics records for a year, and customer purchase records for ten years.' ) . '
';
} else {
/* translators: Default privacy policy text. */
$strings[] = '
' . $suggested_text . __( 'If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.' ) . '
';
/* translators: Default privacy policy text. */
$strings[] = '
' . __( 'For users that register on our website (if any), we also store the personal information they provide in their user profile. All users can see, edit, or delete their personal information at any time (except they cannot change their username). Website administrators can also see and edit that information.' ) . '
';
}
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'What rights you have over your data' ) . ' ';
if ( $description ) {
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this section you should explain what rights your users have over their data and how they can invoke those rights.' ) . '
';
} else {
/* translators: Default privacy policy text. */
$strings[] = '
' . $suggested_text . __( 'If you have an account on this site, or have left comments, you can request to receive an exported file of the personal data we hold about you, including any data you have provided to us. You can also request that we erase any personal data we hold about you. This does not include any data we are obliged to keep for administrative, legal, or security purposes.' ) . '
';
}
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Where your data is sent' ) . ' ';
if ( $description ) {
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this section you should list all transfers of your site data outside the European Union and describe the means by which that data is safeguarded to European data protection standards. This could include your web hosting, cloud storage, or other third party services.' ) . '
';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'European data protection law requires data about European residents which is transferred outside the European Union to be safeguarded to the same standards as if the data was in Europe. So in addition to listing where data goes, you should describe how you ensure that these standards are met either by yourself or by your third party providers, whether that is through an agreement such as Privacy Shield, model clauses in your contracts, or binding corporate rules.' ) . '
';
} else {
/* translators: Default privacy policy text. */
$strings[] = '
' . $suggested_text . __( 'Visitor comments may be checked through an automated spam detection service.' ) . '
';
}
if ( $description ) {
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Contact information' ) . ' ';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this section you should provide a contact method for privacy-specific concerns. If you are required to have a Data Protection Officer, list their name and full contact details here as well.' ) . '
';
}
if ( $description ) {
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Additional information' ) . ' ';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'If you use your site for commercial purposes and you engage in more complex collection or processing of personal data, you should note the following information in your privacy policy in addition to the information we have already discussed.' ) . '
';
}
if ( $description ) {
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'How we protect your data' ) . ' ';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this section you should explain what measures you have taken to protect your users’ data. This could include technical measures such as encryption; security measures such as two factor authentication; and measures such as staff training in data protection. If you have carried out a Privacy Impact Assessment, you can mention it here too.' ) . '
';
}
if ( $description ) {
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'What data breach procedures we have in place' ) . ' ';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'In this section you should explain what procedures you have in place to deal with data breaches, either potential or real, such as internal reporting systems, contact mechanisms, or bug bounties.' ) . '
';
}
if ( $description ) {
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'What third parties we receive data from' ) . ' ';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'If your website receives data about users from third parties, including advertisers, this information must be included within the section of your privacy policy dealing with third party data.' ) . '
';
}
if ( $description ) {
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'What automated decision making and/or profiling we do with user data' ) . ' ';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'If your website provides a service which includes automated decision making - for example, allowing customers to apply for credit, or aggregating their data into an advertising profile - you must note that this is taking place, and include information about how that information is used, what decisions are made with that aggregated data, and what rights users have over decisions made without human intervention.' ) . '
';
}
if ( $description ) {
/* translators: Default privacy policy heading. */
$strings[] = '
' . __( 'Industry regulatory disclosure requirements' ) . ' ';
/* translators: Privacy policy tutorial. */
$strings[] = '
' . __( 'If you are a member of a regulated industry, or if you are subject to additional privacy laws, you may be required to disclose that information here.' ) . '
';
$strings[] = '
';
}
if ( $blocks ) {
foreach ( $strings as $key => $string ) {
if ( str_starts_with( $string, '' ) ) {
$strings[ $key ] = "\n" . $string . "\n\n";
}
if ( str_starts_with( $string, '
\n" . $string . "\n\n";
}
}
}
$content = implode( '', $strings );
// End of the suggested privacy policy text.
/**
* Filters the default content suggested for inclusion in a privacy policy.
*
* @since 4.9.6
* @since 5.0.0 Added the `$strings`, `$description`, and `$blocks` parameters.
* @deprecated 5.7.0 Use wp_add_privacy_policy_content() instead.
*
* @param string $content The default policy content.
* @param string[] $strings An array of privacy policy content strings.
* @param bool $description Whether policy descriptions should be included.
* @param bool $blocks Whether the content should be formatted for the block editor.
*/
return apply_filters_deprecated(
'wp_get_default_privacy_policy_content',
array( $content, $strings, $description, $blocks ),
'5.7.0',
'wp_add_privacy_policy_content()'
);
}
/**
* Adds the suggested privacy policy text to the policy postbox.
*
* @since 4.9.6
*/
public static function add_suggested_content() {
$content = self::get_default_content( false, false );
wp_add_privacy_policy_content( __( 'WordPress' ), $content );
}
}
screen.php 0000644 00000014366 15220264612 0006546 0 ustar 00 id ] ) ) {
/**
* Filters the column headers for a list table on a specific screen.
*
* The dynamic portion of the hook name, `$screen->id`, refers to the
* ID of a specific screen. For example, the screen ID for the Posts
* list table is edit-post, so the filter for that screen would be
* manage_edit-post_columns.
*
* @since 3.0.0
*
* @param string[] $columns The column header labels keyed by column ID.
*/
$column_headers[ $screen->id ] = apply_filters( "manage_{$screen->id}_columns", array() );
}
return $column_headers[ $screen->id ];
}
/**
* Get a list of hidden columns.
*
* @since 2.7.0
*
* @param string|WP_Screen $screen The screen you want the hidden columns for
* @return string[] Array of IDs of hidden columns.
*/
function get_hidden_columns( $screen ) {
if ( is_string( $screen ) ) {
$screen = convert_to_screen( $screen );
}
$hidden = get_user_option( 'manage' . $screen->id . 'columnshidden' );
$use_defaults = ! is_array( $hidden );
if ( $use_defaults ) {
$hidden = array();
/**
* Filters the default list of hidden columns.
*
* @since 4.4.0
*
* @param string[] $hidden Array of IDs of columns hidden by default.
* @param WP_Screen $screen WP_Screen object of the current screen.
*/
$hidden = apply_filters( 'default_hidden_columns', $hidden, $screen );
}
/**
* Filters the list of hidden columns.
*
* @since 4.4.0
* @since 4.4.1 Added the `use_defaults` parameter.
*
* @param string[] $hidden Array of IDs of hidden columns.
* @param WP_Screen $screen WP_Screen object of the current screen.
* @param bool $use_defaults Whether to show the default columns.
*/
return apply_filters( 'hidden_columns', $hidden, $screen, $use_defaults );
}
/**
* Prints the meta box preferences for screen meta.
*
* @since 2.7.0
*
* @global array $wp_meta_boxes Global meta box state.
*
* @param WP_Screen $screen
*/
function meta_box_prefs( $screen ) {
global $wp_meta_boxes;
if ( is_string( $screen ) ) {
$screen = convert_to_screen( $screen );
}
if ( empty( $wp_meta_boxes[ $screen->id ] ) ) {
return;
}
$hidden = get_hidden_meta_boxes( $screen );
foreach ( array_keys( $wp_meta_boxes[ $screen->id ] ) as $context ) {
foreach ( array( 'high', 'core', 'default', 'low' ) as $priority ) {
if ( ! isset( $wp_meta_boxes[ $screen->id ][ $context ][ $priority ] ) ) {
continue;
}
foreach ( $wp_meta_boxes[ $screen->id ][ $context ][ $priority ] as $box ) {
if ( false === $box || ! $box['title'] ) {
continue;
}
// Submit box cannot be hidden.
if ( 'submitdiv' === $box['id'] || 'linksubmitdiv' === $box['id'] ) {
continue;
}
$widget_title = $box['title'];
if ( is_array( $box['args'] ) && isset( $box['args']['__widget_basename'] ) ) {
$widget_title = $box['args']['__widget_basename'];
}
$is_hidden = in_array( $box['id'], $hidden, true );
printf(
' %3$s ',
esc_attr( $box['id'] ),
checked( $is_hidden, false, false ),
$widget_title
);
}
}
}
}
/**
* Gets an array of IDs of hidden meta boxes.
*
* @since 2.7.0
*
* @param string|WP_Screen $screen Screen identifier
* @return string[] IDs of hidden meta boxes.
*/
function get_hidden_meta_boxes( $screen ) {
if ( is_string( $screen ) ) {
$screen = convert_to_screen( $screen );
}
$hidden = get_user_option( "metaboxhidden_{$screen->id}" );
$use_defaults = ! is_array( $hidden );
// Hide slug boxes by default.
if ( $use_defaults ) {
$hidden = array();
if ( 'post' === $screen->base ) {
if ( in_array( $screen->post_type, array( 'post', 'page', 'attachment' ), true ) ) {
$hidden = array( 'slugdiv', 'trackbacksdiv', 'postcustom', 'postexcerpt', 'commentstatusdiv', 'commentsdiv', 'authordiv', 'revisionsdiv' );
} else {
$hidden = array( 'slugdiv' );
}
}
/**
* Filters the default list of hidden meta boxes.
*
* @since 3.1.0
*
* @param string[] $hidden An array of IDs of meta boxes hidden by default.
* @param WP_Screen $screen WP_Screen object of the current screen.
*/
$hidden = apply_filters( 'default_hidden_meta_boxes', $hidden, $screen );
}
/**
* Filters the list of hidden meta boxes.
*
* @since 3.3.0
*
* @param string[] $hidden An array of IDs of hidden meta boxes.
* @param WP_Screen $screen WP_Screen object of the current screen.
* @param bool $use_defaults Whether to show the default meta boxes.
* Default true.
*/
return apply_filters( 'hidden_meta_boxes', $hidden, $screen, $use_defaults );
}
/**
* Register and configure an admin screen option
*
* @since 3.1.0
*
* @param string $option An option name.
* @param mixed $args Option-dependent arguments.
*/
function add_screen_option( $option, $args = array() ) {
$current_screen = get_current_screen();
if ( ! $current_screen ) {
return;
}
$current_screen->add_option( $option, $args );
}
/**
* Get the current screen object
*
* @since 3.1.0
*
* @global WP_Screen $current_screen WordPress current screen object.
*
* @return WP_Screen|null Current screen object or null when screen not defined.
*/
function get_current_screen() {
global $current_screen;
if ( ! $current_screen instanceof WP_Screen ) {
return null;
}
return $current_screen;
}
/**
* Set the current screen object
*
* @since 3.0.0
*
* @param string|WP_Screen $hook_name Optional. The hook name (also known as the hook suffix) used to determine the screen,
* or an existing screen object.
*/
function set_current_screen( $hook_name = '' ) {
WP_Screen::get( $hook_name )->set_current_screen();
}
template.php 0000644 00000302544 15220264612 0007100 0 ustar 00 'category',
'descendants_and_self' => $descendants_and_self,
'selected_cats' => $selected_cats,
'popular_cats' => $popular_cats,
'walker' => $walker,
'checked_ontop' => $checked_ontop,
)
);
}
/**
* Outputs an unordered list of checkbox input elements labelled with term names.
*
* Taxonomy-independent version of wp_category_checklist().
*
* @since 3.0.0
* @since 4.4.0 Introduced the `$echo` argument.
*
* @param int $post_id Optional. Post ID. Default 0.
* @param array|string $args {
* Optional. Array or string of arguments for generating a terms checklist. Default empty array.
*
* @type int $descendants_and_self ID of the category to output along with its descendants.
* Default 0.
* @type int[] $selected_cats Array of category IDs to mark as checked. Default false.
* @type int[] $popular_cats Array of category IDs to receive the "popular-category" class.
* Default false.
* @type Walker $walker Walker object to use to build the output. Default empty which
* results in a Walker_Category_Checklist instance being used.
* @type string $taxonomy Taxonomy to generate the checklist for. Default 'category'.
* @type bool $checked_ontop Whether to move checked items out of the hierarchy and to
* the top of the list. Default true.
* @type bool $echo Whether to echo the generated markup. False to return the markup instead
* of echoing it. Default true.
* }
* @return string HTML list of input elements.
*/
function wp_terms_checklist( $post_id = 0, $args = array() ) {
$defaults = array(
'descendants_and_self' => 0,
'selected_cats' => false,
'popular_cats' => false,
'walker' => null,
'taxonomy' => 'category',
'checked_ontop' => true,
'echo' => true,
);
/**
* Filters the taxonomy terms checklist arguments.
*
* @since 3.4.0
*
* @see wp_terms_checklist()
*
* @param array|string $args An array or string of arguments.
* @param int $post_id The post ID.
*/
$params = apply_filters( 'wp_terms_checklist_args', $args, $post_id );
$parsed_args = wp_parse_args( $params, $defaults );
if ( empty( $parsed_args['walker'] ) || ! ( $parsed_args['walker'] instanceof Walker ) ) {
$walker = new Walker_Category_Checklist();
} else {
$walker = $parsed_args['walker'];
}
$taxonomy = $parsed_args['taxonomy'];
$descendants_and_self = (int) $parsed_args['descendants_and_self'];
$args = array( 'taxonomy' => $taxonomy );
$tax = get_taxonomy( $taxonomy );
$args['disabled'] = ! current_user_can( $tax->cap->assign_terms );
$args['list_only'] = ! empty( $parsed_args['list_only'] );
if ( is_array( $parsed_args['selected_cats'] ) ) {
$args['selected_cats'] = array_map( 'intval', $parsed_args['selected_cats'] );
} elseif ( $post_id ) {
$args['selected_cats'] = wp_get_object_terms( $post_id, $taxonomy, array_merge( $args, array( 'fields' => 'ids' ) ) );
} else {
$args['selected_cats'] = array();
}
if ( is_array( $parsed_args['popular_cats'] ) ) {
$args['popular_cats'] = array_map( 'intval', $parsed_args['popular_cats'] );
} else {
$args['popular_cats'] = get_terms(
array(
'taxonomy' => $taxonomy,
'fields' => 'ids',
'orderby' => 'count',
'order' => 'DESC',
'number' => 10,
'hierarchical' => false,
)
);
}
if ( $descendants_and_self ) {
$categories = (array) get_terms(
array(
'taxonomy' => $taxonomy,
'child_of' => $descendants_and_self,
'hierarchical' => 0,
'hide_empty' => 0,
)
);
$self = get_term( $descendants_and_self, $taxonomy );
array_unshift( $categories, $self );
} else {
$categories = (array) get_terms(
array(
'taxonomy' => $taxonomy,
'get' => 'all',
)
);
}
$output = '';
if ( $parsed_args['checked_ontop'] ) {
/*
* Post-process $categories rather than adding an exclude to the get_terms() query
* to keep the query the same across all posts (for any query cache).
*/
$checked_categories = array();
$keys = array_keys( $categories );
foreach ( $keys as $k ) {
if ( in_array( $categories[ $k ]->term_id, $args['selected_cats'], true ) ) {
$checked_categories[] = $categories[ $k ];
unset( $categories[ $k ] );
}
}
// Put checked categories on top.
$output .= $walker->walk( $checked_categories, 0, $args );
}
// Then the rest of them.
$output .= $walker->walk( $categories, 0, $args );
if ( $parsed_args['echo'] ) {
echo $output;
}
return $output;
}
/**
* Retrieves a list of the most popular terms from the specified taxonomy.
*
* If the `$display` argument is true then the elements for a list of checkbox
* ` ` elements labelled with the names of the selected terms is output.
* If the `$post_ID` global is not empty then the terms associated with that
* post will be marked as checked.
*
* @since 2.5.0
*
* @param string $taxonomy Taxonomy to retrieve terms from.
* @param int $default_term Optional. Not used.
* @param int $number Optional. Number of terms to retrieve. Default 10.
* @param bool $display Optional. Whether to display the list as well. Default true.
* @return int[] Array of popular term IDs.
*/
function wp_popular_terms_checklist( $taxonomy, $default_term = 0, $number = 10, $display = true ) {
$post = get_post();
if ( $post && $post->ID ) {
$checked_terms = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'ids' ) );
} else {
$checked_terms = array();
}
$terms = get_terms(
array(
'taxonomy' => $taxonomy,
'orderby' => 'count',
'order' => 'DESC',
'number' => $number,
'hierarchical' => false,
)
);
$tax = get_taxonomy( $taxonomy );
$popular_ids = array();
foreach ( (array) $terms as $term ) {
$popular_ids[] = $term->term_id;
if ( ! $display ) { // Hack for Ajax use.
continue;
}
$id = "popular-$taxonomy-$term->term_id";
$checked = in_array( $term->term_id, $checked_terms, true ) ? 'checked="checked"' : '';
?>
value="term_id; ?>" cap->assign_terms ) ); ?> />
name, '', '' ) );
?>
'link_category',
'orderby' => 'name',
'hide_empty' => 0,
)
);
if ( empty( $categories ) ) {
return;
}
foreach ( $categories as $category ) {
$cat_id = $category->term_id;
/** This filter is documented in wp-includes/category-template.php */
$name = esc_html( apply_filters( 'the_category', $category->name, '', '' ) );
$checked = in_array( $cat_id, $checked_categories, true ) ? ' checked="checked"' : '';
echo ' ', $name, ' ';
}
}
/**
* Adds hidden fields with the data for use in the inline editor for posts and pages.
*
* @since 2.7.0
*
* @param WP_Post $post Post object.
*/
function get_inline_data( $post ) {
$post_type_object = get_post_type_object( $post->post_type );
if ( ! current_user_can( 'edit_post', $post->ID ) ) {
return;
}
$title = esc_textarea( trim( $post->post_title ) );
echo '
' . $title . '
' .
/** This filter is documented in wp-admin/edit-tag-form.php */
'
' . apply_filters( 'editable_slug', $post->post_name, $post ) . '
' . $post->post_author . '
' . esc_html( $post->ping_status ) . '
' . esc_html( $post->post_status ) . '
' . mysql2date( 'd', $post->post_date, false ) . '
' . mysql2date( 'm', $post->post_date, false ) . '
' . mysql2date( 'Y', $post->post_date, false ) . '
' . mysql2date( 'H', $post->post_date, false ) . '
' . mysql2date( 'i', $post->post_date, false ) . '
' . mysql2date( 's', $post->post_date, false ) . '
' . esc_html( $post->post_password ) . '
';
if ( $post_type_object->hierarchical ) {
echo '
' . $post->post_parent . '
';
}
echo '
' . ( $post->page_template ? esc_html( $post->page_template ) : 'default' ) . '
';
if ( post_type_supports( $post->post_type, 'page-attributes' ) ) {
echo '';
}
$taxonomy_names = get_object_taxonomies( $post->post_type );
foreach ( $taxonomy_names as $taxonomy_name ) {
$taxonomy = get_taxonomy( $taxonomy_name );
if ( ! $taxonomy->show_in_quick_edit ) {
continue;
}
if ( $taxonomy->hierarchical ) {
$terms = get_object_term_cache( $post->ID, $taxonomy_name );
if ( false === $terms ) {
$terms = wp_get_object_terms( $post->ID, $taxonomy_name );
wp_cache_add( $post->ID, wp_list_pluck( $terms, 'term_id' ), $taxonomy_name . '_relationships' );
}
$term_ids = empty( $terms ) ? array() : wp_list_pluck( $terms, 'term_id' );
echo '
' . implode( ',', $term_ids ) . '
';
} else {
$terms_to_edit = get_terms_to_edit( $post->ID, $taxonomy_name );
if ( ! is_string( $terms_to_edit ) ) {
$terms_to_edit = '';
}
echo '
'
. esc_html( str_replace( ',', ', ', $terms_to_edit ) ) . '
';
}
}
if ( ! $post_type_object->hierarchical ) {
echo '
' . ( is_sticky( $post->ID ) ? 'sticky' : '' ) . '
';
}
if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
echo '
' . esc_html( get_post_format( $post->ID ) ) . '
';
}
/**
* Fires after outputting the fields for the inline editor for posts and pages.
*
* @since 4.9.8
*
* @param WP_Post $post The current post object.
* @param WP_Post_Type $post_type_object The current post's post type object.
*/
do_action( 'add_inline_data', $post, $post_type_object );
echo '
';
}
/**
* Outputs the in-line comment reply-to form in the Comments list table.
*
* @since 2.7.0
*
* @global WP_List_Table $wp_list_table
*
* @param int $position Optional. The value of the 'position' input field. Default 1.
* @param bool $checkbox Optional. The value of the 'checkbox' input field. Default false.
* @param string $mode Optional. If set to 'single', will use WP_Post_Comments_List_Table,
* otherwise WP_Comments_List_Table. Default 'single'.
* @param bool $table_row Optional. Whether to use a table instead of a div element. Default true.
*/
function wp_comment_reply( $position = 1, $checkbox = false, $mode = 'single', $table_row = true ) {
global $wp_list_table;
/**
* Filters the in-line comment reply-to form output in the Comments
* list table.
*
* Returning a non-empty value here will short-circuit display
* of the in-line comment-reply form in the Comments list table,
* echoing the returned value instead.
*
* @since 2.7.0
*
* @see wp_comment_reply()
*
* @param string $content The reply-to form content.
* @param array $args An array of default args.
*/
$content = apply_filters(
'wp_comment_reply',
'',
array(
'position' => $position,
'checkbox' => $checkbox,
'mode' => $mode,
)
);
if ( ! empty( $content ) ) {
echo $content;
return;
}
if ( ! $wp_list_table ) {
if ( 'single' === $mode ) {
$wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table' );
} else {
$wp_list_table = _get_list_table( 'WP_Comments_List_Table' );
}
}
?>