PluginProbe
Polylang / 1.5
Polylang v1.5
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
← All changes | admin/admin.php +309 -132 2.91.5 View file →
@@ -1,123 +1,167 @@
1 1 <?php
2 -/**
3 - * @package Polylang
4 - */
5 2
6 -/**
7 - * Admin side controller
3 +/*
4 + * admin side controller
8 5 * accessible in $polylang global object
9 6 *
10 - * Properties:
11 - * options => inherited, reference to Polylang options array
12 - * model => inherited, reference to PLL_Model object
13 - * links_model => inherited, reference to PLL_Links_Model object
14 - * links => inherited, reference to PLL_Admin_Links object
15 - * static_pages => inherited, reference to PLL_Admin_Static_Pages object
16 - * filters_links => inherited, reference to PLL_Filters_Links object
17 - * curlang => inherited, optional, current language used to filter the content (language of the post or term being edited, equal to filter_lang otherwise)
18 - * filter_lang => inherited, optional, current status of the admin languages filter (in the admin bar)
19 - * pref_lang => inherited, preferred language used as default when saving posts or terms
20 - * posts => reference to PLL_CRUD_Posts object
21 - * terms => reference to PLL_CRUD_Terms object
22 - * filters => reference to PLL_Admin_Filters object
23 - * filters_sanitization => reference to PLL_Filters_Sanitization object
24 - * filters_columns => reference to PLL_Admin_Filters_Columns object
25 - * filters_post => reference to PLL_Admin_Filters_Post object
26 - * filters_term => reference to PLL_Admin_filters_Term object
27 - * nav_menu => reference to PLL_Admin_Nav_Menu object
28 - * block_editor => reference to PLL_Admin_Block_Editor object
29 - * classic_editor => reference to PLL_Admin_Classic_Editor object
30 - * filters_media => optional, reference to PLL_Admin_Filters_Media object
7 + * properties:
8 + * options => inherited, reference to Polylang options array
9 + * model => inherited, reference to PLL_Model object
10 + * links_model => inherited, reference to PLL_Links_Model object
11 + * settings_page => optional, reference ot PLL_Settings object
12 + * links => reference to PLL_Links object
13 + * curlang => optional, current language used to filter admin content
14 + * pref_lang => preferred language used as default when saving posts or terms
15 + * filters => reference to PLL_Filters object
16 + * filters_columns => reference to PLL_Admin_Filters_Columns object
17 + * filters_post => reference to PLL_Admin_Filters_Post object
18 + * filters_term => reference to PLL_Admin_filters_Term object
19 + * nav_menu => reference to PLL_Admin_Nav_Menu object
20 + * sync => reference to PLL_Admin_Sync object
21 + * filters_media => optional, reference to PLL_Admin_Filters_Media object
31 22 *
32 23 * @since 1.2
33 24 */
34 -class PLL_Admin extends PLL_Admin_Base {
35 - /**
36 - * Instance of PLL_Admin_Filters
25 +class PLL_Admin extends PLL_Base {
26 + public $curlang, $pref_lang;
27 + public $settings_page, $filters, $filters_columns, $filters_post, $filters_term, $nav_menu, $sync, $filters_media;
28 +
29 + /*
30 + * loads the polylang text domain
31 + * setups filters and action needed on all admin pages and on plugins page
37 32 *
38 - * @var PLL_Admin_Filters
33 + * @since 1.2
34 + *
35 + * @param object $links_model
39 36 */
40 - public $filters;
37 + public function __construct(&$links_model) {
38 + parent::__construct($links_model);
41 39
42 - /**
43 - * Instance of PLL_Admin_Filters_Columns
40 + // plugin i18n, only needed for backend
41 + load_plugin_textdomain('polylang', false, basename(POLYLANG_DIR).'/languages');
42 +
43 + // adds the link to the languages panel in the wordpress admin menu
44 + add_action('admin_menu', array(&$this, 'add_menus'));
45 +
46 + // setup js scripts and css styles
47 + add_action('admin_enqueue_scripts', array(&$this, 'admin_enqueue_scripts'));
48 + add_action('admin_print_footer_scripts', array(&$this, 'admin_print_footer_scripts'));
49 +
50 + // adds a 'settings' link in the plugins table
51 + add_filter('plugin_action_links_' . POLYLANG_BASENAME, array(&$this, 'plugin_action_links'));
52 + add_action('in_plugin_update_message-' . POLYLANG_BASENAME, array(&$this, 'plugin_update_message'), 10, 2);
53 + }
54 +
55 + /*
56 + * setups filters and action needed on all admin pages and on plugins page
57 + * loads the settings pages or the filters base on the request
44 58 *
45 - * @var PLL_Admin_Filters_Columns
59 + * @since 1.2
60 + *
61 + * @param object $links_model
46 62 */
47 - public $filters_columns;
63 + public function init() {
64 + if (PLL_SETTINGS)
65 + $this->settings_page = new PLL_Settings($this);
48 66
49 - /**
50 - * Instance of PLL_Admin_Filters_Post
51 - *
52 - * @var PLL_Admin_Filters_Post
53 - */
54 - public $filters_post;
67 + if (!$this->model->get_languages_list())
68 + return;
55 69
56 - /**
57 - * Instance of PLL_Admin_filters_Term
70 + $this->links = new PLL_Links($this);
71 +
72 + // filter admin language for users
73 + // we must not call user info before WordPress defines user roles in wp-settings.php
74 + add_filter('setup_theme', array(&$this, 'init_user'));
75 +
76 + // adds the languages in admin bar
77 + add_action('admin_bar_menu', array(&$this, 'admin_bar_menu'), 100); // 100 determines the position
78 +
79 + // setup filters for admin pages
80 + if (!PLL_SETTINGS)
81 + add_action('wp_loaded', array(&$this, 'add_filters'));
82 + }
83 +
84 + /*
85 + * adds the link to the languages panel in the wordpress admin menu
58 86 *
59 - * @var PLL_Admin_filters_Term
87 + * @since 0.1
60 88 */
61 - public $filters_term;
89 + public function add_menus() {
90 + add_submenu_page('options-general.php', $title = __('Languages', 'polylang'), $title, 'manage_options', 'mlang',
91 + PLL_SETTINGS ? array($this->settings_page, 'languages_page') : create_function('', ''));
92 + }
62 93
63 - /**
64 - * Instance of PLL_Admin_Nav_Menu
94 + /*
95 + * setup js scripts & css styles (only on the relevant pages)
65 96 *
66 - * @var PLL_Admin_Nav_Menu
97 + * @since 0.6
67 98 */
68 - public $nav_menu;
99 + public function admin_enqueue_scripts() {
100 + $screen = get_current_screen();
101 + $suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
69 102
70 - /**
71 - * Instance of PLL_Admin_Filters_Media
72 - *
73 - * @var PLL_Admin_Filters_Media
74 - */
75 - public $filters_media;
103 + // for each script:
104 + // 0 => the pages on which to load the script
105 + // 1 => the scripts it needs to work
106 + // 2 => 1 if loaded even if languages have not been defined yet, 0 otherwise
107 + // 3 => 1 if loaded in footer
108 + // FIXME: check if I can load more scripts in footer
109 + $scripts = array(
110 + 'admin' => array( array('settings_page_mlang'), array('jquery', 'wp-ajax-response', 'postbox'), 1 , 0),
111 + 'post' => array( array('post', 'media', 'async-upload', 'edit'), array('jquery', 'wp-ajax-response', 'inline-edit-post', 'post', 'jquery-ui-autocomplete'), 0 , 0),
112 + 'term' => array( array('edit-tags'), array('jquery', 'wp-ajax-response', 'jquery-ui-autocomplete'), 0, 1),
113 + 'user' => array( array('profile', 'user-edit'), array('jquery'), 0 , 0),
114 + );
76 115
77 - /**
78 - * Instance of PLL_Filters_Sanitization
79 - *
80 - * @since 2.9
81 - *
82 - * @var PLL_Filters_Sanitization
83 - */
84 - public $filters_sanitization;
116 + foreach ($scripts as $script => $v)
117 + if (in_array($screen->base, $v[0]) && ($v[2] || $this->model->get_languages_list()))
118 + wp_enqueue_script('pll_'.$script, POLYLANG_URL .'/js/'.$script.$suffix.'.js', $v[1], POLYLANG_VERSION, $v[3]);
85 119
86 - /**
87 - * Loads the polylang text domain
88 - * Setups filters and action needed on all admin pages and on plugins page
89 - *
90 - * @since 1.2
91 - *
92 - * @param object $links_model
93 - */
94 - public function __construct( &$links_model ) {
95 - parent::__construct( $links_model );
120 + wp_enqueue_style('polylang_admin', POLYLANG_URL .'/css/admin'.$suffix.'.css', array(), POLYLANG_VERSION);
96 121
97 - // Adds a 'settings' link in the plugins table
98 - add_filter( 'plugin_action_links_' . POLYLANG_BASENAME, array( $this, 'plugin_action_links' ) );
99 - add_action( 'in_plugin_update_message-' . POLYLANG_BASENAME, array( $this, 'plugin_update_message' ), 10, 2 );
122 + // backward compatibility WP < 3.8
123 + // don't load this for old versions
124 + if (version_compare($GLOBALS['wp_version'], '3.8alpha' , '>='))
125 + wp_enqueue_style('polylang_admin_mobi', POLYLANG_URL .'/css/admin-mobi'.$suffix.'.css', array(), POLYLANG_VERSION);
100 126 }
101 127
102 - /**
103 - * Setups filters and action needed on all admin pages and on plugins page
104 - * Loads the settings pages or the filters base on the request
128 + /*
129 + * sets pll_ajax_backend on all backend ajax request
105 130 *
106 - * @since 1.2
131 + * @since 1.4
107 132 */
108 - public function init() {
109 - parent::init();
133 + public function admin_print_footer_scripts() {
134 + global $post_ID;
135 + $params = array('pll_ajax_backend' => 1);
136 + if (!empty($post_ID))
137 + $params = array_merge($params, array('pll_post_id' => $post_ID));
110 138
111 - // Setup filters for admin pages
112 - // Priority 5 to make sure filters are there before customize_register is fired
113 - if ( $this->model->get_languages_list() ) {
114 - add_action( 'wp_loaded', array( $this, 'add_filters' ), 5 );
139 + $str = $arr = '';
140 + foreach ($params as $k => $v) {
141 + $str .= $k . '=' . $v . '&';
142 + $arr .= (empty($arr) ? '' : ', ') . $k . ': ' . $v;
115 143 }
144 +?>
145 +<script type="text/javascript">
146 + if (typeof jQuery != 'undefined') {
147 + (function($){
148 + $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
149 + if (typeof options.data == 'string') {
150 + options.data = '<?php echo $str;?>'+options.data;
151 + }
152 + else {
153 + options.data = $.extend(options.data, {<?php echo $arr;?>});
154 + }
155 + });
156 + })(jQuery)
116 157 }
158 +</script><?php
117 159
118 - /**
119 - * Adds a 'settings' link in the plugins table
160 + }
161 +
162 + /*
163 + * adds a 'settings' link in the plugins table
120 164 *
121 165 * @since 0.1
122 166 *
123 167 * @param array $links list of links associated to the plugin
@@ -122,77 +166,210 @@
122 166 *
123 167 * @param array $links list of links associated to the plugin
124 168 * @return array modified list of links
125 169 */
126 - public function plugin_action_links( $links ) {
127 - array_unshift( $links, '<a href="admin.php?page=mlang">' . __( 'Settings', 'polylang' ) . '</a>' );
170 + public function plugin_action_links($links) {
171 + array_unshift($links, '<a href="admin.php?page=mlang">' . __('Settings', 'polylang') . '</a>');
128 172 return $links;
129 173 }
130 174
131 - /**
132 - * Adds the upgrade notice in plugins table
175 + /*
176 + * adds the upgrade notice in plugins table
133 177 *
134 178 * @since 1.1.6
135 179 *
136 - * @param array $plugin_data Not used
137 - * @param object $r Plugin update data
180 + * @param array $plugin_data not used
181 + * @param object $r plugin update data
138 182 */
139 - public function plugin_update_message( $plugin_data, $r ) {
140 - if ( isset( $r->upgrade_notice ) ) {
141 - printf( '<p style="margin: 3px 0 0 0; border-top: 1px solid #ddd; padding-top: 3px">%s</p>', esc_html( $r->upgrade_notice ) );
183 + function plugin_update_message($plugin_data, $r) {
184 + if (isset($r->upgrade_notice))
185 + printf('<p style="margin: 3px 0 0 0; border-top: 1px solid #ddd; padding-top: 3px">%s</p>', $r->upgrade_notice);
186 + }
187 +
188 + /*
189 + * defines the backend language and the admin language filter based on user preferences
190 + *
191 + * @since 1.2.3
192 + */
193 + public function init_user() {
194 + // backend locale
195 + add_filter('locale', array(&$this, 'get_locale'));
196 +
197 + // language for admin language filter: may be empty
198 + // $_GET['lang'] is numeric when editing a language, not when selecting a new language in the filter
199 + if (!defined('DOING_AJAX') && !empty($_GET['lang']) && !is_numeric($_GET['lang']) && current_user_can('edit_user', $user_id = get_current_user_id()))
200 + update_user_meta($user_id, 'pll_filter_content', ($lang = $this->model->get_language($_GET['lang'])) ? $lang->slug : '');
201 +
202 + $this->curlang = $this->model->get_language(get_user_meta(get_current_user_id(), 'pll_filter_content', true));
203 +
204 + // set preferred language for use when saving posts and terms: must not be empty
205 + $this->pref_lang = empty($this->curlang) ? $this->model->get_language($this->options['default_lang']) : $this->curlang;
206 + $this->pref_lang = apply_filters('pll_admin_preferred_language', $this->pref_lang);
207 +
208 + // inform that the admin language has been set
209 + // only if the admin language is one of the Polylang defined language
210 + if ($curlang = $this->model->get_language(get_locale())) {
211 + $GLOBALS['text_direction'] = $curlang->is_rtl ? 'rtl' : 'ltr'; // force text direction according to language setting
212 + do_action('pll_language_defined', $curlang->slug, $curlang);
142 213 }
214 + else
215 + do_action('pll_no_language_defined'); // to load overriden textdomains
143 216 }
144 217
145 - /**
146 - * Setup filters for admin pages
218 + /*
219 + * get the locale based on user preference
147 220 *
221 + * @since 0.4
222 + *
223 + * @param string $locale
224 + * @return string modified locale
225 + */
226 + public function get_locale($locale) {
227 + return ($loc = get_user_meta(get_current_user_id(), 'user_lang', 'true')) ? $loc : $locale;
228 + }
229 +
230 + /*
231 + * setup filters for admin pages
232 + *
148 233 * @since 1.2
149 - * @since 2.7 instantiate a PLL_Bulk_Translate instance.
150 234 */
151 235 public function add_filters() {
152 - $this->filters_sanitization = new PLL_Filters_Sanitization( $this->get_locale_for_sanitization() );
153 - // All these are separated just for convenience and maintainability
154 - $classes = array( 'Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Classic_Editor', 'Block_Editor' );
236 + // all these are separated just for convenience and maintainability
237 + $classes = array('Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Sync');
155 238
156 - // Don't load media filters if option is disabled or if user has no right
157 - if ( $this->options['media_support'] && ( $obj = get_post_type_object( 'attachment' ) ) && ( current_user_can( $obj->cap->edit_posts ) || current_user_can( $obj->cap->create_posts ) ) ) {
239 + // don't load media filters if option is disabled or if user has no right
240 + if ($this->options['media_support'] && ($obj = get_post_type_object('attachment')) && current_user_can($obj->cap->edit_posts) && current_user_can($obj->cap->create_posts))
158 241 $classes[] = 'Filters_Media';
242 +
243 + foreach ($classes as $class) {
244 + $obj = strtolower($class);
245 + $class = apply_filters('pll_' . $obj, 'PLL_Admin_' . $class);
246 + $this->$obj = new $class($this);
159 247 }
248 + }
160 249
161 - foreach ( $classes as $class ) {
162 - $obj = strtolower( $class );
250 + /*
251 + * adds the languages list in admin bar for the admin languages filter
252 + *
253 + * @since 0.9
254 + *
255 + * @param object $wp_admin_bar
256 + */
257 + public function admin_bar_menu($wp_admin_bar) {
258 + $url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
163 259
164 - /**
165 - * Filter the class to instantiate when loading admin filters
166 - *
167 - * @since 1.5
168 - *
169 - * @param string $class class name
170 - */
171 - $class = apply_filters( 'pll_' . $obj, 'PLL_Admin_' . $class );
172 - $this->$obj = new $class( $this );
260 + $all_item = (object) array(
261 + 'slug' => 'all',
262 + 'name' => __('Show all languages', 'polylang'),
263 + 'flag' => '<span class="ab-icon"></span>'
264 + );
265 +
266 + $selected = empty($this->curlang) ? $all_item : $this->curlang;
267 +
268 + $wp_admin_bar->add_menu(array(
269 + 'id' => 'languages',
270 + 'title' => $selected->flag . '<span class="ab-label">'. esc_html($selected->name) . '</span>',
271 + 'meta' => array('title' => __('Filters content by language', 'polylang')),
272 + ));
273 +
274 + foreach (array_merge(array($all_item), $this->model->get_languages_list()) as $lang) {
275 + if ($selected->slug == $lang->slug)
276 + continue;
277 +
278 + $img = empty($lang->flag) ? '' : (false !== strpos($lang->flag, 'img') ? $lang->flag . '&nbsp;' : $lang->flag);
279 +
280 + $wp_admin_bar->add_menu(array(
281 + 'parent' => 'languages',
282 + 'id' => $lang->slug,
283 + 'title' => $lang->flag . esc_html($lang->name),
284 + 'href' => esc_url(add_query_arg('lang', $lang->slug, remove_query_arg('paged',$url))),
285 + ));
173 286 }
174 287 }
175 - /**
176 - * Retrieve the locale according to the current language instead of the language
177 - * of the admin interface.
288 + /*
289 + * downloads mofiles from http://svn.automattic.com/wordpress-i18n/
290 + * FIXME is it the best class for this?
291 + * FIXME use language packs API coming with WP 3.7 instead (does not seem to work fully yet)
178 292 *
179 - * @since 2.0
293 + * @since 0.6
180 294 *
181 - * @return string
295 + * @param string $locale locale to download
296 + * @param bool $upgrade optional true if this is an upgrade, false if this is the first download, defaults to false
297 + * @return bool true on success, false otherwise
182 298 */
183 - public function get_locale_for_sanitization() {
184 - $locale = get_locale();
299 + static public function download_mo($locale, $upgrade = false) {
300 + global $wp_version;
301 + $mofile = WP_LANG_DIR."/$locale.mo";
185 302
186 - if ( isset( $_POST['post_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['post_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
187 - $locale = $lang->locale;
188 - } elseif ( isset( $_POST['term_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['term_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
189 - $locale = $lang->locale;
190 - } elseif ( isset( $_POST['inline_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
191 - $locale = $lang->locale;
192 - } elseif ( ! empty( $this->curlang ) ) {
193 - $locale = $this->curlang->locale;
303 + // does file exists ?
304 + if ((file_exists($mofile) && !$upgrade) || $locale == 'en_US')
305 + return true;
306 +
307 + // does language directory exists ?
308 + if (!is_dir(WP_LANG_DIR)) {
309 + if (!@mkdir(WP_LANG_DIR))
310 + return false;
194 311 }
195 312
196 - return $locale;
313 + // will first look in tags/ (most languages) then in branches/ (only Greek ?)
314 + $base = 'http://svn.automattic.com/wordpress-i18n/'.$locale;
315 + $bases = array($base.'/tags/', $base.'/branches/');
316 +
317 + foreach ($bases as $base) {
318 + // get all the versions available in the subdirectory
319 + $resp = wp_remote_get($base);
320 + if (is_wp_error($resp) || 200 != $resp['response']['code'])
321 + continue;
322 +
323 + preg_match_all('#>([0-9\.]+)\/#', $resp['body'], $matches);
324 + if (empty($matches[1]))
325 + continue;
326 +
327 + rsort($matches[1]); // sort from newest to oldest
328 + $versions = $matches[1];
329 +
330 + $newest = $upgrade ? $upgrade : $wp_version;
331 + foreach ($versions as $key=>$version) {
332 + // will not try to download a too recent mofile
333 + if (version_compare($version, $newest, '>'))
334 + unset($versions[$key]);
335 + // will not download an older version if we are upgrading
336 + if ($upgrade && version_compare($version, $wp_version, '<='))
337 + unset($versions[$key]);
338 + }
339 +
340 + $versions = array_splice($versions, 0, 5); // reduce the number of versions to test to 5
341 + $args = array('timeout' => 30, 'stream' => true);
342 +
343 + // try to download the file
344 + foreach ($versions as $version) {
345 + $resp = wp_remote_get($base."$version/messages/$locale.mo", $args + array('filename' => $mofile));
346 + if (is_wp_error($resp) || 200 != $resp['response']['code']) {
347 + unlink($mofile); // otherwise we download a gzipped 404 page
348 + continue;
349 + }
350 + // try to download ms and continents-cities files if exist (will not return false if failed)
351 + // with new files introduced in WP 3.4
352 + foreach (array('ms', 'continents-cities', 'admin', 'admin-network') as $file) {
353 + $resp = wp_remote_get($base."$version/messages/$file-$locale.mo", $args + array('filename' => WP_LANG_DIR."/$file-$locale.mo"));
354 + if (is_wp_error($resp) || 200 != $resp['response']['code'])
355 + unlink(WP_LANG_DIR."/$file-$locale.mo");
356 + }
357 + // try to download theme files if exist (will not return false if failed)
358 + // FIXME not updated when the theme is updated outside a core update
359 + foreach (array('twentyten', 'twentyeleven', 'twentytwelve', 'twentythirteen', 'twentyfourteen') as $theme) {
360 + if (!is_dir($theme_dir = get_theme_root()."/$theme/languages"))
361 + continue; // the theme is not present
362 +
363 + $resp = wp_remote_get($base."$version/messages/$theme/$locale.mo", $args + array('filename' => "$theme_dir/$locale.mo"));
364 + if (is_wp_error($resp) || 200 != $resp['response']['code'])
365 + unlink("$theme_dir/$locale.mo");
366 + }
367 + return true;
368 + }
369 + }
370 +
371 + // we did not succeeded to download a file :(
372 + add_settings_error('general', 'pll_download_mo', __('The language was created, but the WordPress language file was not downloaded. Please install it manually.', 'polylang'));
373 + return false;
197 374 }
198 375 }