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

459 lines 16.9 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 $action = isset($_REQUEST['pll_action']) ? $_REQUEST['pll_action'] : '';
54
55 switch ($action) {
56 case 'add':
57 check_admin_referer( 'add-lang', '_wpnonce_add-lang' );
58 $error = $this->validate_lang();
59
60 if ($error == 0) {
61 $r = wp_insert_term($_POST['name'], 'language', array('slug' => $_POST['slug'], 'description' => $_POST['description']));
62 wp_update_term($r['term_id'], 'language', array('term_group' => $_POST['term_group'])); // can't set the term group directly in wp_insert_term
63 update_metadata('term', $r['term_id'], '_rtl', $_POST['rtl']);
64
65 if (!isset($this->options['default_lang'])) { // if this is the first language created, set it as default language
66 $this->options['default_lang'] = $_POST['slug'];
67 update_option('polylang', $this->options);
68 }
69
70 flush_rewrite_rules(); // refresh rewrite rules
71
72 if (!$this->download_mo($_POST['description']))
73 $error = 5;
74 }
75
76 wp_redirect('admin.php?page=mlang'. ($error ? '&error='.$error : '') ); // to refresh the page (possible thanks to the $_GET['noheader']=true)
77 exit;
78 break;
79
80 case 'delete':
81 check_admin_referer('delete-lang');
82
83 if (!empty($_GET['lang'])) {
84 $lang_id = (int) $_GET['lang'];
85 $lang = $this->get_language($lang_id);
86 $lang_slug = $lang->slug;
87
88 // update the language slug in posts meta
89 $posts = get_posts(array(
90 'numberposts' => -1,
91 'nopaging' => true,
92 'fields' => 'ids',
93 'meta_key' => '_translations',
94 'post_type' => 'any',
95 'post_status' => 'any'
96 ));
97 $this->delete_translations('post', $posts, $lang_slug);
98
99 // update the language slug in categories & post tags meta
100 $terms = get_terms($this->taxonomies, array('get'=>'all', 'fields'=>'ids'));
101 $this->delete_translations('term', $terms, $lang_slug);
102
103 // FIXME should find something more efficient (with a sql query ?)
104 foreach ($terms as $id) {
105 if (($lg = $this->get_term_language($id)) && $lg->term_id == $lang_id)
106 $this->delete_term_language($id); // delete language of this term
107 }
108
109 // delete language option in widgets
110 if (!empty($this->options['widgets'])) {
111 foreach ($this->options['widgets'] as $key=>$slug) {
112 if ($slug == $lang_slug)
113 unset($this->options['widgets'][$key]);
114 }
115 }
116
117 // delete users options
118 foreach (get_users(array('fields' => 'ID')) as $user_id) {
119 delete_user_meta($user_id, 'user_lang', $lang->description);
120 delete_user_meta($user_id, 'pll_filter_content', $lang_slug);
121 delete_user_meta($user_id, 'description_'.$lang_slug);
122 }
123
124 // delete the string translations
125 delete_option('polylang_mo'.$lang_id);
126
127 // delete the language itself
128 delete_metadata('term', $lang_id, '_rtl');
129 wp_delete_term($lang_id, 'language');
130
131 // oops ! we deleted the default language...
132 if ($this->options['default_lang'] == $lang_slug) {
133 if ($listlanguages = $this->get_languages_list())
134 $this->options['default_lang'] = $listlanguages[0]->slug; // arbitrary choice...
135 else
136 unset($this->options['default_lang']);
137 }
138
139 update_option('polylang', $this->options);
140 flush_rewrite_rules(); // refresh rewrite rules
141 }
142 wp_redirect('admin.php?page=mlang'); // to refresh the page (possible thanks to the $_GET['noheader']=true)
143 exit;
144 break;
145
146 case 'edit':
147 if (!empty($_GET['lang'])) {
148 $edit_lang = $this->get_language((int) $_GET['lang']);
149 $rtl = get_metadata('term', $edit_lang->term_id, '_rtl', true);
150 }
151 break;
152
153 case 'update':
154 check_admin_referer( 'add-lang', '_wpnonce_add-lang' );
155 $lang_id = (int) $_POST['lang_id'];
156 $lang = $this->get_language($lang_id);
157 $error = $this->validate_lang($lang);
158
159 if ($error == 0) {
160 // Update links to this language in posts and terms in case the slug has been modified
161 $old_slug = $lang->slug;
162
163 if ($old_slug != $_POST['slug']) {
164 // update the language slug in posts meta
165 $posts = get_posts(array(
166 'numberposts' => -1,
167 'nopaging' => true,
168 'fields' => 'ids',
169 'meta_key' => '_translations',
170 'post_type' => 'any',
171 'post_status' => 'any'
172 ));
173 $this->update_translations('post', $posts, $old_slug);
174
175 // update the language slug in categories & post tags meta
176 $terms = get_terms($this->taxonomies, array('get'=>'all', 'fields'=>'ids'));
177 $this->update_translations('term', $terms, $old_slug);
178
179 // update language option in widgets
180 foreach ($this->options['widgets'] as $key=>$lang) {
181 if ($lang == $old_slug)
182 $this->options['widgets'][$key] = $_POST['slug'];
183 }
184
185 // update the default language option if necessary
186 if ($this->options['default_lang'] == $old_slug)
187 $this->options['default_lang'] = $_POST['slug'];
188 }
189
190 update_option('polylang', $this->options);
191
192 // and finally update the language itself
193 $args = array(
194 'name' => $_POST['name'],
195 'slug' => $_POST['slug'],
196 'description' => $_POST['description'],
197 'term_group' => $_POST['term_group']
198 );
199 wp_update_term($lang_id, 'language', $args);
200 update_metadata('term', $lang_id, '_rtl', $_POST['rtl']);
201
202 flush_rewrite_rules(); // refresh rewrite rules
203 }
204
205 wp_redirect('admin.php?page=mlang'. ($error ? '&error='.$error : '') ); // to refresh the page (possible thanks to the $_GET['noheader']=true)
206 exit;
207 break;
208
209 case 'string-translation':
210 if (!empty($_REQUEST['submit'])) {
211 check_admin_referer( 'string-translation', '_wpnonce_string-translation' );
212 $strings = $this->get_strings();
213
214 foreach ($this->get_languages_list() as $language) {
215 if(empty($_POST['translation'][$language->name])) // in case the language filter is active (thanks to John P. Bloch)
216 continue;
217
218 $mo = $this->mo_import($language);
219
220 foreach ($_POST['translation'][$language->name] as $key=>$translation) {
221 $mo->add_entry($mo->make_entry($strings[$key]['string'], stripslashes($translation)));
222 }
223 $mo->add_entry($mo->make_entry('', '')); // empty string translation, just in case
224
225 // clean database
226 if (!empty($_POST['clean'])) {
227 $new_mo = new MO();
228 foreach ($strings as $string)
229 $new_mo->add_entry($mo->make_entry($string['string'], $mo->translate($string['string'])));
230 }
231 $this->mo_export(isset($new_mo) ? $new_mo : $mo, $language);
232 }
233 }
234
235 if (WP_List_Table::current_action() == 'delete' && !empty($_REQUEST['strings']) && function_exists('icl_unregister_string')) {
236 check_admin_referer( 'string-translation', '_wpnonce_string-translation' );
237 $strings = $this->get_strings();
238
239 foreach ($_REQUEST['strings'] as $key)
240 icl_unregister_string($strings[$key]['context'], $strings[$key]['name']);
241 }
242
243 // to refresh the page (possible thanks to the $_GET['noheader']=true)
244 $url = 'admin.php?page=mlang&tab=strings';
245 foreach(array('s', 'paged', 'group') as $qv)
246 $url = empty($_REQUEST[$qv]) ? $url : $url . '&' . $qv . '=' . $_REQUEST[$qv];
247 wp_redirect($url);
248 exit;
249 break;
250
251 case 'options':
252 check_admin_referer( 'options-lang', '_wpnonce_options-lang' );
253
254 foreach(array('default_lang', 'force_lang', 'rewrite') as $key)
255 if (isset($_POST[$key]))
256 $this->options[$key] = $_POST[$key];
257
258 foreach (array('browser', 'hide_default', 'redirect_lang', 'media_support') as $key)
259 $this->options[$key] = isset($_POST[$key]) ? 1 : 0;
260
261 foreach (array('sync', 'post_types', 'taxonomies') as $key)
262 $this->options[$key] = empty($_POST[$key]) ? array() : array_keys($_POST[$key], 1);
263
264 update_option('polylang', $this->options);
265
266 // refresh rewrite rules in case rewrite, hide_default, post types or taxonomies options have been modified
267 // it seems useless to refresh permastruct here
268 flush_rewrite_rules();
269
270 // fills existing posts & terms with default language
271 if (isset($_POST['fill_languages']) && $nolang = $this->get_objects_with_no_lang()) {
272 global $wpdb;
273 $lang = $this->get_language($this->options['default_lang']);
274
275 $values = array();
276 foreach ($nolang['posts'] as $post_id)
277 $values[] = $wpdb->prepare("(%d, %d)", $post_id, $lang->term_taxonomy_id);
278
279 if ($values) {
280 $wpdb->query("INSERT INTO $wpdb->term_relationships (object_id, term_taxonomy_id) VALUES " . implode(',', $values));
281 wp_update_term_count($lang->term_taxonomy_id, 'language'); // updating term count is mandatory (thanks to AndyDeGroo)
282 }
283
284 $values = array();
285 foreach ($nolang['terms'] as $term_id)
286 $values[] = $wpdb->prepare("(%d, %s, %d)", $term_id, '_language', $lang->term_id);
287
288 if ($values)
289 $wpdb->query("INSERT INTO $wpdb->termmeta (term_id, meta_key, meta_value) VALUES " . implode(',', $values));
290
291 }
292 wp_redirect('admin.php?page=mlang&tab=settings&updated=true'); // updated=true interpreted by WP
293 exit;
294 break;
295
296 default:
297 break;
298 }
299
300 // prepare the list of tabs
301 $tabs = array('lang' => __('Languages','polylang'));
302
303 // only if at least one language has been created
304 if ($listlanguages = $this->get_languages_list()) {
305 $tabs['strings'] = __('Strings translation','polylang');
306 $tabs['settings'] = __('Settings', 'polylang');
307 }
308
309 $active_tab = !empty($_GET['tab']) ? $_GET['tab'] : 'lang';
310
311 switch($active_tab) {
312 case 'lang':
313 // prepare the list table of languages
314 $data = array();
315 foreach ($listlanguages as $lang)
316 $data[] = array_merge( (array) $lang, array('flag' => $this->get_flag($lang)) ) ;
317
318 $list_table = new Polylang_Languages_Table();
319 $list_table->prepare_items($data);
320
321 if (!$action)
322 $rtl = 0;
323
324 // error messages for data validation
325 // FIXME no validation for WordPress locale
326 //$errors[1] = __('Enter a valid WorPress locale', 'polylang');
327 $errors[2] = __('The language code must be 2 characters long', 'polylang');
328 $errors[3] = __('The language code must be unique', 'polylang');
329 $errors[4] = __('The language must have a name', 'polylang');
330 $errors[5] = __('The language was created, but the WordPress language file was not downloaded. Please install it manually.', 'polylang');
331 break;
332
333 case 'strings':
334 // get the strings to translate
335 $data = $this->get_strings();
336
337 $selected = empty($_REQUEST['group']) ? -1 : $_REQUEST['group'];
338 foreach ($data as $key=>$row) {
339 $groups[] = $row['context']; // get the groups
340
341 // filter for search string
342 if (($selected !=-1 && $row['context'] != $selected) || (!empty($_REQUEST['s']) && stripos($row['name'], $_REQUEST['s']) === false && stripos($row['string'], $_REQUEST['s']) === false))
343 unset ($data[$key]);
344 }
345
346 $groups = array_unique($groups);
347
348 // load translations
349 foreach ($listlanguages as $language) {
350 // filters by language if requested
351 if (($lg = get_user_meta(get_current_user_id(), 'pll_filter_content', true)) && $language->slug != $lg)
352 continue;
353
354 $mo = $this->mo_import($language);
355 foreach ($data as $key=>$row) {
356 $data[$key]['translations'][$language->name] = $mo->translate($row['string']);
357 $data[$key]['row'] = $key; // store the row number for convenience
358 }
359 }
360
361 $string_table = new Polylang_String_Table($groups, $selected);
362 $string_table->prepare_items($data);
363 break;
364
365 case 'settings':
366 $post_types = array_unique(apply_filters('pll_get_post_types', get_post_types(array('_builtin' => false)), true));
367 $taxonomies = array_unique(apply_filters('pll_get_taxonomies', array_diff(get_taxonomies(array('_builtin' => false)), array('language')), true));
368 break;
369
370 default:
371 break;
372 }
373 // displays the page
374 include(PLL_INC.'/languages-form.php');
375 }
376
377 // validates data entered when creating or updating a language
378 function validate_lang($lang = null) {
379 // validate locale
380 // FIXME no validation for WordPress locale as it breaks de_DE_Sie
381 /*
382 $loc = $_POST['description'];
383 if ( !preg_match('#^[a-z]{2,3}$#', $loc) && !preg_match('#^[a-z]{2,3}_[A-Z]{2,3}$#', $loc) )
384 $error = 1;
385 */
386
387 // validate slug length
388 if (!preg_match('#^[a-z]{2,3}$#', $_POST['slug']))
389 $error = 2;
390
391 // validate slug is unique
392 if ($this->get_language($_POST['slug']) && ( $lang === null || (isset($lang) && $lang->slug != $_POST['slug'])))
393 $error = 3;
394
395 // validate name
396 if ($_POST['name'] == '')
397 $error = 4;
398
399 return isset($error) ? $error : 0;
400 }
401
402 // returns unstranslated posts and terms ids
403 function get_objects_with_no_lang() {
404 $posts = get_posts(array(
405 'numberposts' => -1,
406 'nopaging' => true,
407 'post_type' => $this->post_types,
408 'post_status' => 'any',
409 'fields' => 'ids',
410 'tax_query' => array(array(
411 'taxonomy' => 'language',
412 'terms' => get_terms('language', array('fields'=>'ids')),
413 'operator' => 'NOT IN'
414 ))
415 ));
416
417 global $wpdb;
418 $terms = get_terms($this->taxonomies, array('get'=>'all', 'fields'=>'ids'));
419 $tr_terms = $wpdb->get_col("SELECT term_id FROM $wpdb->termmeta WHERE meta_key = '_language'");
420 $terms = array_unique(array_diff($terms, $tr_terms)); // array_unique to avoid duplicates if a term is in more than one taxonomy
421
422 return apply_filters('pll_get_objects_with_no_lang', empty($posts) && empty($terms) ? false : array('posts' => $posts, 'terms' => $terms));
423 }
424
425 function &get_strings() {
426 // WP strings
427 $this->register_string(__('Site Title'), get_option('blogname'), 'WordPress');
428 $this->register_string(__('Tagline'), get_option('blogdescription'), 'WordPress');
429 $this->register_string(__('Date Format'), get_option('date_format'), 'WordPress');
430 $this->register_string(__('Time Format'), get_option('time_format'), 'WordPress');
431
432 // widgets titles
433 global $wp_registered_widgets;
434 $sidebars = wp_get_sidebars_widgets();
435 foreach ($sidebars as $sidebar => $widgets) {
436 if ($sidebar == 'wp_inactive_widgets' || !isset($widgets))
437 continue;
438
439 foreach ($widgets as $widget) {
440 // nothing can be done if the widget is created using pre WP2.8 API :(
441 // there is no object, so we can't access it to get the widget options
442 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'))
443 continue;
444
445 $widget_settings = $wp_registered_widgets[$widget]['callback'][0]->get_settings();
446 $number = $wp_registered_widgets[$widget]['params'][0]['number'];
447 // don't enable widget title translation if the widget is visible in only one language or if there is no title
448 if (empty($this->options['widgets'][$widget]) && isset($widget_settings[$number]['title']) && $title = $widget_settings[$number]['title'])
449 $this->register_string(__('Widget title', 'polylang'), $title, 'Widget');
450 }
451 }
452
453 // allow plugins to modify our list of strings, mainly for use by our Polylang_WPML_Compat class
454 $this->strings = apply_filters('pll_get_strings', $this->strings);
455 return $this->strings;
456 }
457
458 } // class Polylang_Admin
459