PluginProbe ʕ •ᴥ•ʔ
Permalink Manager Lite / 2.5.1.2
Permalink Manager Lite v2.5.1.2
2.6.0 2.5.5 2.5.4 2.5.3.4 2.2.18 2.2.19.2 2.2.19.3 2.2.19.3.1 2.2.2 2.2.20 2.2.20.1 2.2.20.3 2.2.4 2.2.5 2.2.6 2.2.7.2 2.2.7.3 2.2.7.5 2.2.7.6 2.2.8.4 2.2.8.5 2.2.8.6 2.2.8.7 2.2.8.9 2.2.9.1 2.2.9.2 2.2.9.2.1 2.2.9.3 2.2.9.4 2.2.9.6 2.2.9.7 2.2.9.9 2.3.0 2.3.1.1 2.4.0 2.4.1 2.4.1.2 2.4.1.3 2.4.1.4 2.4.1.5 2.4.1.6 2.4.2 2.4.2.1 2.4.3 2.4.3.1 2.4.3.2 2.4.3.3 2.4.3.4 2.4.4 2.4.4.1 2.4.4.2 2.4.4.3 2.5.0 2.5.1 2.5.1.1 2.5.1.2 2.5.1.3 2.5.1.4 2.5.2 2.5.2.1 2.5.2.2 2.5.2.3 2.5.2.4 2.5.3 2.5.3.1 2.5.3.2 2.5.3.3 trunk 0.2 0.3 0.3.1 0.3.2 0.3.3 0.3.4 0.4 0.4.1 0.4.2 0.4.3 0.4.4 0.4.6 0.4.7 0.4.8 0.4.9 0.5.3 0.5.4 1.0.0 1.0.1 1.0.4 1.1.0 1.1.1 1.1.2 1.11.6.3 2.0.0 2.0.3 2.0.4 2.0.4.3 2.0.5.1 2.0.5.2 2.0.5.3 2.0.5.3.1 2.0.5.4 2.0.5.4a 2.0.5.5 2.0.5.6 2.0.5.6.1 2.0.5.7 2.0.5.9a 2.0.6.2.1 2.0.6.2a 2.0.6.3 2.1.0 2.1.1 2.1.2.4 2.2.0 2.2.1.1 2.2.1.2 2.2.11 2.2.12 2.2.13.1 2.2.14 2.2.15.1 2.2.16 2.2.17
permalink-manager / includes / integrations / permalink-manager-woocommerce.php
permalink-manager / includes / integrations Last commit date
permalink-manager-language-plugins.php 1 year ago permalink-manager-seo-plugins.php 1 year ago permalink-manager-third-parties.php 1 year ago permalink-manager-woocommerce.php 1 year ago
permalink-manager-woocommerce.php
410 lines
1 <?php
2
3 /**
4 * WooCommerce plugins integration
5 */
6 class Permalink_Manager_WooCommerce {
7
8 public function __construct() {
9 add_action( 'init', array( $this, 'init_hooks' ), 99 );
10 add_action( 'plugins_loaded', array( $this, 'init_early_hooks' ), 99 );
11 }
12
13 /**
14 * Add support for SEO plugins using their hooks
15 */
16 function init_hooks() {
17 if ( class_exists( 'WooCommerce' ) ) {
18 add_filter( 'permalink_manager_filter_query', array( $this, 'woocommerce_detect' ), 8, 5 );
19 add_filter( 'template_redirect', array( $this, 'woocommerce_checkout_fix' ), 9 );
20
21 if ( class_exists( 'Permalink_Manager_Pro_Functions' ) ) {
22 if ( empty( $permalink_manager_options['general']['partial_disable']['post_types'] ) || ! in_array( 'shop_coupon', $permalink_manager_options['general']['partial_disable']['post_types'] ) ) {
23 if ( is_admin() ) {
24 add_filter( 'woocommerce_coupon_data_tabs', 'Permalink_Manager_Pro_Functions::woocommerce_coupon_tabs' );
25 add_action( 'woocommerce_coupon_data_panels', 'Permalink_Manager_Pro_Functions::woocommerce_coupon_panel' );
26 add_action( 'woocommerce_coupon_options_save', 'Permalink_Manager_Pro_Functions::woocommerce_save_coupon_uri', 9, 2 );
27 }
28
29 add_filter( 'request', 'Permalink_Manager_Pro_Functions::woocommerce_detect_coupon_code', 1, 1 );
30 }
31 } else {
32 add_filter( 'permalink_manager_disabled_post_types', array( $this, 'woocommerce_coupon_uris' ), 9, 1 );
33 }
34
35 // WooCommerce Import/Export
36 add_filter( 'woocommerce_product_export_product_default_columns', array( $this, 'woocommerce_csv_custom_uri_column' ), 9 );
37 add_filter( 'woocommerce_product_export_product_column_custom_uri', array( $this, 'woocommerce_export_custom_uri_value' ), 9, 3 );
38
39 add_filter( 'woocommerce_csv_product_import_mapping_options', array( $this, 'woocommerce_csv_custom_uri_column' ), 9 );
40 add_filter( 'woocommerce_csv_product_import_mapping_default_columns', array( $this, 'woocommerce_csv_custom_uri_column' ), 9 );
41 add_action( 'woocommerce_product_import_inserted_product_object', array( $this, 'woocommerce_csv_import_custom_uri' ), 9, 2 );
42
43 add_action( 'woocommerce_product_duplicate', array( $this, 'woocommerce_generate_permalinks_after_duplicate' ), 9, 2 );
44 add_filter( 'permalink_manager_filter_default_post_uri', array( $this, 'woocommerce_product_attributes' ), 5, 5 );
45
46 if ( wp_doing_ajax() && class_exists( 'SitePress' ) ) {
47 add_filter( 'permalink_manager_filter_final_post_permalink', array( $this, 'woocommerce_translate_ajax_fragments_urls' ), 9999, 3 );
48 }
49
50 // Make sure that the custom permalinks are not generated prematurely
51 add_filter( 'woocommerce_rest_pre_insert_product_object', array( $this, 'woocommerce_delay_product_custom_permalink' ), 10, 3 );
52 add_action( 'woocommerce_rest_insert_product_object', array( $this, 'woocommerce_set_custom_uri_after_rest_insert' ), 20, 3 );
53 }
54
55 // WooCommerce Wishlist Plugin
56 if ( function_exists( 'tinv_get_option' ) ) {
57 add_filter( 'permalink_manager_detect_uri', array( $this, 'ti_woocommerce_wishlist_uris' ), 15, 3 );
58 }
59
60 // WooCommerce Subscriptions
61 if ( class_exists( 'WC_Subscriptions' ) ) {
62 add_filter( 'permalink_manager_filter_final_post_permalink', array( $this, 'wcs_fix_subscription_links' ), 10, 3 );
63 }
64 }
65
66 /**
67 * Some hooks must be called shortly after all the plugins are loaded
68 */
69 public function init_early_hooks() {
70 if ( class_exists( 'WooCommerce' ) ) {
71 add_filter( 'woocommerce_get_endpoint_url', array( 'Permalink_Manager_Core_Functions', 'control_trailing_slashes' ), 9 );
72 add_action( 'before_woocommerce_init', array( $this, 'woocommerce_declare_compatibility' ) );
73 }
74 }
75
76 /**
77 * Fix query on WooCommerce shop page & disable the canonical redirect if WooCommerce query variables are set
78 */
79 function woocommerce_detect( $query, $old_query, $uri_parts, $pm_query, $content_type ) {
80 global $woocommerce, $pm_query;
81
82 $shop_page_id = get_option( 'woocommerce_shop_page_id' );
83
84 // WPML - translate shop page id
85 $shop_page_id = apply_filters( 'wpml_object_id', $shop_page_id, 'page', true );
86
87 // Fix shop page
88 if ( get_theme_support( 'woocommerce' ) && ! empty( $pm_query['id'] ) && is_numeric( $pm_query['id'] ) && $shop_page_id == $pm_query['id'] ) {
89 $query['post_type'] = 'product';
90 unset( $query['pagename'] );
91 }
92
93 // Fix WooCommerce pages
94 if ( ! empty( $woocommerce->query->query_vars ) ) {
95 $query_vars = $woocommerce->query->query_vars;
96
97 foreach ( $query_vars as $key => $val ) {
98 if ( isset( $query[ $key ] ) ) {
99 $query['do_not_redirect'] = 1;
100 break;
101 }
102 }
103 }
104
105 return $query;
106 }
107
108 /**
109 * Redirects the user from the Shop archive to the Shop page if the user is not searching for anything
110 * Disable canonical redirect on "thank you" & another WooCommerce pages
111 */
112 function woocommerce_checkout_fix() {
113 global $wp_query, $pm_query, $permalink_manager_options;
114
115 // Redirect from Shop archive to selected page
116 if ( is_shop() && empty( $pm_query['id'] ) ) {
117 $redirect_mode = ( ! empty( $permalink_manager_options['general']['redirect'] ) ) ? $permalink_manager_options['general']['redirect'] : false;
118 $redirect_shop = apply_filters( 'permalink_manager_redirect_shop_archive', false );
119 $shop_page = get_option( 'woocommerce_shop_page_id' );
120
121 if ( $redirect_mode && $redirect_shop && $shop_page && empty( $wp_query->query_vars['s'] ) ) {
122 $shop_url = get_permalink( $shop_page );
123 wp_safe_redirect( $shop_url, $redirect_mode );
124 exit();
125 }
126 }
127
128 if ( is_checkout() || ( function_exists( 'is_wc_endpoint_url' ) && is_wc_endpoint_url() ) ) {
129 $wp_query->query_vars['do_not_redirect'] = 1;
130 }
131 }
132
133 /**
134 * Add "Coupons" to the list of excluded post types
135 *
136 * @param array $post_types
137 *
138 * @return array
139 */
140 public function woocommerce_coupon_uris( $post_types ) {
141 if ( is_array( $post_types ) ) {
142 $post_types[] = 'shop_coupon';
143 }
144
145 return $post_types;
146 }
147
148 /**
149 * Generate a new custom permalink for duplicated product
150 *
151 * @param WC_Product $new_product The new product object.
152 * @param WC_Product $old_product The product that was duplicated.
153 */
154 function woocommerce_generate_permalinks_after_duplicate( $new_product, $old_product ) {
155 if ( ! empty( $new_product ) ) {
156 $product_id = $new_product->get_id();
157
158 // Ignore variations
159 if ( $new_product->get_type() === 'variation' || Permalink_Manager_Helper_Functions::is_post_excluded( $product_id, true ) ) {
160 return;
161 }
162
163 $custom_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri( $product_id, false, true );
164 Permalink_Manager_URI_Functions::save_single_uri( $product_id, $custom_uri, false, true );
165 }
166 }
167
168 /**
169 * If the URI contains %pa_attribute_name% tag, replace it with the value of the attribute
170 *
171 * @param string $default_uri The default custom permalink that WordPress would use for the post.
172 * @param string $slug The post slug.
173 * @param WP_Post $post The post object.
174 * @param string $post_name The post slug.
175 * @param bool $native_uri true if the URI is a native URI, false if it's a custom URI
176 *
177 * @return string The default custom permalink
178 */
179 function woocommerce_product_attributes( $default_uri, $slug, $post, $post_name, $native_uri ) {
180 // Do not affect native URIs
181 if ( $native_uri ) {
182 return $default_uri;
183 }
184
185 // Use only for products
186 if ( empty( $post->post_type ) || $post->post_type !== 'product' ) {
187 return $default_uri;
188 }
189
190 preg_match_all( "/%pa_(.[^\%]+)%/", $default_uri, $custom_fields );
191
192 if ( ! empty( $custom_fields[1] ) ) {
193 $product = wc_get_product( $post->ID );
194
195 foreach ( $custom_fields[1] as $i => $custom_field ) {
196 $attribute_name = sanitize_title( $custom_field );
197 $attribute_value = $product->get_attribute( $attribute_name );
198
199 $default_uri = str_replace( $custom_fields[0][ $i ], Permalink_Manager_Helper_Functions::sanitize_title( $attribute_value ), $default_uri );
200 }
201 }
202
203 return $default_uri;
204 }
205
206 /**
207 * Stop the plugin from generating the custom permalink for new WooCommerce product prematurely.
208 *
209 * @param WC_Data $data The product object.
210 * @param WP_REST_Request $request The REST API request object.
211 * @param bool $is_new_post Whether the product is newly created.
212 */
213 function woocommerce_delay_product_custom_permalink( $data, $request, $is_new_post ) {
214 add_filter( 'permalink_manager_pre_update_post_uri', '__return_null', 100 );
215
216 return $data;
217 }
218
219 /**
220 * Sets a custom permalink for a WooCommerce product after it is inserted via the REST API.
221 *
222 * @param WC_Product $product The product object.
223 * @param WP_REST_Request $request The REST API request object.
224 * @param bool $is_new_post Whether the product is newly created.
225 */
226 function woocommerce_set_custom_uri_after_rest_insert( $product, $request, $is_new_post ) {
227 if ( $is_new_post && class_exists( 'Permalink_Manager_URI_Functions_Post' ) && method_exists( $product, 'get_id' ) ) {
228 $product_id = $product->get_id();
229
230 remove_filter( 'permalink_manager_pre_update_post_uri', '__return_null', 100 );
231
232 $new_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri( $product_id );
233 Permalink_Manager_URI_Functions_Post::save_uri( $product_id, $new_uri, $is_new_post );
234 }
235 }
236
237 /**
238 * Check the current request is a WooCommerce AJAX request. If it is, check the translated page's URL should be returned
239 *
240 * @param string $permalink The full URL of the post
241 * @param WP_Post $post The post object
242 * @param string $old_permalink The original URL of the post.
243 *
244 * @return string The permalink is being returned.
245 */
246 function woocommerce_translate_ajax_fragments_urls( $permalink, $post, $old_permalink ) {
247 // Use it only if the permalinks are different
248 if ( $permalink == $old_permalink || $post->post_type !== 'page' ) {
249 return $permalink;
250 }
251
252 // A. Native WooCommerce AJAX events
253 if ( ! empty( $_REQUEST['wc-ajax'] ) ) {
254 $action = sanitize_title( $_REQUEST['wc-ajax'] );
255 } // B. Shoptimizer theme
256 else if ( ! empty( $_REQUEST['action'] ) ) {
257 $action = sanitize_title( $_REQUEST['action'] );
258 }
259
260 // Allowed action names
261 $allowed_actions = array( 'shoptimizer_pdp_ajax_atc', 'get_refreshed_fragments' );
262
263 if ( ! empty( $action ) && in_array( $action, $allowed_actions ) ) {
264 $translated_post_id = apply_filters( 'wpml_object_id', $post->ID, 'page' );
265 $permalink = ( $translated_post_id !== $post->ID ) ? get_permalink( $translated_post_id ) : $permalink;
266 }
267
268 return $permalink;
269 }
270
271 /**
272 * 4FA. Add a new column to the WooCommerce CSV Import/Export tool
273 *
274 * @param array $columns The array of columns to be displayed.
275 *
276 * @return array The $columns array.
277 */
278 function woocommerce_csv_custom_uri_column( $columns ) {
279 if ( ! is_array( $columns ) ) {
280 return $columns;
281 }
282
283 $label = __( 'Custom URI', 'permalink-manager' );
284 $key = 'custom_uri';
285
286 if ( current_filter() == 'woocommerce_csv_product_import_mapping_default_columns' ) {
287 $columns[ $label ] = $key;
288 } else {
289 $columns[ $key ] = $label;
290 }
291
292 return $columns;
293 }
294
295 /**
296 * 4FB. Return the custom permalink of the product if it exists, otherwise return the default URI
297 *
298 * @param string $value The value of the column.
299 * @param WC_Product $product The product object.
300 * @param mixed $column_id The column ID.
301 *
302 * @return string The custom permalink or default permalink
303 */
304 function woocommerce_export_custom_uri_value( $value, $product, $column_id ) {
305 if ( empty( $value ) && ! empty( $product ) ) {
306 $product_id = $product->get_id();
307
308 // Get custom permalink or default permalink
309 $value = Permalink_Manager_URI_Functions_Post::get_post_uri( $product_id );
310 }
311
312 return $value;
313 }
314
315 /**
316 * 4FC. Set the custom URI for the product using the value from CSV file, if not set use the default permalink
317 *
318 * @param WC_Product $product The product object.
319 * @param array $data The data array for the current row being imported.
320 */
321 function woocommerce_csv_import_custom_uri( $product, $data ) {
322 if ( ! empty( $product ) ) {
323 $product_id = $product->get_id();
324
325 // Ignore variations
326 if ( $product->get_type() == 'variation' ) {
327 return;
328 }
329
330 $current_uri = Permalink_Manager_URI_Functions::get_single_uri( $product_id, false, true );
331
332 // A. Use default permalink if "Custom URI" is not set and did not exist before
333 if ( empty( $current_uri ) && empty( $data['custom_uri'] ) ) {
334 $custom_uri = Permalink_Manager_URI_Functions_Post::get_default_post_uri( $product_id, false, true );
335 } else if ( ! empty( $data['custom_uri'] ) ) {
336 $custom_uri = Permalink_Manager_Helper_Functions::sanitize_title( $data['custom_uri'] );
337 } else {
338 return;
339 }
340
341 Permalink_Manager_URI_Functions::save_single_uri( $product_id, $custom_uri, false, true );
342 }
343 }
344
345 /**
346 * Declare support for 'High-Performance order storage (COT)' and other features in WooCommerce
347 */
348 function woocommerce_declare_compatibility() {
349 $features_util_class = '\Automattic\WooCommerce\Utilities\FeaturesUtil';
350
351 if ( class_exists( $features_util_class ) && method_exists( $features_util_class, 'declare_compatibility' ) ) {
352 $features = method_exists( $features_util_class, 'get_features' ) ? $features_util_class::get_features( true ) : array();
353
354 foreach ( array_keys( $features ) as $feature ) {
355 $features_util_class::declare_compatibility( $feature, PERMALINK_MANAGER_BASENAME );
356 }
357 }
358 }
359
360 /**
361 * Extract the Wishlist ID from the URI and add it to the $uri_parts array (WooCommerce Wishlist Plugin)
362 *
363 * @param array $uri_parts An array of the URI parts.
364 * @param string $request_url The URL that was requested.
365 * @param array $endpoints An array of all the endpoints that are currently registered.
366 *
367 * @return array The URI parts.
368 */
369 function ti_woocommerce_wishlist_uris( $uri_parts, $request_url, $endpoints ) {
370 $wishlist_pid = function_exists( 'tinv_get_option' ) ? tinv_get_option( 'general', 'page_wishlist' ) : '';
371
372 // Find the Wishlist page URI
373 if ( is_numeric( $wishlist_pid )) {
374 $current_uri = Permalink_Manager_URI_Functions::get_single_uri( $wishlist_pid, false, true );
375
376 if ( ! empty( $current_uri ) ) {
377 $wishlist_uri = preg_quote( $current_uri, '/' );
378
379 // Extract the Wishlist ID
380 preg_match( "/^({$wishlist_uri})\/([^\/]+)\/?$/", $uri_parts['uri'], $output_array );
381
382 if ( ! empty( $output_array[2] ) ) {
383 $uri_parts['uri'] = $output_array[1];
384 $uri_parts['endpoint'] = 'tinvwlID';
385 $uri_parts['endpoint_value'] = $output_array[2];
386 }
387 }
388 }
389
390 return $uri_parts;
391 }
392
393 /**
394 * Keep the query strings appended to the product permalinks by WooCommerce Subscriptions
395 *
396 * @param string $permalink
397 * @param WP_Post $post
398 * @param string $old_permalink
399 *
400 * @return string
401 */
402 function wcs_fix_subscription_links( $permalink, $post, $old_permalink ) {
403 if ( ! empty( $post->post_type ) && $post->post_type == 'product' && strpos( $old_permalink, 'switch-subscription=' ) !== false ) {
404 $query_arg = parse_url( $old_permalink, PHP_URL_QUERY );
405 $permalink = "{$permalink}?{$query_arg}";
406 }
407
408 return $permalink;
409 }
410 }