PluginProbe
Polylang / 1.0.1
Polylang v1.0.1
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 1.0.1, at include/admin.php

479 lines 17.3 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.'/list-table.php');
4
5 // setups the Polylang admin panel
6 class Polylang_Admin extends Polylang_Admin_Base {
7
8 function __construct() {
9 parent::__construct();
10
11 // adds screen options and the about box in the languages admin panel
12 add_action('load-settings_page_mlang', array(&$this, 'load_page'));
13
14 // saves per-page value in screen option
15 add_filter('set-screen-option', create_function('$s, $o, $v', 'return $v;'), 10, 3);
16 }
17
18 // adds screen options and the about box in the languages admin panel
19 function load_page() {
20 // test of $_GET['tab'] avoids displaying the automatically generated screen options on other tabs
21 if ((!defined('PLL_DISPLAY_ABOUT') || PLL_DISPLAY_ABOUT) && (!isset($_GET['tab']) || $_GET['tab'] == 'lang')) {
22 add_meta_box('pll_about_box', __('About Polylang', 'polylang'), create_function('', "include(PLL_INC.'/about.php');"), 'settings_page_mlang', 'normal');
23 add_screen_option('per_page', array('label' => __('Languages', 'polylang'), 'default' => 10, 'option' => 'pll_lang_per_page'));
24 }
25
26 if (isset($_GET['tab']) && $_GET['tab'] == 'strings')
27 add_screen_option('per_page', array('label' => __('Strings translations', 'polylang'), 'default' => 10, 'option' => 'pll_strings_per_page'));
28 }
29
30 // used to update the translation when a language slug has been modified
31 function update_translations($type, $ids, $old_slug) {
32 foreach ($ids as $id) {
33 if ($tr = $this->get_translations($type, $id)) {
34 $tr[$_POST['slug']] = $tr[$old_slug];
35 unset($tr[$old_slug]);
36 update_metadata($type, (int) $id, '_translations', $tr);
37 }
38 }
39 }
40
41 // used to delete the translation when a language is deleted
42 function delete_translations($type, $ids, $old_slug) {
43 foreach ($ids as $id) {
44 if ($tr = $this->get_translations($type, $id)) {
45 unset($tr[$old_slug]);
46 update_metadata($type, (int) $id, '_translations', $tr);
47 }
48 }
49 }
50
51 // the languages panel
52 function languages_page() {
53 $options = get_option('polylang');
54
55 // for nav menus form
56 $locations = get_registered_nav_menus();
57 $menus = wp_get_nav_menus();
58 $menu_lang = get_option('polylang_nav_menus');
59
60 // for widgets
61 $widget_lang = get_option('polylang_widgets', array()); // returns empty array if not set yet
62
63 $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
64
65 switch ($action) {
66 case 'add':
67 check_admin_referer( 'add-lang', '_wpnonce_add-lang' );
68 $error = $this->validate_lang();
69
70 if ($error == 0) {
71 $r = wp_insert_term($_POST['name'], 'language', array('slug' => $_POST['slug'], 'description' => $_POST['description']));
72 wp_update_term($r['term_id'], 'language', array('term_group' => $_POST['term_group'])); // can't set the term group directly in wp_insert_term
73 update_metadata('term', $r['term_id'], '_rtl', $_POST['rtl']);
74
75 if (!isset($options['default_lang'])) { // if this is the first language created, set it as default language
76 $options['default_lang'] = $_POST['slug'];
77 update_option('polylang', $options);
78 }
79
80 flush_rewrite_rules(); // refresh rewrite rules
81
82 if (!$this->download_mo($_POST['description']))
83 $error = 5;
84 }
85
86 wp_redirect('admin.php?page=mlang'. ($error ? '&error='.$error : '') ); // to refresh the page (possible thanks to the $_GET['noheader']=true)
87 exit;
88 break;
89
90 case 'delete':
91 check_admin_referer('delete-lang');
92
93 if (!empty($_GET['lang'])) {
94 $lang_id = (int) $_GET['lang'];
95 $lang = $this->get_language($lang_id);
96 $lang_slug = $lang->slug;
97
98 // update the language slug in posts meta
99 $posts = get_posts(array('numberposts'=>-1, 'fields' => 'ids', 'meta_key'=>'_translations', 'post_type'=>'any', 'post_status'=>'any'));
100 $this->delete_translations('post', $posts, $lang_slug);
101
102 // update the language slug in categories & post tags meta
103 $terms = get_terms($this->taxonomies, array('get'=>'all', 'fields'=>'ids'));
104 $this->delete_translations('term', $terms, $lang_slug);
105
106 // FIXME should find something more efficient (with a sql query ?)
107 foreach ($terms as $id) {
108 if (($lg = $this->get_term_language($id)) && $lg->term_id == $lang_id)
109 $this->delete_term_language($id); // delete language of this term
110 }
111
112 // delete menus locations
113 foreach (array_keys($locations) as $location) {
114 if (isset($menu_lang[$location][$lang_slug]))
115 unset($menu_lang[$location][$lang_slug]);
116 }
117 update_option('polylang_nav_menus', $menu_lang);
118
119 // delete language option in widgets
120 foreach ($widget_lang as $key=>$slug) {
121 if ($slug == $lang_slug)
122 unset ($widget_lang[$key]);
123 }
124 update_option('polylang_widgets', $widget_lang);
125
126 // delete users options
127 foreach (get_users(array('fields' => 'ID')) as $user_id) {
128 delete_user_meta($user_id, 'user_lang', $lang->description);
129 delete_user_meta($user_id, 'pll_filter_content', $lang_slug);
130 delete_user_meta($user_id, 'description_'.$lang_slug);
131 }
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
149 flush_rewrite_rules(); // refresh rewrite rules
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 (!empty($_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(
175 'numberposts' => -1,
176 'fields' => 'ids',
177 'meta_key' => '_translations',
178 'post_type' => 'any',
179 'post_status' => 'any'
180 ));
181 $this->update_translations('post', $posts, $old_slug);
182
183 // update the language slug in categories & post tags meta
184 $terms = get_terms($this->taxonomies, array('get'=>'all', 'fields'=>'ids'));
185 $this->update_translations('term', $terms, $old_slug);
186
187 // update menus locations
188 foreach (array_keys($locations) as $location) {
189 if (isset($menu_lang[$location][$old_slug])) {
190 $menu_lang[$location][$_POST['slug']] = $menu_lang[$location][$old_slug];
191 unset($menu_lang[$location][$old_slug]);
192 }
193 }
194 update_option('polylang_nav_menus', $menu_lang);
195
196 // update language option in widgets
197 foreach ($widget_lang as $key=>$lang) {
198 if ($lang == $old_slug)
199 $widget_lang[$key] = $_POST['slug'];
200 }
201 update_option('polylang_widgets', $widget_lang);
202
203 // update the default language option if necessary
204 if ($options['default_lang'] == $old_slug) {
205 $options['default_lang'] = $_POST['slug'];
206 update_option('polylang', $options);
207 }
208 }
209
210 // and finally update the language itself
211 $args = array(
212 'name' => $_POST['name'],
213 'slug' => $_POST['slug'],
214 'description' => $_POST['description'],
215 'term_group' => $_POST['term_group']
216 );
217 wp_update_term($lang_id, 'language', $args);
218 update_metadata('term', $lang_id, '_rtl', $_POST['rtl']);
219
220 flush_rewrite_rules(); // refresh rewrite rules
221 }
222
223 wp_redirect('admin.php?page=mlang'. ($error ? '&error='.$error : '') ); // to refresh the page (possible thanks to the $_GET['noheader']=true)
224 exit;
225 break;
226
227 case 'nav-menus':
228 check_admin_referer( 'nav-menus-lang', '_wpnonce_nav-menus-lang' );
229
230 $menu_lang = $_POST['menu-lang'];
231 foreach (array_keys($locations) as $location)
232 foreach (array_keys($this->get_switcher_options('menu')) as $key)
233 $menu_lang[$location][$key] = isset($menu_lang[$location][$key]) ? 1 : 0;
234
235 update_option('polylang_nav_menus', $menu_lang);
236 break;
237
238 case 'string-translation':
239 check_admin_referer( 'string-translation', '_wpnonce_string-translation' );
240
241 $strings = $this->get_strings();
242
243 foreach ($this->get_languages_list() as $language) {
244 if(empty($_POST['translation'][$language->name])) // in case the language filter is active (thanks to John P. Bloch)
245 continue;
246
247 $mo = $this->mo_import($language);
248
249 foreach ($_POST['translation'][$language->name] as $key=>$translation) {
250 $mo->add_entry($mo->make_entry($strings[$key]['string'], stripslashes($translation)));
251 }
252 $mo->add_entry($mo->make_entry('', '')); // empty string translation, just in case
253
254 // clean database
255 if (!empty($_POST['clean'])) {
256 $new_mo = new MO();
257 foreach ($strings as $string)
258 $new_mo->add_entry($mo->make_entry($string['string'], $mo->translate($string['string'])));
259 }
260 $this->mo_export(isset($new_mo) ? $new_mo : $mo, $language);
261 }
262
263 // to refresh the page (possible thanks to the $_GET['noheader']=true)
264 wp_redirect('admin.php?page=mlang&tab=strings'.(isset($_GET['s']) ? '&s='.$_GET['s'] : ''). (isset($_GET['paged']) ? '&paged='.$_GET['paged'] : ''));
265 exit;
266 break;
267
268 case 'options':
269 check_admin_referer( 'options-lang', '_wpnonce_options-lang' );
270
271 foreach(array('default_lang', 'force_lang', 'rewrite') as $key)
272 if (isset($_POST[$key]))
273 $options[$key] = $_POST[$key];
274
275 foreach (array('browser', 'hide_default', 'redirect_lang', 'media_support') as $key)
276 $options[$key] = isset($_POST[$key]) ? 1 : 0;
277
278 foreach (array('sync', 'post_types', 'taxonomies') as $key)
279 $options[$key] = empty($_POST[$key]) ? array() : array_keys($_POST[$key], 1);
280
281 update_option('polylang', $options);
282
283 // refresh rewrite rules in case rewrite, hide_default, post types or taxonomies options have been modified
284 // it seems useless to refresh permastruct here
285 flush_rewrite_rules();
286
287 // fills existing posts & terms with default language
288 if (isset($_POST['fill_languages']) && $nolang = $this->get_objects_with_no_lang()) {
289 global $wpdb;
290 $lang = $this->get_language($options['default_lang']);
291
292 $values = array();
293 foreach ($nolang['posts'] as $post_id)
294 $values[] = $wpdb->prepare("(%d, %d)", $post_id, $lang->term_taxonomy_id);
295
296 if ($values) {
297 $wpdb->query("INSERT INTO $wpdb->term_relationships (object_id, term_taxonomy_id) VALUES " . implode(',', $values));
298 wp_update_term_count($lang->term_taxonomy_id, 'language'); // updating term count is mandatory (thanks to AndyDeGroo)
299 }
300
301 $values = array();
302 foreach ($nolang['terms'] as $term_id)
303 $values[] = $wpdb->prepare("(%d, %s, %d)", $term_id, '_language', $lang->term_id);
304
305 if ($values)
306 $wpdb->query("INSERT INTO $wpdb->termmeta (term_id, meta_key, meta_value) VALUES " . implode(',', $values));
307
308 }
309 wp_redirect('admin.php?page=mlang&tab=settings');
310 exit;
311 break;
312
313 default:
314 break;
315 }
316
317 // prepare the list of tabs
318 $tabs = array('lang' => __('Languages','polylang'));
319
320 // only if at least one language has been created
321 if ($listlanguages = $this->get_languages_list()) {
322 if (current_theme_supports('menus'))
323 $tabs['menus'] = __('Menus','polylang'); // don't display the menu tab if the active theme does not support nav menus
324
325 $tabs['strings'] = __('Strings translation','polylang');
326 $tabs['settings'] = __('Settings', 'polylang');
327 }
328
329 $active_tab = !empty($_GET['tab']) ? $_GET['tab'] : 'lang';
330
331 switch($active_tab) {
332 case 'lang':
333 // prepare the list table of languages
334 $data = array();
335 foreach ($listlanguages as $lang)
336 $data[] = array_merge( (array) $lang, array('flag' => $this->get_flag($lang)) ) ;
337
338 $list_table = new Polylang_Languages_Table();
339 $list_table->prepare_items($data);
340
341 if (!$action)
342 $rtl = 0;
343
344 // error messages for data validation
345 // FIXME no validation for WordPress locale
346 //$errors[1] = __('Enter a valid WorPress locale', 'polylang');
347 $errors[2] = __('The language code must be 2 characters long', 'polylang');
348 $errors[3] = __('The language code must be unique', 'polylang');
349 $errors[4] = __('The language must have a name', 'polylang');
350 $errors[5] = __('The language was created, but the WordPress language file was not downloaded. Please install it manually.', 'polylang');
351 break;
352
353 case 'menus':
354 // default values
355 foreach (array_keys($locations) as $key)
356 if (isset($menu_lang[$key]))
357 $menu_lang[$key] = wp_parse_args($menu_lang[$key], $this->get_switcher_options('menu', 'default'));
358 break;
359
360 case 'strings':
361 // get the strings to translate
362 $data = $this->get_strings();
363 if (!empty($_REQUEST['s'])) {
364 foreach ($data as $key=>$row) {
365 if (stripos($row['name'], $_REQUEST['s']) === false && stripos($row['string'], $_REQUEST['s']) === false)
366 unset ($data[$key]);
367 }
368 }
369
370 // load translations
371 foreach ($listlanguages as $language) {
372 // filters by language if requested
373 if (($lg = get_user_meta(get_current_user_id(), 'pll_filter_content', true)) && $language->slug != $lg)
374 continue;
375
376 $mo = $this->mo_import($language);
377 foreach ($data as $key=>$row) {
378 $data[$key]['translations'][$language->name] = $mo->translate($row['string']);
379 $data[$key]['row'] = $key; // store the row number for convenience
380 }
381 }
382
383 $string_table = new Polylang_String_Table();
384 $string_table->prepare_items($data);
385 break;
386
387 case 'settings':
388 $post_types = array_unique(apply_filters('pll_get_post_types', get_post_types(array('_builtin' => false)), true));
389 $taxonomies = array_unique(apply_filters('pll_get_taxonomies', array_diff(get_taxonomies(array('_builtin' => false)), array('language')), true));
390 break;
391
392 default:
393 break;
394 }
395 // displays the page
396 include(PLL_INC.'/languages-form.php');
397 }
398
399 // validates data entered when creating or updating a language
400 function validate_lang($lang = null) {
401 // validate locale
402 // FIXME no validation for WordPress local as it breaks de_DE_Sie
403 /*
404 $loc = $_POST['description'];
405 if ( !preg_match('#^[a-z]{2,3}$#', $loc) && !preg_match('#^[a-z]{2,3}_[A-Z]{2,3}$#', $loc) )
406 $error = 1;
407 */
408
409 // validate slug length
410 if (!preg_match('#^[a-z]{2,3}$#', $_POST['slug']))
411 $error = 2;
412
413 // validate slug is unique
414 if ($this->get_language($_POST['slug']) && ( $lang === null || (isset($lang) && $lang->slug != $_POST['slug'])))
415 $error = 3;
416
417 // validate name
418 if ($_POST['name'] == '')
419 $error = 4;
420
421 return isset($error) ? $error : 0;
422 }
423
424 // returns unstranslated posts and terms ids
425 function get_objects_with_no_lang() {
426 $posts = get_posts(array(
427 'numberposts' => -1,
428 'post_type' => $this->post_types,
429 'post_status' => 'any',
430 'fields' => 'ids',
431 'tax_query' => array(array(
432 'taxonomy' => 'language',
433 'terms' => get_terms('language', array('fields'=>'ids')),
434 'operator' => 'NOT IN'
435 ))
436 ));
437
438 global $wpdb;
439 $terms = get_terms($this->taxonomies, array('get'=>'all', 'fields'=>'ids'));
440 $tr_terms = $wpdb->get_col("SELECT term_id FROM $wpdb->termmeta WHERE meta_key = '_language'");
441 $terms = array_unique(array_diff($terms, $tr_terms)); // array_unique to avoid duplicates if a term is in more than one taxonomy
442
443 return apply_filters('pll_get_objects_with_no_lang', empty($posts) && empty($terms) ? false : array('posts' => $posts, 'terms' => $terms));
444 }
445
446 function &get_strings() {
447 global $wp_registered_widgets;
448 $languages = get_option('polylang_widgets');
449
450 // WP strings
451 $this->register_string(__('Site Title'), get_option('blogname'));
452 $this->register_string(__('Tagline'), get_option('blogdescription'));
453 $this->register_string(__('Date Format'), get_option('date_format'));
454 $this->register_string(__('Time Format'), get_option('time_format'));
455
456 // widgets titles
457 $sidebars = wp_get_sidebars_widgets();
458 foreach ($sidebars as $sidebar => $widgets) {
459 if ($sidebar == 'wp_inactive_widgets' || !isset($widgets))
460 continue;
461
462 foreach ($widgets as $widget) {
463 // nothing can be done if the widget is created using pre WP2.8 API :(
464 // there is no object, so we can't access it to get the widget options
465 if (!isset($wp_registered_widgets[$widget]['callback'][0]) || !is_object($wp_registered_widgets[$widget]['callback'][0]) || !method_exists($wp_registered_widgets[$widget]['callback'][0], 'get_settings'))
466 continue;
467
468 $widget_settings = $wp_registered_widgets[$widget]['callback'][0]->get_settings();
469 $number = $wp_registered_widgets[$widget]['params'][0]['number'];
470 // don't enable widget title translation if the widget is visible in only one language or if there is no title
471 if (empty($languages[$widget]) && isset($widget_settings[$number]['title']) && $title = $widget_settings[$number]['title'])
472 $this->register_string(__('Widget title', 'polylang'), $title);
473 }
474 }
475 return $this->strings;
476 }
477
478 } // class Polylang_Admin
479