PluginProbe
Polylang / 2.8.3
Polylang v2.8.3
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 / modules / wpml / wpml-config.php

wpml-config.php in Polylang 2.8.3, at modules/wpml/wpml-config.php

324 lines 10.3 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 * Reads and interprets the file wpml-config.xml
8 * See http://wpml.org/documentation/support/language-configuration-files/
9 * The language switcher configuration is not interpreted
10 *
11 * @since 1.0
12 */
13 class PLL_WPML_Config {
14 protected static $instance; // For singleton
15 protected $xmls, $options;
16
17 /**
18 * Constructor
19 *
20 * @since 1.0
21 */
22 public function __construct() {
23 if ( extension_loaded( 'simplexml' ) ) {
24 $this->init();
25 }
26 }
27
28 /**
29 * Access to the single instance of the class
30 *
31 * @since 1.7
32 *
33 * @return object
34 */
35 public static function instance() {
36 if ( empty( self::$instance ) ) {
37 self::$instance = new self();
38 }
39 return self::$instance;
40 }
41
42 /**
43 * Finds the wpml-config.xml files to parse and setup filters
44 *
45 * @since 1.0
46 */
47 public function init() {
48 $this->xmls = array();
49
50 // Plugins
51 // Don't forget sitewide active plugins thanks to Reactorshop http://wordpress.org/support/topic/polylang-and-yoast-seo-plugin/page/2?replies=38#post-4801829
52 $plugins = ( is_multisite() && $sitewide_plugins = get_site_option( 'active_sitewide_plugins' ) ) && is_array( $sitewide_plugins ) ? array_keys( $sitewide_plugins ) : array();
53 $plugins = array_merge( $plugins, get_option( 'active_plugins', array() ) );
54
55 foreach ( $plugins as $plugin ) {
56 if ( file_exists( $file = WP_PLUGIN_DIR . '/' . dirname( $plugin ) . '/wpml-config.xml' ) && false !== $xml = simplexml_load_file( $file ) ) {
57 $this->xmls[ dirname( $plugin ) ] = $xml;
58 }
59 }
60
61 // Theme
62 if ( file_exists( $file = ( $template = get_template_directory() ) . '/wpml-config.xml' ) && false !== $xml = simplexml_load_file( $file ) ) {
63 $this->xmls[ get_template() ] = $xml;
64 }
65
66 // Child theme
67 if ( ( $stylesheet = get_stylesheet_directory() ) !== $template && file_exists( $file = $stylesheet . '/wpml-config.xml' ) && false !== $xml = simplexml_load_file( $file ) ) {
68 $this->xmls[ get_stylesheet() ] = $xml;
69 }
70
71 // Custom
72 if ( file_exists( $file = PLL_LOCAL_DIR . '/wpml-config.xml' ) && false !== $xml = simplexml_load_file( $file ) ) {
73 $this->xmls['Polylang'] = $xml;
74 }
75
76 if ( ! empty( $this->xmls ) ) {
77 add_filter( 'pll_copy_post_metas', array( $this, 'copy_post_metas' ), 20, 2 );
78 add_filter( 'pll_copy_term_metas', array( $this, 'copy_term_metas' ), 20, 2 );
79 add_filter( 'pll_get_post_types', array( $this, 'translate_types' ), 10, 2 );
80 add_filter( 'pll_get_taxonomies', array( $this, 'translate_taxonomies' ), 10, 2 );
81
82 foreach ( $this->xmls as $context => $xml ) {
83 foreach ( $xml->xpath( 'admin-texts/key' ) as $key ) {
84 $attributes = $key->attributes();
85 $name = (string) $attributes['name'];
86
87 if ( false !== strpos( $name, '*' ) ) {
88 $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
89 $names = preg_grep( $pattern, array_keys( wp_load_alloptions() ) );
90
91 foreach ( $names as $_name ) {
92 $this->register_or_translate_option( $context, $_name, $key );
93 }
94 } else {
95 $this->register_or_translate_option( $context, $name, $key );
96 }
97 }
98 }
99 }
100 }
101
102 /**
103 * Adds custom fields to the list of metas to copy when creating a new translation
104 *
105 * @since 1.0
106 *
107 * @param array $metas the list of custom fields to copy or synchronize
108 * @param bool $sync true for sync, false for copy
109 * @return array the list of custom fields to copy or synchronize
110 */
111 public function copy_post_metas( $metas, $sync ) {
112 foreach ( $this->xmls as $xml ) {
113 foreach ( $xml->xpath( 'custom-fields/custom-field' ) as $cf ) {
114 $attributes = $cf->attributes();
115 if ( 'copy' == $attributes['action'] || ( ! $sync && in_array( $attributes['action'], array( 'translate', 'copy-once' ) ) ) ) {
116 $metas[] = (string) $cf;
117 } else {
118 $metas = array_diff( $metas, array( (string) $cf ) );
119 }
120 }
121 }
122 return $metas;
123 }
124
125 /**
126 * Adds term metas to the list of metas to copy when creating a new translation
127 *
128 * @since 2.6
129 *
130 * @param array $metas The list of term metas to copy or synchronize.
131 * @param bool $sync True for sync, false for copy.
132 * @return array The list of term metas to copy or synchronize.
133 */
134 public function copy_term_metas( $metas, $sync ) {
135 foreach ( $this->xmls as $xml ) {
136 foreach ( $xml->xpath( 'custom-term-fields/custom-term-field' ) as $cf ) {
137 $attributes = $cf->attributes();
138 if ( 'copy' == $attributes['action'] || ( ! $sync && in_array( $attributes['action'], array( 'translate', 'copy-once' ) ) ) ) {
139 $metas[] = (string) $cf;
140 } else {
141 $metas = array_diff( $metas, array( (string) $cf ) );
142 }
143 }
144 }
145 return $metas;
146 }
147
148 /**
149 * Language and translation management for custom post types
150 *
151 * @since 1.0
152 *
153 * @param array $types list of post type names for which Polylang manages language and translations
154 * @param bool $hide true when displaying the list in Polylang settings
155 * @return array list of post type names for which Polylang manages language and translations
156 */
157 public function translate_types( $types, $hide ) {
158 foreach ( $this->xmls as $xml ) {
159 foreach ( $xml->xpath( 'custom-types/custom-type' ) as $pt ) {
160 $attributes = $pt->attributes();
161 if ( 1 == $attributes['translate'] && ! $hide ) {
162 $types[ (string) $pt ] = (string) $pt;
163 } else {
164 unset( $types[ (string) $pt ] ); // The theme/plugin author decided what to do with the post type so don't allow the user to change this
165 }
166 }
167 }
168 return $types;
169 }
170
171 /**
172 * Language and translation management for custom taxonomies
173 *
174 * @since 1.0
175 *
176 * @param array $taxonomies list of taxonomy names for which Polylang manages language and translations
177 * @param bool $hide true when displaying the list in Polylang settings
178 * @return array list of taxonomy names for which Polylang manages language and translations
179 */
180 public function translate_taxonomies( $taxonomies, $hide ) {
181 foreach ( $this->xmls as $xml ) {
182 foreach ( $xml->xpath( 'taxonomies/taxonomy' ) as $tax ) {
183 $attributes = $tax->attributes();
184 if ( 1 == $attributes['translate'] && ! $hide ) {
185 $taxonomies[ (string) $tax ] = (string) $tax;
186 } else {
187 unset( $taxonomies[ (string) $tax ] ); // the theme/plugin author decided what to do with the taxonomy so don't allow the user to change this
188 }
189 }
190 }
191 return $taxonomies;
192 }
193
194 /**
195 * Registers or translates the strings for an option
196 *
197 * @since 2.8
198 *
199 * @param string $context The group in which the strings will be registered.
200 * @param string $name Option name.
201 * @param object $key XML node.
202 */
203 protected function register_or_translate_option( $context, $name, $key ) {
204 if ( PLL() instanceof PLL_Frontend ) {
205 $this->options[ $name ] = $key;
206 add_filter( 'option_' . $name, array( $this, 'translate_strings' ) );
207 } else {
208 $this->register_string_recursive( $context, $name, get_option( $name ), $key );
209 }
210 }
211
212 /**
213 * Translates the strings for an option
214 *
215 * @since 1.0
216 *
217 * @param array|string $value Either a string to translate or a list of strings to translate
218 * @return array|string translated string(s)
219 */
220 public function translate_strings( $value ) {
221 $option = substr( current_filter(), 7 );
222 return $this->translate_strings_recursive( $value, $this->options[ $option ] );
223 }
224
225 /**
226 * Recursively registers strings for a serialized option
227 *
228 * @since 1.0
229 * @since 2.7 Signature modified
230 *
231 * @param string $context The group in which the strings will be registered.
232 * @param string $option Option name.
233 * @param array $values Option value.
234 * @param object $key XML node.
235 */
236 protected function register_string_recursive( $context, $option, $values, $key ) {
237 if ( is_object( $values ) ) {
238 $values = (array) $values;
239 }
240
241 $children = $key->children();
242
243 if ( is_array( $values ) ) {
244 if ( count( $children ) ) {
245 foreach ( $children as $child ) {
246 $attributes = $child->attributes();
247 $name = (string) $attributes['name'];
248
249 if ( isset( $values[ $name ] ) ) {
250 $this->register_string_recursive( $context, $name, $values[ $name ], $child );
251 continue;
252 }
253
254 $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
255
256 foreach ( $values as $n => $value ) {
257 // The first case could be handled by the next one, but we avoid calls to preg_match here.
258 if ( '*' === $name || ( false !== strpos( $name, '*' ) && preg_match( $pattern, $n ) ) ) {
259 $this->register_string_recursive( $context, $n, $value, $child );
260 }
261 }
262 }
263 } else {
264 foreach ( $values as $n => $value ) {
265 // Parent key is a wildcard and no sub-key has been whitelisted.
266 $this->register_string_recursive( $context, $n, $value, $key );
267 }
268 }
269 } else {
270 pll_register_string( $option, $values, $context, true ); // Multiline as in WPML.
271 }
272 }
273
274 /**
275 * Recursively translates strings for a serialized option
276 *
277 * @since 1.0
278 *
279 * @param array|string $values Either a string to translate or a list of strings to translate.
280 * @param object $key XML node.
281 * @return array|string Translated string(s)
282 */
283 protected function translate_strings_recursive( $values, $key ) {
284 $children = $key->children();
285
286 if ( is_array( $values ) || is_object( $values ) ) {
287 if ( count( $children ) ) {
288 foreach ( $children as $child ) {
289 $attributes = $child->attributes();
290 $name = (string) $attributes['name'];
291
292 if ( is_array( $values ) && isset( $values[ $name ] ) ) {
293 $values[ $name ] = $this->translate_strings_recursive( $values[ $name ], $child );
294 continue;
295 }
296
297 if ( is_object( $values ) && isset( $values->$name ) ) {
298 $values->$name = $this->translate_strings_recursive( $values->$name, $child );
299 continue;
300 }
301
302 $pattern = '#^' . str_replace( '*', '(?:.+)', $name ) . '$#';
303
304 foreach ( $values as $n => &$value ) {
305 // The first case could be handled by the next one, but we avoid calls to preg_match here.
306 if ( '*' === $name || ( false !== strpos( $name, '*' ) && preg_match( $pattern, $n ) ) ) {
307 $value = $this->translate_strings_recursive( $value, $child );
308 }
309 }
310 }
311 } else {
312 // Parent key is a wildcard and no sub-key has been whitelisted.
313 foreach ( $values as &$value ) {
314 $value = $this->translate_strings_recursive( $value, $key );
315 }
316 }
317 } else {
318 $values = pll__( $values );
319 }
320
321 return $values;
322 }
323 }
324