PluginProbe
Polylang / 0.7
Polylang v0.7
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
polylang / include / admin.php

admin.php in Polylang 0.7, at include/admin.php

528 lines 19.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 require_once(PLL_INC.'/admin-filters.php');
4 require_once(PLL_INC.'/list-table.php');
5
6 // setups the Polylang admin panel and calls for other admin related classes
7 class Polylang_Admin extends Polylang_Base {
8 function __construct() {
9 new Polylang_Admin_Filters();
10
11 // adds a 'settings' link in the plugins table
12 $plugin_file = basename(POLYLANG_DIR).'/polylang.php';
13 add_filter('plugin_action_links_'.$plugin_file, array(&$this, 'plugin_action_links'));
14
15 // adds the link to the languages panel in the wordpress admin menu
16 add_action('admin_menu', array(&$this, 'add_menus'));
17
18 // ugrades languages files after a core upgrade (timing is important)
19 // FIXME private action ? is there a better way to do this ?
20 add_action( '_core_updated_successfully', array(&$this, 'upgrade_languages'), 1); // since WP 3.3
21 }
22
23 // adds a 'settings' link in the plugins table
24 function plugin_action_links($links) {
25 $settings_link = '<a href="admin.php?page=mlang">' . __('Settings') . '</a>';
26 array_unshift( $links, $settings_link );
27 return $links;
28 }
29
30 // adds the link to the languages panel in the wordpress admin menu
31 function add_menus() {
32 add_submenu_page('options-general.php', __('Languages', 'polylang'), __('Languages', 'polylang'), 'manage_options', 'mlang', array(&$this, 'languages_page'));
33 }
34
35 // used to update the translation when a language slug has been modified
36 function update_translations($type, $ids, $old_slug) {
37 foreach ($ids as $id) {
38 $tr = get_metadata($type, $id, '_translations', true);
39 if($tr) {
40 $tr = unserialize($tr);
41 $tr[$_POST['slug']] = $tr[$old_slug];
42 unset($tr[$old_slug]);
43 update_metadata($type, $id, '_translations', serialize($tr));
44 }
45 }
46 }
47
48 // used to delete the translation when a language is deleted
49 function delete_translations($type, $ids, $old_slug) {
50 foreach ($ids as $id) {
51 $tr = get_metadata($type, $id, '_translations', true);
52 if($tr) {
53 $tr = unserialize($tr);
54 unset($tr[$old_slug]);
55 update_metadata($type, $id, '_translations', serialize($tr));
56 }
57 }
58 }
59
60 // the languages panel
61 function languages_page() {
62 global $wp_rewrite;
63 $options = get_option('polylang');
64
65 // for nav menus form
66 $locations = get_registered_nav_menus();
67 $menus = wp_get_nav_menus();
68 $menu_lang = get_option('polylang_nav_menus');
69
70 // for widgets
71 $widget_lang = get_option('polylang_widgets');
72
73 $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
74
75 switch ($action) {
76 case 'add':
77 check_admin_referer( 'add-lang', '_wpnonce_add-lang' );
78 $error = $this->validate_lang();
79
80 if ($error == 0) {
81 $r = wp_insert_term($_POST['name'],'language',array('slug'=>$_POST['slug'], 'description'=>$_POST['description'], 'term_group'=>$_POST['term_group']));
82 update_metadata('term', $r['term_id'], '_rtl', $_POST['rtl']);
83
84 if (!isset($options['default_lang'])) { // if this is the first language created, set it as default language
85 $options['default_lang'] = $_POST['slug'];
86 update_option('polylang', $options);
87 }
88
89 $wp_rewrite->flush_rules(); // refresh rewrite rules
90
91 if (!$this->download_mo($_POST['description']))
92 $error = 5;
93 }
94
95 wp_redirect('admin.php?page=mlang'. ($error ? '&error='.$error : '') ); // to refresh the page (possible thanks to the $_GET['noheader']=true)
96 exit;
97 break;
98
99 case 'delete':
100 check_admin_referer('delete-lang');
101
102 if (isset($_GET['lang']) && $_GET['lang']) {
103 $lang_id = (int) $_GET['lang'];
104 $lang = $this->get_language($lang_id);
105 $lang_slug = $lang->slug;
106
107 // update the language slug in posts meta
108 $posts = get_posts(array('numberposts'=>-1, 'fields' => 'ids', 'meta_key'=>'_translations', 'post_type'=>'any', 'post_status'=>'any'));
109 $this->delete_translations('post', $posts, $lang_slug);
110
111 // update the language slug in categories & post tags meta
112 $terms= get_terms(get_taxonomies(array('show_ui'=>true)), array('get'=>'all', 'fields'=>'ids'));
113 $this->delete_translations('term', $terms, $lang_slug);
114
115 // FIXME should find something more efficient (with a sql query ?)
116 foreach ($terms as $id) {
117 if ($this->get_term_language($id)->term_id == $lang_id)
118 $this->delete_term_language($id); // delete language of this term
119 }
120
121 // delete menus locations
122 foreach ($locations as $location => $description)
123 unset($menu_lang[$location][$lang_slug]);
124 update_option('polylang_nav_menus', $menu_lang);
125
126 // delete language option in widgets
127 foreach ($widget_lang as $key=>$lang) {
128 if ($lang == $lang_slug)
129 unset ($widget_lang[$key]);
130 }
131 update_option('polylang_widgets', $widget_lang);
132
133 // delete the string translations
134 delete_option('polylang_mo'.$lang_id);
135
136 // delete the language itself
137 delete_metadata('term', $lang_id, '_rtl');
138 wp_delete_term($lang_id, 'language');
139
140 // oops ! we deleted the default language...
141 if ($options['default_lang'] == $lang_slug) {
142 if ($listlanguages = $this->get_languages_list())
143 $options['default_lang'] = $listlanguages[0]->slug; // arbitrary choice...
144 else
145 unset($options['default_lang']);
146 update_option('polylang', $options);
147
148 $wp_rewrite->flush_rules(); // refresh rewrite rules
149 }
150 }
151 wp_redirect('admin.php?page=mlang'); // to refresh the page (possible thanks to the $_GET['noheader']=true)
152 exit;
153 break;
154
155 case 'edit':
156 if (isset($_GET['lang']) && $_GET['lang']) {
157 $edit_lang = $this->get_language((int) $_GET['lang']);
158 $rtl = get_metadata('term', $edit_lang->term_id, '_rtl', true);
159 }
160 break;
161
162 case 'update':
163 check_admin_referer( 'add-lang', '_wpnonce_add-lang' );
164 $lang_id = (int) $_POST['lang_id'];
165 $lang = $this->get_language($lang_id);
166 $error = $this->validate_lang($lang);
167
168 if ($error == 0) {
169 // Update links to this language in posts and terms in case the slug has been modified
170 $old_slug = $lang->slug;
171
172 if ($old_slug != $_POST['slug']) {
173 // update the language slug in posts meta
174 $posts = get_posts(array('numberposts'=>-1, 'fields' => 'ids', 'meta_key'=>'_translations', 'post_type'=>'any', 'post_status'=>'any'));
175 $this->update_translations('post', $posts, $old_slug);
176
177 // update the language slug in categories & post tags meta
178 $terms = get_terms(get_taxonomies(array('show_ui'=>true)), array('get'=>'all', 'fields'=>'ids'));
179 $this->update_translations('term', $terms, $old_slug);
180
181 // update menus locations
182 foreach ($locations as $location => $description) {
183 if (isset($menu_lang[$location][$old_slug])) {
184 $menu_lang[$location][$_POST['slug']] = $menu_lang[$location][$old_slug];
185 unset($menu_lang[$location][$old_slug]);
186 }
187 }
188 update_option('polylang_nav_menus', $menu_lang);
189
190 // update language option in widgets
191 foreach ($widget_lang as $key=>$lang) {
192 if ($lang == $old_slug)
193 $widget_lang[$key] = $_POST['slug'];
194 }
195 update_option('polylang_widgets', $widget_lang);
196
197 // update the default language option if necessary
198 if ($options['default_lang'] == $old_slug) {
199 $options['default_lang'] = $_POST['slug'];
200 update_option('polylang', $options);
201 }
202 }
203
204 // and finally update the language itself
205 $args = array('name'=>$_POST['name'], 'slug'=>$_POST['slug'], 'description'=>$_POST['description'], 'term_group'=>$_POST['term_group']);
206 wp_update_term($lang_id, 'language', $args);
207 update_metadata('term', $lang_id, '_rtl', $_POST['rtl']);
208
209 $wp_rewrite->flush_rules(); // refresh rewrite rules
210 }
211
212 wp_redirect('admin.php?page=mlang'. ($error ? '&error='.$error : '') ); // to refresh the page (possible thanks to the $_GET['noheader']=true)
213 exit;
214 break;
215
216 case 'nav-menus':
217 check_admin_referer( 'nav-menus-lang', '_wpnonce_nav-menus-lang' );
218
219 $menu_lang = $_POST['menu-lang'];
220 foreach ($locations as $location => $description)
221 foreach ($this->get_switcher_options('menu') as $key => $str)
222 $menu_lang[$location][$key] = isset($menu_lang[$location][$key]) ? 1 : 0;
223
224 update_option('polylang_nav_menus', $menu_lang);
225 break;
226
227 case 'string-translation':
228 check_admin_referer( 'string-translation', '_wpnonce_string-translation' );
229
230 $strings = $this->get_strings();
231 $mo = new MO();
232
233 foreach ($this->get_languages_list() as $language) {
234 $reader = new POMO_StringReader(base64_decode(get_option('polylang_mo'.$language->term_id)));
235 $mo->import_from_reader($reader);
236
237 foreach ($_POST['translation'][$language->name] as $key=>$translation) {
238 $mo->add_entry($mo->make_entry($strings[$key]['string'], stripslashes($translation)));
239 }
240 // FIXME should I clean the mo object to remove unused strings ?
241 // use base64_encode to store binary mo data in database text field
242 global $wp_version;
243 if (version_compare($wp_version, '3.3', '<')) {
244 // backward compatibility for WP < 3.3
245 require_once(PLL_INC . '/mo.php');
246 update_option('polylang_mo'.$language->term_id, base64_encode(pll_mo_export($mo)));
247 }
248 else
249 update_option('polylang_mo'.$language->term_id, base64_encode($mo->export())); // MO::export since WP 3.3
250 }
251
252 $paged = isset($_GET['paged']) ? '&paged='.$_GET['paged'] : '';
253 wp_redirect('admin.php?page=mlang&tab=strings'.$paged); // to refresh the page (possible thanks to the $_GET['noheader']=true)
254 exit;
255 break;
256
257 case 'options':
258 check_admin_referer( 'options-lang', '_wpnonce_options-lang' );
259
260 $options['default_lang'] = $_POST['default_lang'];
261 $options['browser'] = isset($_POST['browser']) ? 1 : 0;
262 $options['rewrite'] = $_POST['rewrite'];
263 $options['hide_default'] = isset($_POST['hide_default']) ? 1 : 0;
264 $options['force_lang'] = isset($_POST['force_lang']) ? 1 : 0;
265 update_option('polylang', $options);
266
267 // refresh refresh permalink structure and rewrite rules in case rewrite or hide_default options have been modified
268 $wp_rewrite->extra_permastructs['language'][0] = $options['rewrite'] ? '%language%' : '/language/%language%';
269 $wp_rewrite->flush_rules();
270
271 // fills existing posts & terms with default language
272 if (isset($_POST['fill_languages'])) {
273 if(isset($_POST['posts'])) {
274 foreach(explode(',', $_POST['posts']) as $post_id) {
275 $this->set_post_language($post_id, $options['default_lang']);
276 }
277 }
278 if(isset($_POST['terms'])) {
279 foreach(explode(',', $_POST['terms']) as $term_id) {
280 $this->set_term_language($term_id, $options['default_lang']);
281 }
282 }
283 }
284 break;
285
286 default:
287 break;
288 }
289
290 // prepare the list of tabs
291 $tabs = array('lang' => __('Languages','polylang'));
292
293 // only if at least one language has been created
294 if ($listlanguages = $this->get_languages_list()) {
295 if (current_theme_supports('menus'))
296 $tabs['menus'] = __('Menus','polylang'); // don't display the menu tab if the active theme does not support nav menus
297
298 $tabs += array('strings' => __('Strings translation','polylang'), 'settings' => __('Settings', 'polylang'));
299 }
300
301 $active_tab = isset($_GET['tab']) && $_GET['tab'] ? $_GET['tab'] : 'lang';
302
303 switch($active_tab) {
304 case 'lang':
305 // prepare the list table of languages
306 $data = array();
307 foreach ($listlanguages as $lang)
308 $data[] = array_merge( (array) $lang, array('flag' => $this->get_flag($lang)) ) ;
309
310 $list_table = new Polylang_List_Table();
311 $list_table->prepare_items($data);
312
313 $rtl = 0;
314
315 // error messages for data validation
316 $errors[1] = __('Enter a valid WorPress locale', 'polylang');
317 $errors[2] = __('The language code must be 2 characters long', 'polylang');
318 $errors[3] = __('The language code must be unique', 'polylang');
319 $errors[4] = __('The language must have a name', 'polylang');
320 $errors[5] = __('The language was created, but the WordPress language file was not downloaded. Please install it manually.', 'polylang');
321 break;
322
323 case 'menus':
324 // default values
325 foreach ($locations as $key=>$location) {
326 if (isset($menu_lang[$key]))
327 $menu_lang[$key] = wp_parse_args($menu_lang[$key], $this->get_switcher_options('menu', 'default'));
328 }
329 break;
330
331 case 'strings':
332 // get the strings to translate
333 $data = $this->get_strings();
334
335 // load translations
336 $mo = new MO();
337 foreach ($listlanguages as $language) {
338 $reader = new POMO_StringReader(base64_decode(get_option('polylang_mo'.$language->term_id)));
339 $mo->import_from_reader($reader);
340
341 foreach ($data as $key=>$row) {
342 $data[$key]['translations'][$language->name] = $mo->translate($data[$key]['string']);
343 $data[$key]['row'] = $key; // store the row number for convenience
344 }
345 }
346
347 $string_table = new Polylang_String_Table();
348 $string_table->prepare_items($data);
349 break;
350
351 case 'settings':
352 //FIXME rework this as it would not be efficient in case of thousands posts or terms !
353 // detects posts & pages without language set
354 $q = array(
355 'numberposts'=>-1,
356 'post_type' => 'any',
357 'post_status'=>'any',
358 'fields' => 'ids',
359 'tax_query' => array(array(
360 'taxonomy'=> 'language',
361 'terms'=> get_terms('language', array('fields'=>'ids')),
362 'operator'=>'NOT IN'
363 ))
364 );
365 $posts = implode(',', get_posts($q));
366
367 // detects categories & post tags without language set
368 $terms = get_terms(get_taxonomies(array('show_ui'=>true)), array('get'=>'all', 'fields'=>'ids'));
369
370 foreach ($terms as $key => $term_id) {
371 if ($this->get_term_language($term_id))
372 unset($terms[$key]);
373 }
374 $terms = implode(',', $terms);
375 break;
376
377 default:
378 break;
379 }
380 // displays the page
381 include(PLL_INC.'/languages-form.php');
382 }
383
384 // validates data entered when creating or updating a language
385 function validate_lang($lang = null) {
386 // validate locale
387 $loc = $_POST['description'];
388 if ( !preg_match('#^[a-z]{2}$#', $loc) && !preg_match('#^[a-z]{2}_[A-Z]{2}$#', $loc) )
389 $error = 1;
390
391 // validate slug length
392 if (strlen($_POST['slug']) != 2)
393 $error = 2;
394
395 // validate slug is unique
396 if ($this->get_language($_POST['slug']) != null && isset($lang) && $lang->slug != $_POST['slug'])
397 $error = 3;
398
399 // validate name
400 if ($_POST['name'] == '')
401 $error = 4;
402
403 return isset($error) ? $error : 0;
404 }
405
406 // downloads mofiles
407 function download_mo($locale, $upgrade = false) {
408 global $wp_version;
409 $mofile = WP_LANG_DIR."/$locale.mo";
410
411 // does file exists ?
412 if ((file_exists($mofile) && !$upgrade) || $locale == 'en_US')
413 return true;
414
415 // does language directory exists ?
416 if(!is_dir(WP_LANG_DIR)) {
417 if(!@mkdir(WP_LANG_DIR))
418 return false;
419 }
420
421 // will first look in tags/ (most languages) then in branches/ (only Greek ?)
422 $base = 'http://svn.automattic.com/wordpress-i18n/'.$locale;
423 $bases = array($base.'/tags/', $base.'/branches/');
424
425 foreach ($bases as $base) {
426 // get all the versions available in the subdirectory
427 $resp = wp_remote_get($base);
428 if (is_wp_error($resp) || 200 != $resp['response']['code'])
429 continue;
430
431 preg_match_all('#>([0-9\.]+)\/#', $resp['body'], $matches);
432 if (empty($matches[1]))
433 continue;
434
435 rsort($matches[1]); // sort from newest to oldest
436 $versions = $matches[1];
437
438 $newest = $upgrade ? $upgrade : $wp_version;
439 foreach ($versions as $key=>$version) {
440 // will not try to download a too recent mofile
441 if (version_compare($version, $newest, '>'))
442 unset($versions[$key]);
443 // will not download an older version if we are upgrading
444 if ($upgrade && version_compare($version, $wp_version, '<='))
445 unset($versions[$key]);
446 }
447
448 $versions = array_splice($versions, 0, 5); // reduce the number of versions to test to 5
449 $args = array('timeout' => 30, 'stream' => true);
450
451 // try to download the file
452 foreach ($versions as $version) {
453 $resp = wp_remote_get($base."$version/messages/$locale.mo", $args + array('filename' => $mofile));
454 if (is_wp_error($resp) || 200 != $resp['response']['code'])
455 continue;
456
457 // try to download ms and continents-cities files if exist (will not return false if failed)
458 foreach (array("ms-$locale.mo", "continent-cities-$locale.mo") as $file)
459 wp_remote_get($base."$version/messages/$file", $args + array('filename' => WP_LANG_DIR."/$file"));
460
461 // try to download theme files if exist (will not return false if failed)
462 // FIXME not updated when the theme is updated outside a core update
463 foreach (array("twentyten", "twentyeleven") as $theme)
464 wp_remote_get($base."$version/messages/$theme/$locale.mo", $args + array('filename' => get_theme_root()."/$theme/languages/$locale.mo"));
465
466 return true;
467 }
468 }
469 // we did not succeeded to download a file :(
470 return false;
471 }
472
473 // ugrades languages files after a core upgrade
474 function upgrade_languages($version) {
475 apply_filters('update_feedback', __('Upgrading language files&#8230;', 'polylang'));
476 foreach ($this->get_languages_list() as $language)
477 $this->download_mo($language->description, $version);
478 }
479
480 // returns options available for the language switcher (menu or widget)
481 // FIXME do not include the dropdown in menu yet since I need to work on js
482 function get_switcher_options($type = 'widget', $key ='string') {
483 $options = array (
484 'show_names' => array('string' => __('Displays language names', 'polylang'), 'default' => 1),
485 'show_flags' => array('string' => __('Displays flags', 'polylang'), 'default' => 0),
486 'force_home' => array('string' => __('Forces link to front page', 'polylang'), 'default' => 0),
487 'hide_current' => array('string' => __('Hides the current language', 'polylang'), 'default' => 0),
488 );
489 $menu_options = array('switcher' => array('string' => __('Displays a language switcher at the end of the menu', 'polylang'), 'default' => 0));
490 $widget_options = array('dropdown' => array('string' => __('Displays as dropdown', 'polylang'), 'default' => 0));
491 $options = ($type == 'menu') ? array_merge($menu_options, $options) : array_merge($options, $widget_options);
492 return array_map(create_function('$v', "return \$v['$key'];"), $options);
493 }
494
495 function &get_strings() {
496 global $wp_registered_widgets;
497
498 // WP strings
499 $this->register_string(__('Site Title'), get_option('blogname'));
500 $this->register_string(__('Tagline'), get_option('blogdescription'));
501
502 // widgets titles
503 $sidebars = wp_get_sidebars_widgets();
504 foreach ($sidebars as $sidebar => $widgets) {
505 if ($sidebar == 'wp_inactive_widgets' || !isset($widgets))
506 continue;
507
508 foreach ($widgets as $widget) {
509 // nothing can be done if the widget is created using pre WP2.8 API :(
510 // there is no object, so we can't access it to get the widget options
511 // the second part of the test is probably useless
512 if (!isset($wp_registered_widgets[$widget]['callback'][0]) || !is_object($wp_registered_widgets[$widget]['callback'][0]))
513 continue;
514
515 $widget_settings = $wp_registered_widgets[$widget]['callback'][0]->get_settings();
516 $number = $wp_registered_widgets[$widget]['params'][0]['number'];
517 $title = $widget_settings[$number]['title'];
518 if(isset($title) && $title)
519 $this->register_string(__('Widget title', 'polylang'), $title);
520 }
521 }
522 return $this->strings;
523 }
524
525 } // class Polylang_Admin
526
527 ?>
528