PageRenderTime 26ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/polylang/modules/wpml/wpml-config.php

https://gitlab.com/mostafame/team_website
PHP | 378 lines | 224 code | 38 blank | 116 comment | 65 complexity | c0e83b02da32a0f90824ef0cdb3ea600 MD5 | raw file
  1. <?php
  2. /**
  3. * reads and interprets the file wpml-config.xml
  4. * see http://wpml.org/documentation/support/language-configuration-files/
  5. * the language switcher configuration is not interpreted
  6. * the xml parser has been adapted from http://php.net/manual/en/function.xml-parse-into-struct.php#84261
  7. * many thanks to wickedfather at hotmail dot com
  8. *
  9. * @since 1.0
  10. */
  11. class PLL_WPML_Config {
  12. static protected $instance; // for singleton
  13. protected $values, $index, $strings;
  14. public $tags;
  15. /**
  16. * constructor
  17. *
  18. * @since 1.0
  19. */
  20. public function __construct() {
  21. $this->init();
  22. }
  23. /**
  24. * access to the single instance of the class
  25. *
  26. * @since 1.7
  27. *
  28. * @return object
  29. */
  30. static public function instance() {
  31. if ( empty( self::$instance ) ) {
  32. self::$instance = new self();
  33. }
  34. return self::$instance;
  35. }
  36. /**
  37. * parses the wpml-config.xml file
  38. *
  39. * @since 1.0
  40. *
  41. * @param string wpml-config.xml file content
  42. * @param string $context identifies where the file was found
  43. */
  44. protected function xml_parse( $xml, $context ) {
  45. $parser = xml_parser_create();
  46. xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, 0 );
  47. xml_parser_set_option( $parser, XML_OPTION_SKIP_WHITE, 1 );
  48. xml_parse_into_struct( $parser, $xml, $this->values );
  49. xml_parser_free( $parser );
  50. $this->index = 0;
  51. $arr = $this->xml_parse_recursive();
  52. $arr = $arr['wpml-config'];
  53. $keys = array(
  54. array( 'custom-fields', 'custom-field' ),
  55. array( 'custom-types','custom-type' ),
  56. array( 'taxonomies','taxonomy' ),
  57. array( 'admin-texts','key' ),
  58. );
  59. foreach ( $keys as $k ) {
  60. if ( isset( $arr[ $k[0] ] ) ) {
  61. if ( ! isset( $arr[ $k[0] ][ $k[1] ][0] ) ) {
  62. $elem = $arr[ $k[0] ][ $k[1] ];
  63. unset( $arr[ $k[0] ][ $k[1] ] );
  64. $arr[ $k[0] ][ $k[1] ][0] = $elem;
  65. }
  66. $this->tags[ $k[0] ][ $context ] = $arr[ $k[0] ];
  67. }
  68. }
  69. }
  70. /**
  71. * recursively parses the wpml-config.xml file
  72. *
  73. * @since 1.0
  74. *
  75. * @return array
  76. */
  77. protected function xml_parse_recursive() {
  78. $found = array();
  79. $tagCount = array();
  80. while ( isset( $this->values[ $this->index ] ) ) {
  81. $tag = $this->values[ $this->index ]['tag'];
  82. $type = $this->values[ $this->index ]['type'];
  83. if ( isset( $this->values[ $this->index ]['attributes'] ) ) {
  84. $attributes = $this->values[ $this->index ]['attributes'];
  85. }
  86. if ( isset( $this->values[ $this->index ]['value'] ) ) {
  87. $value = $this->values[ $this->index ]['value'];
  88. }
  89. $this->index++;
  90. if ( 'close' == $type ) {
  91. return $found;
  92. }
  93. if ( isset( $tagCount[ $tag ] ) ) {
  94. if ( 1 == $tagCount[ $tag ] ) {
  95. $found[ $tag ] = array( $found[ $tag ] );
  96. }
  97. $tagRef = &$found[ $tag ][ $tagCount[ $tag ] ];
  98. $tagCount[ $tag ]++;
  99. }
  100. else {
  101. $tagCount[ $tag ] = 1;
  102. $tagRef = &$found[ $tag ];
  103. }
  104. if ( 'open' == $type ) {
  105. $tagRef = $this->xml_parse_recursive();
  106. if ( isset( $attributes ) ) {
  107. $tagRef['attributes'] = $attributes;
  108. }
  109. }
  110. if ( 'complete' == $type ) {
  111. if ( isset( $attributes ) ) {
  112. $tagRef['attributes'] = $attributes;
  113. $tagRef = &$tagRef['value'];
  114. }
  115. if ( isset( $value ) ) {
  116. $tagRef = $value;
  117. }
  118. }
  119. }
  120. return $found;
  121. }
  122. /**
  123. * finds the wpml-config.xml files to parse and setup filters
  124. *
  125. * @since 1.0
  126. */
  127. public function init() {
  128. $this->tags = array();
  129. // theme
  130. if ( file_exists( $file = ( $template = get_template_directory() ) .'/wpml-config.xml' ) ) {
  131. $this->xml_parse( file_get_contents( $file ), get_template() ); // FIXME fopen + fread + fclose quicker ?
  132. }
  133. // child theme
  134. if ( ( $stylesheet = get_stylesheet_directory() ) !== $template && file_exists( $file = $stylesheet . '/wpml-config.xml' ) ) {
  135. $this->xml_parse( file_get_contents( $file ), get_stylesheet() );
  136. }
  137. // plugins
  138. // 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
  139. $plugins = ( is_multisite() && $sitewide_plugins = get_site_option( 'active_sitewide_plugins' ) ) && is_array( $sitewide_plugins ) ? array_keys( $sitewide_plugins ) : array();
  140. $plugins = array_merge( $plugins, get_option( 'active_plugins' ) );
  141. foreach ( $plugins as $plugin ) {
  142. if ( file_exists( $file = WP_PLUGIN_DIR.'/'.dirname( $plugin ).'/wpml-config.xml' ) ) {
  143. $this->xml_parse( file_get_contents( $file ), dirname( $plugin ) );
  144. }
  145. }
  146. // custom
  147. if ( file_exists( $file = PLL_LOCAL_DIR.'/wpml-config.xml' ) ) {
  148. $this->xml_parse( file_get_contents( $file ), 'Polylang' );
  149. }
  150. if ( isset( $this->tags['custom-fields'] ) ) {
  151. add_filter( 'pll_copy_post_metas', array( &$this, 'copy_post_metas' ), 10, 2 );
  152. }
  153. if ( isset( $this->tags['custom-types'] ) ) {
  154. add_filter( 'pll_get_post_types', array( &$this, 'translate_types' ), 10, 2 );
  155. }
  156. if ( isset( $this->tags['taxonomies'] ) ) {
  157. add_filter( 'pll_get_taxonomies', array( &$this, 'translate_taxonomies' ), 10, 2 );
  158. }
  159. if ( ! isset( $this->tags['admin-texts'] ) ) {
  160. return;
  161. }
  162. // get a cleaner array for easy manipulation
  163. foreach ( $this->tags['admin-texts'] as $context => $arr ) {
  164. foreach ( $arr as $keys ) {
  165. $this->strings[ $context ] = $this->admin_texts_recursive( $keys );
  166. }
  167. }
  168. foreach ( $this->strings as $context => $options ) {
  169. foreach ( $options as $option_name => $value ) {
  170. if ( PLL_ADMIN ) { // backend
  171. $option = get_option( $option_name );
  172. if ( is_string( $option ) && 1 == $value ) {
  173. pll_register_string( $option_name, $option, $context );
  174. }
  175. elseif ( is_array( $option ) && is_array( $value ) ) {
  176. $this->register_string_recursive( $context, $value, $option ); // for a serialized option
  177. }
  178. }
  179. else {
  180. add_filter( 'option_'.$option_name, array( &$this, 'translate_strings' ) );
  181. }
  182. }
  183. }
  184. }
  185. /**
  186. * arranges strings in a cleaner way
  187. *
  188. * @since 1.0
  189. *
  190. * @param array $keys
  191. * @return array
  192. */
  193. protected function admin_texts_recursive( $keys ) {
  194. if ( ! isset( $keys[0] ) ) {
  195. $elem = $keys;
  196. unset( $keys );
  197. $keys[0] = $elem;
  198. }
  199. foreach ( $keys as $key ) {
  200. $strings[ $key['attributes']['name'] ] = isset( $key['key'] ) ? $this->admin_texts_recursive( $key['key'] ) : 1;
  201. }
  202. return $strings;
  203. }
  204. /**
  205. * recursively registers strings for a serialized option
  206. *
  207. * @since 1.0
  208. *
  209. * @param string $context the group in which the strings will be registered
  210. * @param array $strings
  211. * @param array $options
  212. */
  213. protected function register_string_recursive( $context, $strings, $options ) {
  214. foreach ( $options as $name => $value ) {
  215. if ( isset( $strings[ $name ] ) ) {
  216. // allow numeric values to be translated
  217. // https://wordpress.org/support/topic/wpml-configxml-strings-skipped-when-numbers-ids
  218. if ( ( is_numeric( $value ) || is_string( $value ) ) && 1 == $strings[ $name ] ) {
  219. pll_register_string( $name, $value, $context );
  220. }
  221. elseif ( is_array( $value ) && is_array( $strings[ $name ] ) ) {
  222. $this->register_string_recursive( $context, $strings[ $name ], $value );
  223. }
  224. }
  225. }
  226. }
  227. /**
  228. * adds custom fields to the list of metas to copy when creating a new translation
  229. *
  230. * @since 1.0
  231. *
  232. * @param array $metas the list of custom fields to copy or synchronize
  233. * @param bool $sync true for sync, false for copy
  234. * @return array the list of custom fields to copy or synchronize
  235. */
  236. public function copy_post_metas( $metas, $sync ) {
  237. foreach ( $this->tags['custom-fields'] as $context ) {
  238. foreach ( $context['custom-field'] as $cf ) {
  239. // copy => copy and synchronize
  240. // translate => copy but don't synchronize
  241. // ignore => don't copy
  242. // see http://wordpress.org/support/topic/custom-field-values-override-other-translation-values?replies=8#post-4655563
  243. if ( 'copy' == $cf['attributes']['action'] || ( ! $sync && 'translate' == $cf['attributes']['action'] ) ) {
  244. $metas[] = $cf['value'];
  245. }
  246. else {
  247. $metas = array_diff( $metas, array( $cf['value'] ) );
  248. }
  249. }
  250. }
  251. return $metas;
  252. }
  253. /**
  254. * language and translation management for custom post types
  255. *
  256. * @since 1.0
  257. *
  258. * @param array $types list of post type names for which Polylang manages language and translations
  259. * @param bool $hide true when displaying the list in Polylang settings
  260. * @return array list of post type names for which Polylang manages language and translations
  261. */
  262. public function translate_types( $types, $hide ) {
  263. foreach ( $this->tags['custom-types'] as $context ) {
  264. foreach ( $context['custom-type'] as $pt ) {
  265. if ( 1 == $pt['attributes']['translate'] && ! $hide ) {
  266. $types[ $pt['value'] ] = $pt['value'];
  267. }
  268. else {
  269. unset( $types[ $pt['value'] ] ); // the author decided what to do with the post type so don't allow the user to change this
  270. }
  271. }
  272. }
  273. return $types;
  274. }
  275. /**
  276. * language and translation management for custom taxonomies
  277. *
  278. * @since 1.0
  279. *
  280. * @param array $taxonomies list of taxonomy names for which Polylang manages language and translations
  281. * @param bool $hide true when displaying the list in Polylang settings
  282. * @return array list of taxonomy names for which Polylang manages language and translations
  283. */
  284. public function translate_taxonomies( $taxonomies, $hide ) {
  285. foreach ( $this->tags['taxonomies'] as $context ) {
  286. foreach ( $context['taxonomy'] as $tax ) {
  287. if ( 1 == $tax['attributes']['translate'] && ! $hide ) {
  288. $taxonomies[ $tax['value'] ] = $tax['value'];
  289. }
  290. else {
  291. unset( $taxonomies[ $tax['value'] ] ); // the author decided what to do with the taxonomy so don't allow the user to change this
  292. }
  293. }
  294. }
  295. return $taxonomies;
  296. }
  297. /**
  298. * translates the strings for an option
  299. *
  300. * @since 1.0
  301. *
  302. * @param array|string either a string to translate or a list of strings to translate
  303. * @return array|string translated string(s)
  304. */
  305. public function translate_strings( $value ) {
  306. if ( is_array( $value ) ) {
  307. $option = substr( current_filter(), 7 );
  308. foreach ( $this->strings as $context => $options ) {
  309. if ( array_key_exists( $option, $options ) ) {
  310. return $this->translate_strings_recursive( $options[ $option ], $value ); // for a serialized option
  311. }
  312. }
  313. }
  314. return pll__( $value );
  315. }
  316. /**
  317. * recursively translates strings for a serialized option
  318. *
  319. * @since 1.0
  320. *
  321. * @param array $strings
  322. * @param array|string $values either a string to translate or a list of strings to translate
  323. * @return array|string translated string(s)
  324. */
  325. protected function translate_strings_recursive( $strings, $values ) {
  326. foreach ( $values as $name => $value ) {
  327. if ( isset( $strings[ $name ] ) ) {
  328. // allow numeric values to be translated
  329. // https://wordpress.org/support/topic/wpml-configxml-strings-skipped-when-numbers-ids
  330. if ( ( is_numeric( $value ) || is_string( $value ) ) && 1 == $strings[ $name ] ) {
  331. $values[ $name ] = pll__( $value );
  332. }
  333. elseif ( is_array( $value ) && is_array( $strings[ $name ] ) ) {
  334. $values[ $name ] = $this->translate_strings_recursive( $strings[ $name ], $value );
  335. }
  336. }
  337. }
  338. return $values;
  339. }
  340. } // class PLL_WPML_Config