<?php
require_once(PLL_INC.'/list-table.php');
// setups the Polylang admin panel
class Polylang_Admin extends Polylang_Admin_Base {
function __construct() {
parent::__construct();
// adds screen options and the about box in the languages admin panel
add_action('load-settings_page_mlang', array(&$this, 'load_page'));
// saves per-page value in screen option
add_filter('set-screen-option', create_function('$s, $o, $v', 'return $v;'), 10, 3);
}
// adds screen options and the about box in the languages admin panel
function load_page() {
// test of $_GET['tab'] avoids displaying the automatically generated screen options on other tabs
if ((!defined('PLL_DISPLAY_ABOUT') || PLL_DISPLAY_ABOUT) && (!isset($_GET['tab']) || $_GET['tab'] == 'lang')) {
add_meta_box('pll_about_box', __('About Polylang', 'polylang'), create_function('', "include(PLL_INC.'/about.php');"), 'settings_page_mlang', 'normal');
add_screen_option('per_page', array('label' => __('Languages', 'polylang'), 'default' => 10, 'option' => 'pll_lang_per_page'));
}
if (isset($_GET['tab']) && $_GET['tab'] == 'strings')
add_screen_option('per_page', array('label' => __('Strings translations', 'polylang'), 'default' => 10, 'option' => 'pll_strings_per_page'));
}
// used to update the translation when a language slug has been modified
function update_translations($type, $ids, $old_slug) {
foreach ($ids as $id) {
if ($tr = $this->get_translations($type, $id)) {
$tr[$_POST['slug']] = $tr[$old_slug];
unset($tr[$old_slug]);
update_metadata($type, (int) $id, '_translations', $tr);
}
}
}
// used to delete the translation when a language is deleted
function delete_translations($type, $ids, $old_slug) {
foreach ($ids as $id) {
if ($tr = $this->get_translations($type, $id)) {
unset($tr[$old_slug]);
update_metadata($type, (int) $id, '_translations', $tr);
}
}
}
// the languages panel
function languages_page() {
$action = isset($_REQUEST['pll_action']) ? $_REQUEST['pll_action'] : '';
switch ($action) {
case 'add':
check_admin_referer( 'add-lang', '_wpnonce_add-lang' );
$error = $this->validate_lang();
if ($error == 0) {
$r = wp_insert_term($_POST['name'], 'language', array('slug' => $_POST['slug'], 'description' => $_POST['description']));
wp_update_term($r['term_id'], 'language', array('term_group' => $_POST['term_group'])); // can't set the term group directly in wp_insert_term
update_metadata('term', $r['term_id'], '_rtl', $_POST['rtl']);
if (!isset($this->options['default_lang'])) { // if this is the first language created, set it as default language
$this->options['default_lang'] = $_POST['slug'];
update_option('polylang', $this->options);
}
flush_rewrite_rules(); // refresh rewrite rules
if (!$this->download_mo($_POST['description']))
$error = 5;
}
wp_redirect('admin.php?page=mlang'. ($error ? '&error='.$error : '') ); // to refresh the page (possible thanks to the $_GET['noheader']=true)
exit;
break;
case 'delete':
check_admin_referer('delete-lang');
if (!empty($_GET['lang'])) {
$lang_id = (int) $_GET['lang'];
$lang = $this->get_language($lang_id);
$lang_slug = $lang->slug;
// update the language slug in posts meta
$posts = get_posts(array(
'numberposts' => -1,
'nopaging' => true,
'fields' => 'ids',
'meta_key' => '_translations',
'post_type' => 'any',
'post_status' => 'any'
));
$this->delete_translations('post', $posts, $lang_slug);
// update the language slug in categories & post tags meta
$terms = get_terms($this->taxonomies, array('get'=>'all', 'fields'=>'ids'));
$this->delete_translations('term', $terms, $lang_slug);
// FIXME should find something more efficient (with a sql query ?)
foreach ($terms as $id) {
if (($lg = $this->get_term_language($id)) && $lg->term_id == $lang_id)
$this->delete_term_language($id); // delete language of this term
}
// delete language option in widgets
if (!empty($this->options['widgets'])) {
foreach ($this->options['widgets'] as $key=>$slug) {
if ($slug == $lang_slug)
unset($this->options['widgets'][$key]);
}
}
// delete users options
foreach (get_users(array('fields' => 'ID')) as $user_id) {
delete_user_meta($user_id, 'user_lang', $lang->description);
delete_user_meta($user_id, 'pll_filter_content', $lang_slug);
delete_user_meta($user_id, 'description_'.$lang_slug);
}
// delete the string translations
delete_option('polylang_mo'.$lang_id);
// delete the language itself
delete_metadata('term', $lang_id, '_rtl');
wp_delete_term($lang_id, 'language');
// oops ! we deleted the default language...
if ($this->options['default_lang'] == $lang_slug) {
if ($listlanguages = $this->get_languages_list())
$this->options['default_lang'] = $listlanguages[0]->slug; // arbitrary choice...
else
unset($this->options['default_lang']);
}
update_option('polylang', $this->options);
flush_rewrite_rules(); // refresh rewrite rules
}
wp_redirect('admin.php?page=mlang'); // to refresh the page (possible thanks to the $_GET['noheader']=true)
exit;
break;
case 'edit':
if (!empty($_GET['lang'])) {
$edit_lang = $this->get_language((int) $_GET['lang']);
$rtl = get_metadata('term', $edit_lang->term_id, '_rtl', true);
}
break;
case 'update':
check_admin_referer( 'add-lang', '_wpnonce_add-lang' );
$lang_id = (int) $_POST['lang_id'];
$lang = $this->get_language($lang_id);
$error = $this->validate_lang($lang);
if ($error == 0) {
// Update links to this language in posts and terms in case the slug has been modified
$old_slug = $lang->slug;
if ($old_slug != $_POST['slug']) {
// update the language slug in posts meta
$posts = get_posts(array(
'numberposts' => -1,
'nopaging' => true,
'fields' => 'ids',
'meta_key' => '_translations',
'post_type' => 'any',
'post_status' => 'any'
));
$this->update_translations('post', $posts, $old_slug);
// update the language slug in categories & post tags meta
$terms = get_terms($this->taxonomies, array('get'=>'all', 'fields'=>'ids'));
$this->update_translations('term', $terms, $old_slug);
// update language option in widgets
foreach ($this->options['widgets'] as $key=>$lang) {
if ($lang == $old_slug)
$this->options['widgets'][$key] = $_POST['slug'];
}
// update the default language option if necessary
if ($this->options['default_lang'] == $old_slug)
$this->options['default_lang'] = $_POST['slug'];
}
update_option('polylang', $this->options);
// and finally update the language itself
$args = array(
'name' => $_POST['name'],
'slug' => $_POST['slug'],
'description' => $_POST['description'],
'term_group' => $_POST['term_group']
);
wp_update_term($lang_id, 'language', $args);
update_metadata('term', $lang_id, '_rtl', $_POST['rtl']);
flush_rewrite_rules(); // refresh rewrite rules
}
wp_redirect('admin.php?page=mlang'. ($error ? '&error='.$error : '') ); // to refresh the page (possible thanks to the $_GET['noheader']=true)
exit;
break;
case 'string-translation':
if (!empty($_REQUEST['submit'])) {
check_admin_referer( 'string-translation', '_wpnonce_string-translation' );
$strings = $this->get_strings();
foreach ($this->get_languages_list() as $language) {
if(empty($_POST['translation'][$language->name])) // in case the language filter is active (thanks to John P. Bloch)
continue;
$mo = $this->mo_import($language);
foreach ($_POST['translation'][$language->name] as $key=>$translation) {
$mo->add_entry($mo->make_entry($strings[$key]['string'], stripslashes($translation)));
}
$mo->add_entry($mo->make_entry('', '')); // empty string translation, just in case
// clean database
if (!empty($_POST['clean'])) {
$new_mo = new MO();
foreach ($strings as $string)
$new_mo->add_entry($mo->make_entry($string['string'], $mo->translate($string['string'])));
}
$this->mo_export(isset($new_mo) ? $new_mo : $mo, $language);
}
}
if (WP_List_Table::current_action() == 'delete' && !empty($_REQUEST['strings']) && function_exists('icl_unregister_string')) {
check_admin_referer( 'string-translation', '_wpnonce_string-translation' );
$strings = $this->get_strings();
foreach ($_REQUEST['strings'] as $key)
icl_unregister_string($strings[$key]['context'], $strings[$key]['name']);
}
// to refresh the page (possible thanks to the $_GET['noheader']=true)
$url = 'admin.php?page=mlang&tab=strings';
foreach(array('s', 'paged', 'group') as $qv)
$url = empty($_REQUEST[$qv]) ? $url : $url . '&' . $qv . '=' . $_REQUEST[$qv];
wp_redirect($url);
exit;
break;
case 'options':
check_admin_referer( 'options-lang', '_wpnonce_options-lang' );
foreach(array('default_lang', 'force_lang', 'rewrite') as $key)
if (isset($_POST[$key]))
$this->options[$key] = $_POST[$key];
foreach (array('browser', 'hide_default', 'redirect_lang', 'media_support') as $key)
$this->options[$key] = isset($_POST[$key]) ? 1 : 0;
foreach (array('sync', 'post_types', 'taxonomies') as $key)
$this->options[$key] = empty($_POST[$key]) ? array() : array_keys($_POST[$key], 1);
update_option('polylang', $this->options);
// refresh rewrite rules in case rewrite, hide_default, post types or taxonomies options have been modified
// it seems useless to refresh permastruct here
flush_rewrite_rules();
// fills existing posts & terms with default language
if (isset($_POST['fill_languages']) && $nolang = $this->get_objects_with_no_lang()) {
global $wpdb;
$lang = $this->get_language($this->options['default_lang']);
$values = array();
foreach ($nolang['posts'] as $post_id)
$values[] = $wpdb->prepare("(%d, %d)", $post_id, $lang->term_taxonomy_id);
if ($values) {
$wpdb->query("INSERT INTO $wpdb->term_relationships (object_id, term_taxonomy_id) VALUES " . implode(',', $values));
wp_update_term_count($lang->term_taxonomy_id, 'language'); // updating term count is mandatory (thanks to AndyDeGroo)
}
$values = array();
foreach ($nolang['terms'] as $term_id)
$values[] = $wpdb->prepare("(%d, %s, %d)", $term_id, '_language', $lang->term_id);
if ($values)
$wpdb->query("INSERT INTO $wpdb->termmeta (term_id, meta_key, meta_value) VALUES " . implode(',', $values));
}
wp_redirect('admin.php?page=mlang&tab=settings&updated=true'); // updated=true interpreted by WP
exit;
break;
default:
break;
}
// prepare the list of tabs
$tabs = array('lang' => __('Languages','polylang'));
// only if at least one language has been created
if ($listlanguages = $this->get_languages_list()) {
$tabs['strings'] = __('Strings translation','polylang');
$tabs['settings'] = __('Settings', 'polylang');
}
$active_tab = !empty($_GET['tab']) ? $_GET['tab'] : 'lang';
switch($active_tab) {
case 'lang':
// prepare the list table of languages
$data = array();
foreach ($listlanguages as $lang)
$data[] = array_merge( (array) $lang, array('flag' => $this->get_flag($lang)) ) ;
$list_table = new Polylang_Languages_Table();
$list_table->prepare_items($data);
if (!$action)
$rtl = 0;
// error messages for data validation
// FIXME no validation for WordPress locale
//$errors[1] = __('Enter a valid WorPress locale', 'polylang');
$errors[2] = __('The language code must be 2 characters long', 'polylang');
$errors[3] = __('The language code must be unique', 'polylang');
$errors[4] = __('The language must have a name', 'polylang');
$errors[5] = __('The language was created, but the WordPress language file was not downloaded. Please install it manually.', 'polylang');
break;
case 'strings':
// get the strings to translate
$data = $this->get_strings();
$selected = empty($_REQUEST['group']) ? -1 : $_REQUEST['group'];
foreach ($data as $key=>$row) {
$groups[] = $row['context']; // get the groups
// filter for search string
if (($selected !=-1 && $row['context'] != $selected) || (!empty($_REQUEST['s']) && stripos($row['name'], $_REQUEST['s']) === false && stripos($row['string'], $_REQUEST['s']) === false))
unset ($data[$key]);
}
$groups = array_unique($groups);
// load translations
foreach ($listlanguages as $language) {
// filters by language if requested
if (($lg = get_user_meta(get_current_user_id(), 'pll_filter_content', true)) && $language->slug != $lg)
continue;
$mo = $this->mo_import($language);
foreach ($data as $key=>$row) {
$data[$key]['translations'][$language->name] = $mo->translate($row['string']);
$data[$key]['row'] = $key; // store the row number for convenience
}
}
$string_table = new Polylang_String_Table($groups, $selected);
$string_table->prepare_items($data);
break;
case 'settings':
$post_types = array_unique(apply_filters('pll_get_post_types', get_post_types(array('_builtin' => false)), true));
$taxonomies = array_unique(apply_filters('pll_get_taxonomies', array_diff(get_taxonomies(array('_builtin' => false)), array('language')), true));
break;
default:
break;
}
// displays the page
include(PLL_INC.'/languages-form.php');
}
// validates data entered when creating or updating a language
function validate_lang($lang = null) {
// validate locale
// FIXME no validation for WordPress locale as it breaks de_DE_Sie
/*
$loc = $_POST['description'];
if ( !preg_match('#^[a-z]{2,3}$#', $loc) && !preg_match('#^[a-z]{2,3}_[A-Z]{2,3}$#', $loc) )
$error = 1;
*/
// validate slug length
if (!preg_match('#^[a-z]{2,3}$#', $_POST['slug']))
$error = 2;
// validate slug is unique
if ($this->get_language($_POST['slug']) && ( $lang === null || (isset($lang) && $lang->slug != $_POST['slug'])))
$error = 3;
// validate name
if ($_POST['name'] == '')
$error = 4;
return isset($error) ? $error : 0;
}
// returns unstranslated posts and terms ids
function get_objects_with_no_lang() {
$posts = get_posts(array(
'numberposts' => -1,
'nopaging' => true,
'post_type' => $this->post_types,
'post_status' => 'any',
'fields' => 'ids',
'tax_query' => array(array(
'taxonomy' => 'language',
'terms' => get_terms('language', array('fields'=>'ids')),
'operator' => 'NOT IN'
))
));
global $wpdb;
$terms = get_terms($this->taxonomies, array('get'=>'all', 'fields'=>'ids'));
$tr_terms = $wpdb->get_col("SELECT term_id FROM $wpdb->termmeta WHERE meta_key = '_language'");
$terms = array_unique(array_diff($terms, $tr_terms)); // array_unique to avoid duplicates if a term is in more than one taxonomy
return apply_filters('pll_get_objects_with_no_lang', empty($posts) && empty($terms) ? false : array('posts' => $posts, 'terms' => $terms));
}
function &get_strings() {
// WP strings
$this->register_string(__('Site Title'), get_option('blogname'), 'WordPress');
$this->register_string(__('Tagline'), get_option('blogdescription'), 'WordPress');
$this->register_string(__('Date Format'), get_option('date_format'), 'WordPress');
$this->register_string(__('Time Format'), get_option('time_format'), 'WordPress');
// widgets titles
global $wp_registered_widgets;
$sidebars = wp_get_sidebars_widgets();
foreach ($sidebars as $sidebar => $widgets) {
if ($sidebar == 'wp_inactive_widgets' || !isset($widgets))
continue;
foreach ($widgets as $widget) {
// nothing can be done if the widget is created using pre WP2.8 API :(
// there is no object, so we can't access it to get the widget options
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'))
continue;
$widget_settings = $wp_registered_widgets[$widget]['callback'][0]->get_settings();
$number = $wp_registered_widgets[$widget]['params'][0]['number'];
// don't enable widget title translation if the widget is visible in only one language or if there is no title
if (empty($this->options['widgets'][$widget]) && isset($widget_settings[$number]['title']) && $title = $widget_settings[$number]['title'])
$this->register_string(__('Widget title', 'polylang'), $title, 'Widget');
}
}
// allow plugins to modify our list of strings, mainly for use by our Polylang_WPML_Compat class
$this->strings = apply_filters('pll_get_strings', $this->strings);
return $this->strings;
}
} // class Polylang_Admin