PluginProbe
Polylang / 3.7
Polylang v3.7
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / include / links-directory.php

links-directory.php in Polylang 3.7, at include/links-directory.php

296 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 /**
7 * Links model for use when the language code is added in the url as a directory
8 * for example mysite.com/en/something.
9 *
10 * @since 1.2
11 */
12 class PLL_Links_Directory extends PLL_Links_Permalinks {
13 /**
14 * Relative path to the home url.
15 *
16 * @var string
17 */
18 protected $home_relative;
19
20 /**
21 * Constructor.
22 *
23 * @since 1.2
24 *
25 * @param PLL_Model $model PLL_Model instance.
26 */
27 public function __construct( &$model ) {
28 parent::__construct( $model );
29
30 $this->home_relative = home_url( '/', 'relative' );
31 }
32
33 /**
34 * Adds hooks for rewrite rules.
35 *
36 * @since 1.6
37 *
38 * @return void
39 */
40 public function init() {
41 add_action( 'pll_prepare_rewrite_rules', array( $this, 'prepare_rewrite_rules' ) ); // Ensure it's hooked before `self::do_prepare_rewrite_rules()` is called.
42
43 parent::init();
44 }
45
46 /**
47 * Adds the language code in a url.
48 *
49 * @since 1.2
50 * @since 3.4 Accepts now a language slug.
51 *
52 * @param string $url The url to modify.
53 * @param PLL_Language|string|false $language Language object or slug.
54 * @return string The modified url.
55 */
56 public function add_language_to_link( $url, $language ) {
57 if ( $language instanceof PLL_Language ) {
58 $language = $language->slug;
59 }
60
61 if ( ! empty( $language ) ) {
62 $base = $this->options['rewrite'] ? '' : 'language/';
63 $slug = $this->options['default_lang'] === $language && $this->options['hide_default'] ? '' : $base . $language . '/';
64 $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : preg_replace( '#^https?://#', '://', $this->home . '/' . $this->root );
65
66 if ( false === strpos( $url, $new = $root . $slug ) ) {
67 $pattern = preg_quote( $root, '#' );
68 $pattern = '#' . $pattern . '#';
69 return preg_replace( $pattern, $new, $url, 1 ); // Only once.
70 }
71 }
72 return $url;
73 }
74
75 /**
76 * Returns the url without the language code.
77 *
78 * @since 1.2
79 *
80 * @param string $url The url to modify.
81 * @return string The modified url.
82 */
83 public function remove_language_from_link( $url ) {
84 $languages = $this->model->get_languages_list(
85 array(
86 'hide_default' => $this->options['hide_default'],
87 'fields' => 'slug',
88 )
89 );
90
91 if ( ! empty( $languages ) ) {
92 $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : preg_replace( '#^https?://#', '://', $this->home . '/' . $this->root );
93
94 $pattern = preg_quote( $root, '@' );
95 $pattern = '@' . $pattern . ( $this->options['rewrite'] ? '' : 'language/' ) . '(' . implode( '|', $languages ) . ')(([?#])|(/|$))@';
96 $url = preg_replace( $pattern, $root . '$3', $url );
97 }
98 return $url;
99 }
100
101 /**
102 * Returns the language based on the language code in the url.
103 *
104 * @since 1.2
105 * @since 2.0 Add the $url argument.
106 *
107 * @param string $url Optional, defaults to the current url.
108 * @return string The language slug.
109 */
110 public function get_language_from_url( $url = '' ) {
111 if ( empty( $url ) ) {
112 $url = pll_get_requested_url();
113 }
114
115 $path = (string) wp_parse_url( $url, PHP_URL_PATH );
116 $root = ( false === strpos( $url, '://' ) ) ? $this->home_relative . $this->root : $this->home . '/' . $this->root;
117
118 $pattern = (string) wp_parse_url( $root . ( $this->options['rewrite'] ? '' : 'language/' ), PHP_URL_PATH );
119 $pattern = preg_quote( $pattern, '#' );
120 $pattern = '#^' . $pattern . '(' . implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) . ')(/|$)#';
121 return preg_match( $pattern, trailingslashit( $path ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language.
122 }
123
124 /**
125 * Returns the home url in a given language.
126 *
127 * @since 1.3.1
128 * @since 3.4 Accepts now a language slug.
129 *
130 * @param PLL_Language|string $language Language object or slug.
131 * @return string
132 */
133 public function home_url( $language ) {
134 if ( $language instanceof PLL_Language ) {
135 $language = $language->slug;
136 }
137
138 $base = $this->options['rewrite'] ? '' : 'language/';
139 $slug = $this->options['default_lang'] === $language && $this->options['hide_default'] ? '' : '/' . $this->root . $base . $language;
140 return trailingslashit( $this->home . $slug );
141 }
142
143 /**
144 * Prepares the rewrite rules filters.
145 *
146 * @since 0.8.1
147 * @since 3.5 Hooked to `pll_prepare_rewrite_rules` and remove `$pre` parameter.
148 *
149 * @return void
150 */
151 public function prepare_rewrite_rules() {
152 /*
153 * Don't modify the rules if there is no languages created yet and make sure
154 * to add the filters only once and if all custom post types and taxonomies
155 * have been registered.
156 */
157 if ( ! $this->model->has_languages() || ! did_action( 'wp_loaded' ) || ! self::$can_filter_rewrite_rules ) {
158 return;
159 }
160
161 foreach ( $this->get_rewrite_rules_filters_with_callbacks() as $rule => $callback ) {
162 add_filter( $rule, $callback );
163 }
164 }
165
166 /**
167 * The rewrite rules !
168 *
169 * Always make sure that the default language is at the end in case the language information is hidden for default language.
170 * Thanks to brbrbr http://wordpress.org/support/topic/plugin-polylang-rewrite-rules-not-correct.
171 *
172 * @since 0.8.1
173 *
174 * @param string[] $rules Rewrite rules.
175 * @return string[] Modified rewrite rules.
176 */
177 public function rewrite_rules( $rules ) {
178 $filter = str_replace( '_rewrite_rules', '', current_filter() );
179
180 global $wp_rewrite;
181 $newrules = array();
182
183 $languages = $this->model->get_languages_list( array( 'fields' => 'slug' ) );
184 if ( $this->options['hide_default'] ) {
185 $languages = array_diff( $languages, array( $this->options['default_lang'] ) );
186 }
187
188 if ( ! empty( $languages ) ) {
189 $slug = $wp_rewrite->root . ( $this->options['rewrite'] ? '' : 'language/' ) . '(' . implode( '|', $languages ) . ')/';
190 }
191
192 // For custom post type archives.
193 $cpts = array_intersect( $this->model->get_translated_post_types(), get_post_types( array( '_builtin' => false ) ) );
194 $cpts = $cpts ? '#post_type=(' . implode( '|', $cpts ) . ')#' : '';
195
196 foreach ( $rules as $key => $rule ) {
197 if ( ! is_string( $rule ) || ! is_string( $key ) ) {
198 // Protection against a bug in Sendinblue for WooCommerce. See: https://wordpress.org/support/topic/bug-introduced-in-rewrite-rules/
199 continue;
200 }
201
202 // Special case for translated post types and taxonomies to allow canonical redirection.
203 if ( $this->options['force_lang'] && in_array( $filter, array_merge( $this->model->get_translated_post_types(), $this->model->get_translated_taxonomies() ) ) ) {
204
205 /**
206 * Filters the rewrite rules to modify.
207 *
208 * @since 1.9.1
209 *
210 * @param bool $modify Whether to modify or not the rule, defaults to true.
211 * @param array $rule Original rewrite rule.
212 * @param string $filter Current set of rules being modified.
213 * @param string|bool $archive Custom post post type archive name or false if it is not a cpt archive.
214 */
215 if ( isset( $slug ) && apply_filters( 'pll_modify_rewrite_rule', true, array( $key => $rule ), $filter, false ) ) {
216 $newrules[ $slug . str_replace( $wp_rewrite->root, '', ltrim( $key, '^' ) ) ] = str_replace(
217 array( '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?' ),
218 array( '[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&' ),
219 $rule
220 ); // Should be enough!
221 }
222
223 $newrules[ $key ] = $rule;
224 }
225
226 // Rewrite rules filtered by language.
227 elseif ( in_array( $filter, $this->always_rewrite ) || in_array( $filter, $this->model->get_filtered_taxonomies() ) || ( $cpts && preg_match( $cpts, $rule, $matches ) && ! strpos( $rule, 'name=' ) ) || ( 'rewrite_rules_array' != $filter && $this->options['force_lang'] ) ) {
228
229 /** This filter is documented in include/links-directory.php */
230 if ( apply_filters( 'pll_modify_rewrite_rule', true, array( $key => $rule ), $filter, empty( $matches[1] ) ? false : $matches[1] ) ) {
231 if ( isset( $slug ) ) {
232 $newrules[ $slug . str_replace( $wp_rewrite->root, '', ltrim( $key, '^' ) ) ] = str_replace(
233 array( '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?' ),
234 array( '[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&' ),
235 $rule
236 ); // Should be enough!
237 }
238
239 if ( $this->options['hide_default'] ) {
240 $newrules[ $key ] = str_replace( '?', '?lang=' . $this->options['default_lang'] . '&', $rule );
241 }
242 } else {
243 $newrules[ $key ] = $rule;
244 }
245 }
246
247 // Unmodified rules.
248 else {
249 $newrules[ $key ] = $rule;
250 }
251 }
252
253 // The home rewrite rule.
254 if ( 'root' == $filter && isset( $slug ) ) {
255 $newrules[ $slug . '?$' ] = $wp_rewrite->index . '?lang=$matches[1]';
256 }
257
258 return $newrules;
259 }
260
261 /**
262 * Removes hooks to filter rewrite rules, called when switching blog @see {PLL_Base::switch_blog()}.
263 * See `self::prepare_rewrite_rules()` for added hooks.
264 *
265 * @since 3.5
266 *
267 * @return void
268 */
269 public function remove_filters() {
270 parent::remove_filters();
271
272 foreach ( $this->get_rewrite_rules_filters_with_callbacks() as $rule => $callback ) {
273 remove_filter( $rule, $callback );
274 }
275 }
276
277 /**
278 * Returns *all* rewrite rules filters with their associated callbacks.
279 *
280 * @since 3.5
281 *
282 * @return callable[] Array of hook names as key and callbacks as values.
283 */
284 protected function get_rewrite_rules_filters_with_callbacks() {
285 $filters = array(
286 'rewrite_rules_array' => array( $this, 'rewrite_rules' ), // Needed for post type archives.
287 );
288
289 foreach ( $this->get_rewrite_rules_filters() as $type ) {
290 $filters[ $type . '_rewrite_rules' ] = array( $this, 'rewrite_rules' );
291 }
292
293 return $filters;
294 }
295 }
296