PageRenderTime 52ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/polylang/include/translated-object.php

https://gitlab.com/hop23typhu/bryepoxy
PHP | 272 lines | 131 code | 34 blank | 107 comment | 23 complexity | f17d1d082ec1c8f72c2124bc5e4c6517 MD5 | raw file
  1. <?php
  2. /**
  3. * Setups the objects languages and translations model
  4. *
  5. * @since 1.8
  6. */
  7. abstract class PLL_Translated_Object {
  8. public $model;
  9. protected $object_type, $tax_language, $tax_translations, $tax_tt;
  10. /**
  11. * Constructor
  12. *
  13. * @since 1.8
  14. *
  15. * @param object $model
  16. */
  17. public function __construct( &$model ) {
  18. $this->model = &$model;
  19. // register our taxonomies as soon as possible
  20. // this is early registration, not ready for rewrite rules as wp_rewrite will be setup later
  21. $args = array( 'label' => false, 'public' => false, 'query_var' => false, 'rewrite' => false, '_pll' => true );
  22. register_taxonomy( $this->tax_language, $this->object_type, $args );
  23. $args['update_count_callback'] = '_update_generic_term_count'; // count *all* posts to avoid deleting in clean_translations_terms
  24. register_taxonomy( $this->tax_translations, $this->object_type, $args );
  25. }
  26. /**
  27. * Wrap wp_get_object_terms to cache it and return only one object
  28. * inspired by the function get_the_terms
  29. *
  30. * @since 1.2
  31. *
  32. * @param int $object_id post_id or term_id
  33. * @param string $taxonomy Polylang taxonomy depending if we are looking for a post ( or term ) language ( or translation )
  34. * @return bool|object the term associated to the object in the requested taxonomy if exists, false otherwise
  35. */
  36. public function get_object_term( $object_id, $taxonomy ) {
  37. if ( empty( $object_id ) ) {
  38. return false;
  39. }
  40. $object_id = (int) $object_id;
  41. $term = get_object_term_cache( $object_id, $taxonomy );
  42. if ( false === $term ) {
  43. // query language and translations at the same time
  44. $taxonomies = array( $this->tax_language, $this->tax_translations );
  45. // query terms
  46. foreach ( wp_get_object_terms( $object_id, $taxonomies ) as $t ) {
  47. $terms[ $t->taxonomy ] = $t;
  48. if ( $t->taxonomy == $taxonomy ) {
  49. $term = $t;
  50. }
  51. }
  52. // store it the way WP wants it
  53. // set an empty cache if no term found in the taxonomy
  54. foreach ( $taxonomies as $tax ) {
  55. wp_cache_add( $object_id, empty( $terms[ $tax ] ) ? array() : array( $terms[ $tax ] ), $tax . '_relationships' );
  56. }
  57. }
  58. else {
  59. $term = reset( $term );
  60. }
  61. return empty( $term ) ? false : $term;
  62. }
  63. /**
  64. * Tells wether to store a translation term
  65. *
  66. * @since 1.8
  67. *
  68. * @param array $translations an associative array of translations with language code as key and translation id as value
  69. */
  70. protected function keep_translation_group( $translations ) {
  71. return count( $translations ) > 1;
  72. }
  73. /**
  74. * Saves translations for posts or terms
  75. *
  76. * @since 0.5
  77. *
  78. * @param int $id post id or term id
  79. * @param array $translations an associative array of translations with language code as key and translation id as value
  80. */
  81. public function save_translations( $id, $translations ) {
  82. $id = (int) $id;
  83. if ( ( $lang = $this->get_language( $id ) ) && isset( $translations ) && is_array( $translations ) ) {
  84. // sanitize the translations array
  85. $translations = array_map( 'intval', $translations );
  86. $translations = array_merge( array( $lang->slug => $id ), $translations ); // make sure this object is in translations
  87. $translations = array_diff( $translations, array( 0 ) ); // don't keep non translated languages
  88. $translations = array_intersect_key( $translations, array_flip( $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) ); // keep only valid languages slugs as keys
  89. // unlink removed translations
  90. $old_translations = $this->get_translations( $id );
  91. foreach ( array_diff_assoc( $old_translations, $translations ) as $object_id ) {
  92. $this->delete_translation( $object_id );
  93. }
  94. // don't create a translation group for untranslated posts as it is useless
  95. // but we need one for terms to allow relationships remap when importing from a WXR file
  96. if ( $this->keep_translation_group( $translations ) ) {
  97. $terms = wp_get_object_terms( $translations, $this->tax_translations );
  98. $term = reset( $terms );
  99. // create a new term if necessary
  100. if ( empty( $term ) ) {
  101. wp_insert_term( $group = uniqid( 'pll_' ), $this->tax_translations, array( 'description' => serialize( $translations ) ) );
  102. }
  103. else {
  104. // take care not to overwrite extra data stored in description field, if any
  105. $d = unserialize( $term->description );
  106. $d = is_array( $d ) ? array_diff_key( $d, $old_translations ) : array(); // remove old translations
  107. $d = array_merge( $d, $translations ); // add new one
  108. wp_update_term( $group = (int) $term->term_id, $this->tax_translations, array( 'description' => serialize( $d ) ) );
  109. }
  110. // link all translations to the new term
  111. foreach ( $translations as $p ) {
  112. wp_set_object_terms( $p, $group, $this->tax_translations );
  113. }
  114. // clean now unused translation groups
  115. foreach ( wp_list_pluck( $terms, 'term_id' ) as $term_id ) {
  116. $term = get_term( $term_id, $this->tax_translations );
  117. if ( empty( $term->count ) ) {
  118. wp_delete_term( $term_id, $this->tax_translations );
  119. }
  120. }
  121. }
  122. }
  123. }
  124. /**
  125. * Deletes a translation of a post or term
  126. *
  127. * @since 0.5
  128. *
  129. * @param int $id post id or term id
  130. */
  131. public function delete_translation( $id ) {
  132. $id = (int) $id;
  133. $term = $this->get_object_term( $id, $this->tax_translations );
  134. if ( ! empty( $term ) ) {
  135. $d = unserialize( $term->description );
  136. $slug = array_search( $id, $this->get_translations( $id ) ); // in case some plugin stores the same value with different key
  137. unset( $d[ $slug ] );
  138. if ( empty( $d ) ) {
  139. wp_delete_term( (int) $term->term_id, $this->tax_translations );
  140. }
  141. else {
  142. wp_update_term( (int) $term->term_id, $this->tax_translations, array( 'description' => serialize( $d ) ) );
  143. }
  144. }
  145. }
  146. /**
  147. * Returns an array of translations of a post or term
  148. *
  149. * @since 0.5
  150. *
  151. * @param int $id post id or term id
  152. * @return array an associative array of translations with language code as key and translation id as value
  153. */
  154. public function get_translations( $id ) {
  155. $term = $this->get_object_term( $id, $this->tax_translations );
  156. $translations = empty( $term ) ? array() : unserialize( $term->description );
  157. // make sure we return only translations ( thus we allow plugins to store other informations in the array )
  158. if ( is_array( $translations ) ) {
  159. $translations = array_intersect_key( $translations, array_flip( $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) );
  160. }
  161. // make sure to return at least the passed post or term in its translation array
  162. if ( empty( $translations ) && $lang = $this->get_language( $id ) ) {
  163. $translations = array( $lang->slug => $id );
  164. }
  165. return $translations;
  166. }
  167. /**
  168. * Returns the id of the translation of a post or term
  169. *
  170. * @since 0.5
  171. *
  172. * @param int $id post id or term id
  173. * @param object|string $lang object or slug
  174. * @return bool|int post id or term id of the translation, false if there is none
  175. */
  176. public function get_translation( $id, $lang ) {
  177. if ( ! $lang = $this->model->get_language( $lang ) ) {
  178. return false;
  179. }
  180. $translations = $this->get_translations( $id );
  181. return isset( $translations[ $lang->slug ] ) ? $translations[ $lang->slug ] : false;
  182. }
  183. /**
  184. * Among the object and its translations, returns the id of the object which is in $lang
  185. *
  186. * @since 0.1
  187. *
  188. * @param int $id post id or term id
  189. * @param int|string|object $lang language ( term_id or slug or object )
  190. * @return bool|int the translation post id or term id if exists, otherwise the post id or term id, false if the post has no language
  191. */
  192. public function get( $id, $lang ) {
  193. $obj_lang = $this->get_language( $id ); // FIXME is this necessary?
  194. if ( ! $lang || ! $obj_lang ) {
  195. return false;
  196. }
  197. $lang = $this->model->get_language( $lang );
  198. return $obj_lang->term_id == $lang->term_id ? $id : $this->get_translation( $id, $lang );
  199. }
  200. /**
  201. * A where clause to add to sql queries when filtering by language is needed directly in query
  202. *
  203. * @since 1.2
  204. *
  205. * @param object|array|string $lang a PLL_Language object or a comma separated list of languag slug or an array of language slugs
  206. * @return string where clause
  207. */
  208. public function where_clause( $lang ) {
  209. global $wpdb;
  210. $tt_id = $this->tax_tt;
  211. // $lang is an object
  212. // generally the case if the query is coming from Polylang
  213. if ( is_object( $lang ) ) {
  214. return $wpdb->prepare( ' AND pll_tr.term_taxonomy_id = %d', $lang->$tt_id );
  215. }
  216. // $lang is a comma separated list of slugs ( or an array of slugs )
  217. // generally the case is the query is coming from outside with 'lang' parameter
  218. $slugs = is_array( $lang ) ? $lang : explode( ',', $lang );
  219. foreach ( $slugs as $slug ) {
  220. $languages[] = (int) $this->model->get_language( $slug )->$tt_id;
  221. }
  222. return ' AND pll_tr.term_taxonomy_id IN ( ' . implode( ',', $languages ) . ' )';
  223. }
  224. /**
  225. * Returns ids of objects in a language similarly to get_objects_in_term for a taxonomy
  226. * faster than get_objects_in_term as it avoids a JOIN
  227. *
  228. * @since 1.4
  229. *
  230. * @param object $lang a PLL_Language object
  231. * @return array
  232. */
  233. public function get_objects_in_language( $lang ) {
  234. global $wpdb;
  235. $tt_id = $this->tax_tt;
  236. return $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $lang->$tt_id ) );
  237. }
  238. }