PluginProbe
Polylang / 1.5.2
Polylang v1.5.2
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 +312 -117 3.0.21.5.2 View file →
@@ -1,180 +1,375 @@
1 1 <?php
2 -/**
3 - * @package Polylang
4 - */
5 2
6 -/**
7 - * Main Polylang class for admin (except Polylang pages), accessible from @see PLL().
3 +/*
4 + * admin side controller
5 + * accessible in $polylang global object
8 6 *
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
22 + *
9 23 * @since 1.2
10 24 */
11 -class PLL_Admin extends PLL_Admin_Base {
12 - /**
13 - * @var 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
32 + *
33 + * @since 1.2
34 + *
35 + * @param object $links_model
14 36 */
15 - public $filters;
37 + public function __construct(&$links_model) {
38 + parent::__construct($links_model);
16 39
17 - /**
18 - * @var PLL_Admin_Filters_Columns
19 - */
20 - public $filters_columns;
40 + // plugin i18n, only needed for backend
41 + load_plugin_textdomain('polylang', false, basename(POLYLANG_DIR).'/languages');
21 42
22 - /**
23 - * @var PLL_Admin_Filters_Post
24 - */
25 - public $filters_post;
43 + // adds the link to the languages panel in the wordpress admin menu
44 + add_action('admin_menu', array(&$this, 'add_menus'));
26 45
27 - /**
28 - * @var PLL_Admin_Filters_Term
29 - */
30 - public $filters_term;
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'));
31 49
32 - /**
33 - * @var PLL_Admin_Filters_Media
34 - */
35 - public $filters_media;
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 + }
36 54
37 - /**
38 - * @since 2.9
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
39 58 *
40 - * @var PLL_Filters_Sanitization
59 + * @since 1.2
60 + *
61 + * @param object $links_model
41 62 */
42 - public $filters_sanitization;
63 + public function init() {
64 + if (PLL_SETTINGS)
65 + $this->settings_page = new PLL_Settings($this);
43 66
44 - /**
45 - * @var PLL_Admin_Block_Editor
46 - */
47 - public $block_editor;
67 + if (!$this->model->get_languages_list())
68 + return;
48 69
49 - /**
50 - * @var PLL_Admin_Classic_Editor
51 - */
52 - public $classic_editor;
70 + $this->links = new PLL_Links($this);
53 71
54 - /**
55 - * @var PLL_Admin_Nav_Menu
56 - */
57 - public $nav_menu;
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'));
58 75
59 - /**
60 - * @var PLL_Admin_Filters_Widgets_Options
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
86 + *
87 + * @since 0.1
61 88 */
62 - public $filters_widgets;
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 + }
63 93
64 - /**
65 - * Setups filters and action needed on all admin pages and on plugins page.
94 + /*
95 + * setup js scripts & css styles (only on the relevant pages)
66 96 *
67 - * @since 1.2
68 - *
69 - * @param PLL_Links_Model $links_model Reference to the links model.
97 + * @since 0.6
70 98 */
71 - public function __construct( &$links_model ) {
72 - parent::__construct( $links_model );
99 + public function admin_enqueue_scripts() {
100 + $screen = get_current_screen();
101 + $suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
73 102
74 - // Adds a 'settings' link in the plugins table
75 - add_filter( 'plugin_action_links_' . POLYLANG_BASENAME, array( $this, 'plugin_action_links' ) );
76 - add_action( 'in_plugin_update_message-' . POLYLANG_BASENAME, array( $this, 'plugin_update_message' ), 10, 2 );
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 + );
115 +
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]);
119 +
120 + wp_enqueue_style('polylang_admin', POLYLANG_URL .'/css/admin'.$suffix.'.css', array(), POLYLANG_VERSION);
121 +
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);
77 126 }
78 127
79 - /**
80 - * Setups filters and action needed on all admin pages and on plugins page
81 - * Loads the settings pages or the filters base on the request
128 + /*
129 + * sets pll_ajax_backend on all backend ajax request
82 130 *
83 - * @since 1.2
131 + * @since 1.4
84 132 */
85 - public function init() {
86 - 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));
87 138
88 - // Setup filters for admin pages
89 - // Priority 5 to make sure filters are there before customize_register is fired
90 - if ( $this->model->get_languages_list() ) {
91 - 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;
92 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)
93 157 }
158 +</script><?php
94 159
95 - /**
96 - * Adds a 'settings' link for our plugin in the plugins list table.
160 + }
161 +
162 + /*
163 + * adds a 'settings' link in the plugins table
97 164 *
98 165 * @since 0.1
99 166 *
100 - * @param string[] $links List of links associated to the plugin.
101 - * @return string[] Modified list of links.
167 + * @param array $links list of links associated to the plugin
168 + * @return array modified list of links
102 169 */
103 - public function plugin_action_links( $links ) {
104 - 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>');
105 172 return $links;
106 173 }
107 174
108 - /**
109 - * Adds the upgrade notice in plugins table
175 + /*
176 + * adds the upgrade notice in plugins table
110 177 *
111 178 * @since 1.1.6
112 179 *
113 - * @param array $plugin_data Not used
114 - * @param object $r Plugin update data
115 - * @return void
180 + * @param array $plugin_data not used
181 + * @param object $r plugin update data
116 182 */
117 - public function plugin_update_message( $plugin_data, $r ) {
118 - if ( isset( $r->upgrade_notice ) ) {
119 - 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);
120 213 }
214 + else
215 + do_action('pll_no_language_defined'); // to load overriden textdomains
121 216 }
122 217
123 - /**
124 - * Setup filters for admin pages
218 + /*
219 + * get the locale based on user preference
125 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 + *
126 233 * @since 1.2
127 - * @since 2.7 instantiate a PLL_Bulk_Translate instance.
128 - * @return void
129 234 */
130 235 public function add_filters() {
131 - $this->filters_sanitization = new PLL_Filters_Sanitization( $this->get_locale_for_sanitization() );
132 - $this->filters_widgets = new PLL_Admin_Filters_Widgets_Options( $this );
236 + // all these are separated just for convenience and maintainability
237 + $classes = array('Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Sync');
133 238
134 - // All these are separated just for convenience and maintainability
135 - $classes = array( 'Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Classic_Editor', 'Block_Editor' );
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))
241 + $classes[] = 'Filters_Media';
136 242
137 - // Don't load media filters if option is disabled or if user has no right
138 - 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 ) ) ) {
139 - $classes[] = 'Filters_Media';
243 + foreach ($classes as $class) {
244 + $obj = strtolower($class);
245 + $class = apply_filters('pll_' . $obj, 'PLL_Admin_' . $class);
246 + $this->$obj = new $class($this);
140 247 }
248 + }
141 249
142 - foreach ( $classes as $class ) {
143 - $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'];
144 259
145 - /**
146 - * Filter the class to instantiate when loading admin filters
147 - *
148 - * @since 1.5
149 - *
150 - * @param string $class class name
151 - */
152 - $class = apply_filters( 'pll_' . $obj, 'PLL_Admin_' . $class );
153 - $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 + ));
154 286 }
155 287 }
156 -
157 - /**
158 - * Retrieve the locale according to the current language instead of the language
159 - * 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)
160 292 *
161 - * @since 2.0
293 + * @since 0.6
162 294 *
163 - * @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
164 298 */
165 - public function get_locale_for_sanitization() {
166 - $locale = get_locale();
299 + static public function download_mo($locale, $upgrade = false) {
300 + global $wp_version;
301 + $mofile = WP_LANG_DIR."/$locale.mo";
167 302
168 - if ( isset( $_POST['post_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['post_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
169 - $locale = $lang->locale;
170 - } elseif ( isset( $_POST['term_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['term_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
171 - $locale = $lang->locale;
172 - } elseif ( isset( $_POST['inline_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
173 - $locale = $lang->locale;
174 - } elseif ( ! empty( $this->curlang ) ) {
175 - $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;
176 311 }
177 312
178 - 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;
179 374 }
180 375 }