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

/admin/admin-filters-post.php

https://github.com/Screenfeed/polylang
PHP | 373 lines | 205 code | 58 blank | 110 comment | 39 complexity | 920bd72468b15a19d0c07ab0c9bdb502 MD5 | raw file
  1. <?php
  2. /*
  3. * manages filters and actions related to posts on admin side
  4. *
  5. * @since 1.2
  6. */
  7. class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
  8. public $options, $curlang;
  9. /*
  10. * constructor: setups filters and actions
  11. *
  12. * @since 1.2
  13. *
  14. * @param object $polylang
  15. */
  16. public function __construct(&$polylang) {
  17. parent::__construct($polylang);
  18. $this->options = &$polylang->options;
  19. $this->curlang = &$polylang->curlang;
  20. // filters posts, pages and media by language
  21. add_filter('parse_query',array(&$this,'parse_query'));
  22. // adds the Languages box in the 'Edit Post' and 'Edit Page' panels
  23. add_action('add_meta_boxes', array(&$this, 'add_meta_boxes'));
  24. // ajax response for changing the language in the post metabox
  25. add_action('wp_ajax_post_lang_choice', array(&$this,'post_lang_choice'));
  26. add_action('wp_ajax_pll_posts_not_translated', array(&$this,'ajax_posts_not_translated'));
  27. // adds actions and filters related to languages when creating, saving or deleting posts and pages
  28. add_action('save_post', array(&$this, 'save_post'), 21, 2); // priority 21 to come after advanced custom fields (20) and before the event calendar which breaks everything after 25
  29. add_action('before_delete_post', array(&$this, 'delete_post'));
  30. if ($this->options['media_support'])
  31. add_action('delete_attachment', array(&$this, 'delete_post')); // action shared with media
  32. // filters the pages by language in the parent dropdown list in the page attributes metabox
  33. add_filter('page_attributes_dropdown_pages_args', array(&$this, 'page_attributes_dropdown_pages_args'), 10, 2);
  34. }
  35. /*
  36. * filters posts, pages and media by language
  37. *
  38. * @since 0.1
  39. *
  40. * @param object $query a WP_Query object
  41. */
  42. public function parse_query($query) {
  43. $qvars = &$query->query_vars;
  44. // do not filter post types such as nav_menu_item
  45. if (isset($qvars['post_type']) && !$this->model->is_translated_post_type($qvars['post_type'])) {
  46. unset ($qvars['lang']);
  47. return;
  48. }
  49. if (isset($qvars['post_type']) && !isset($qvars['lang'])) {
  50. // filters the list of media (or wp-links) by language when uploading from post
  51. if (isset($_REQUEST['pll_post_id']) && $lang = $this->model->get_post_language($_REQUEST['pll_post_id']))
  52. $query->set('lang', $lang->slug);
  53. elseif (!empty($this->curlang))
  54. $qvars['lang'] = $this->curlang->slug;
  55. }
  56. if (isset($qvars['lang']) && 'all' === $qvars['lang'])
  57. unset ($qvars['lang']);
  58. }
  59. /*
  60. * adds the Language box in the 'Edit Post' and 'Edit Page' panels (as well as in custom post types panels)
  61. *
  62. * @since 0.1
  63. *
  64. * @param string $post_type
  65. */
  66. public function add_meta_boxes($post_type) {
  67. if ($this->model->is_translated_post_type($post_type))
  68. add_meta_box('ml_box', __('Languages','polylang'), array(&$this, 'post_language'), $post_type, 'side', 'high');
  69. }
  70. /*
  71. * displays the Languages metabox in the 'Edit Post' and 'Edit Page' panels
  72. *
  73. * @since 0.1
  74. */
  75. public function post_language() {
  76. global $post_ID;
  77. $post_id = $post_ID;
  78. $post_type = get_post_type($post_ID);
  79. $lang = ($lg = $this->model->get_post_language($post_ID)) ? $lg :
  80. (isset($_GET['new_lang']) ? $this->model->get_language($_GET['new_lang']) :
  81. $this->pref_lang);
  82. $dropdown = new PLL_Walker_Dropdown();
  83. wp_nonce_field('pll_language', '_pll_nonce');
  84. // NOTE: the class "tags-input" allows to include the field in the autosave $_POST (see autosave.js)
  85. printf('
  86. <p><strong>%s</strong></p>
  87. %s
  88. <div id="post-translations" class="translations">',
  89. __('Language', 'polylang'),
  90. $dropdown->walk($this->model->get_languages_list(), array(
  91. 'name' => $post_type == 'attachment' ? sprintf('attachments[%d][language]', $post_ID) : 'post_lang_choice',
  92. 'class' => $post_type == 'attachment' ? 'media_lang_choice' : 'tags-input',
  93. 'selected' => $lang ? $lang->slug : ''
  94. ))
  95. );
  96. if ($lang)
  97. include(PLL_ADMIN_INC.'/view-translations-'.($post_type == 'attachment' ? 'media' : 'post').'.php');
  98. echo '</div>'."\n";
  99. }
  100. /*
  101. * ajax response for changing the language in the post metabox
  102. *
  103. * @since 0.2
  104. */
  105. public function post_lang_choice() {
  106. check_ajax_referer('pll_language', '_pll_nonce');
  107. global $post_ID; // obliged to use the global variable for wp_popular_terms_checklist
  108. $post_ID = $_POST['post_id'];
  109. $post_type = get_post_type($post_ID);
  110. $lang = $this->model->get_language($_POST['lang']);
  111. $post_type_object = get_post_type_object($post_type);
  112. if (!current_user_can($post_type_object->cap->edit_posts) || !current_user_can($post_type_object->cap->create_posts))
  113. wp_die(-1);
  114. $this->model->set_post_language($post_ID, $lang); // save language, useful to set the language when uploading media from post
  115. ob_start();
  116. if ($lang)
  117. include(PLL_ADMIN_INC.'/view-translations-post.php');
  118. $x = new WP_Ajax_Response(array('what' => 'translations', 'data' => ob_get_contents()));
  119. ob_end_clean();
  120. // categories
  121. if (isset($_POST['taxonomies'])) {
  122. // not set for pages
  123. foreach ($_POST['taxonomies'] as $taxname) {
  124. $taxonomy = get_taxonomy($taxname);
  125. ob_start();
  126. $popular_ids = wp_popular_terms_checklist($taxonomy->name);
  127. $supplemental['populars'] = ob_get_contents();
  128. ob_end_clean();
  129. ob_start();
  130. // use $post_ID to remember ckecked terms in case we come back to the original language
  131. wp_terms_checklist( $post_ID, array( 'taxonomy' => $taxonomy->name, 'popular_cats' => $popular_ids ));
  132. $supplemental['all'] = ob_get_contents();
  133. ob_end_clean();
  134. $supplemental['dropdown'] = wp_dropdown_categories(array(
  135. 'taxonomy' => $taxonomy->name,
  136. 'hide_empty' => 0,
  137. 'name' => 'new'.$taxonomy->name.'_parent',
  138. 'orderby' => 'name',
  139. 'hierarchical' => 1,
  140. 'show_option_none' => '&mdash; '.$taxonomy->labels->parent_item.' &mdash;',
  141. 'echo' => 0
  142. ));
  143. $x->Add(array('what' => 'taxonomy', 'data' => $taxonomy->name, 'supplemental' => $supplemental));
  144. }
  145. }
  146. // parent dropdown list (only for hierarchical post types)
  147. if (in_array($post_type, get_post_types(array('hierarchical' => true)))) {
  148. require_once( ABSPATH . 'wp-admin/includes/meta-boxes.php' );
  149. ob_start();
  150. page_attributes_meta_box(get_post($post_ID));
  151. $x->Add(array('what' => 'pages', 'data' => ob_get_contents()));
  152. ob_end_clean();
  153. }
  154. $x->send();
  155. }
  156. /*
  157. * ajax response for input in translation autocomplete input box
  158. *
  159. * @since 1.5
  160. */
  161. public function ajax_posts_not_translated() {
  162. check_ajax_referer('pll_language', '_pll_nonce');
  163. $posts = get_posts(array(
  164. 's' => $_REQUEST['term'],
  165. 'suppress_filters' => 0, // to make the post_fields filter work
  166. 'lang' => 0, // avoid admin language filter
  167. 'numberposts' => 10, // limit to 10 posts
  168. 'post_status' => 'any',
  169. 'post_type' => $_REQUEST['post_type'],
  170. 'orderby' => 'title',
  171. 'order' => 'ASC',
  172. 'tax_query' => array(array(
  173. 'taxonomy' => 'language',
  174. 'field' => 'term_taxonomy_id', // WP 3.5+
  175. 'terms' => $this->model->get_language($_REQUEST['translation_language'])->term_taxonomy_id
  176. ))
  177. ));
  178. $return = array();
  179. foreach ($posts as $key => $post) {
  180. if (!$this->model->get_translation('post', $post->ID, $_REQUEST['post_language']))
  181. $return[] = array('id' => $post->ID, 'value' => $post->post_title, 'link' => $this->edit_translation_link($post->ID));
  182. }
  183. // add current translation in list
  184. if ($post_id = $this->model->get_translation('post', $_REQUEST['pll_post_id'],$_REQUEST['translation_language'])) {
  185. $post = get_post($post_id);
  186. array_unshift($return, array(
  187. 'id' => $post_id,
  188. 'value' => $post->post_title,
  189. 'link' => $this->edit_translation_link($post_id)
  190. ));
  191. }
  192. wp_die(json_encode($return));
  193. }
  194. /*
  195. * saves language
  196. * checks the terms saved are in the right language
  197. *
  198. * @since 1.5
  199. *
  200. * @param int $post_id
  201. * @param array $post
  202. */
  203. protected function save_language($post_id, $post) {
  204. // security checks are necessary to accept language modifications
  205. // as 'wp_insert_post' can be called from outside WP admin
  206. // edit post
  207. if (isset($_REQUEST['post_lang_choice'])) {
  208. check_admin_referer('pll_language', '_pll_nonce');
  209. $this->model->set_post_language($post_id, $lang = $_REQUEST['post_lang_choice']);
  210. }
  211. // quick edit and bulk edit
  212. elseif (isset($_REQUEST['inline_lang_choice'])) {
  213. // bulk edit does not modify the language
  214. if (isset($_REQUEST['bulk_edit']) && $_REQUEST['inline_lang_choice'] == -1)
  215. return;
  216. isset($_REQUEST['bulk_edit']) ? check_admin_referer('bulk-posts') : check_admin_referer('inlineeditnonce', '_inline_edit');
  217. if (($old_lang = $this->model->get_post_language($post_id)) && $old_lang->slug != $_REQUEST['inline_lang_choice'])
  218. $this->model->delete_translation('post', $post_id);
  219. $this->model->set_post_language($post_id, $lang = $_REQUEST['inline_lang_choice']);
  220. }
  221. // quick press
  222. // 'post-quickpress-save', 'post-quickpress-publish' = backward compatibility WP < 3.8
  223. elseif (isset($_REQUEST['action']) && in_array($_REQUEST['action'], array('post-quickpress-save', 'post-quickpress-publish', 'post-quickdraft-save'))) {
  224. check_admin_referer('add-' . $post->post_type);
  225. $this->model->set_post_language($post_id, $this->pref_lang); // default language for Quick draft
  226. }
  227. else
  228. $this->set_default_language($post_id);
  229. // make sure we get save terms in the right language (especially tags with same name in different languages)
  230. if (!empty($lang)) {
  231. // FIXME quite a lot of queries in foreach
  232. foreach ($this->model->get_translated_taxonomies() as $tax) {
  233. $terms = get_the_terms($post_id, $tax);
  234. if (is_array($terms)) {
  235. $newterms = array();
  236. foreach ($terms as $term) {
  237. if ($newterm = $this->model->term_exists($term->name, $tax, $term->parent, $lang))
  238. $newterms[] = (int) $newterm; // cast is important otherwise we get 'numeric' tags
  239. elseif (!is_wp_error($term_info = wp_insert_term($term->name, $tax))) // create the term in the correct language
  240. $newterms[] = (int) $term_info['term_id'];
  241. }
  242. wp_set_object_terms($post_id, $newterms, $tax);
  243. }
  244. }
  245. }
  246. }
  247. /*
  248. * called when a post (or page) is saved, published or updated
  249. * saves languages and translations
  250. *
  251. * @since 0.1
  252. *
  253. * @param int $post_id
  254. * @param object $post
  255. */
  256. public function save_post($post_id, $post) {
  257. // does nothing except on post types which are filterable
  258. if (!$this->model->is_translated_post_type($post->post_type))
  259. return;
  260. // capability check
  261. // as 'wp_insert_post' can be called from outside WP admin
  262. $post_type_object = get_post_type_object($post->post_type);
  263. if (!current_user_can($post_type_object->cap->edit_posts) || !current_user_can($post_type_object->cap->create_posts))
  264. wp_die( __( 'Cheatin&#8217; uh?' ) );
  265. if ($id = wp_is_post_revision($post_id))
  266. $post_id = $id;
  267. $this->save_language($post_id, $post);
  268. if (isset($_POST['post_tr_lang']))
  269. $translations = $this->save_translations($post_id, $_POST['post_tr_lang']);
  270. do_action('pll_save_post', $post_id, $post, empty($translations) ? $this->model->get_translations('post', $post_id) : $translations);
  271. }
  272. /*
  273. * called when a post, page or media is deleted
  274. * don't delete translations if this is a post revision thanks to AndyDeGroo who catched this bug
  275. * http://wordpress.org/support/topic/plugin-polylang-quick-edit-still-breaks-translation-linking-of-pages-in-072
  276. *
  277. * @since 0.1
  278. *
  279. * @param int $post_id
  280. */
  281. public function delete_post($post_id) {
  282. if (!wp_is_post_revision($post_id))
  283. $this->model->delete_translation('post', $post_id);
  284. }
  285. /*
  286. * filters the pages by language in the parent dropdown list in the page attributes metabox
  287. *
  288. * @since 0.6
  289. *
  290. * @param array $dropdown_args arguments passed to wp_dropdown_pages
  291. * @param object $post
  292. * @return array modified arguments
  293. */
  294. public function page_attributes_dropdown_pages_args($dropdown_args, $post) {
  295. $dropdown_args['lang'] = isset($_POST['lang']) ? $this->model->get_language($_POST['lang']) : $this->model->get_post_language($post->ID); // ajax or not ?
  296. if (!$dropdown_args['lang'])
  297. $dropdown_args['lang'] = $this->pref_lang;
  298. return $dropdown_args;
  299. }
  300. /*
  301. * returns html markup for a translation link
  302. *
  303. * @since 1.4
  304. *
  305. * @param int $post_id translation post id
  306. * @return string
  307. */
  308. public function edit_translation_link($post_id) {
  309. return sprintf(
  310. '<a href="%1$s" class="pll_icon_edit" title="%2$s"></a>',
  311. esc_url(get_edit_post_link($post_id)),
  312. __('Edit', 'polylang')
  313. );
  314. }
  315. }