| @@ -1,170 +1,191 @@ | ||
| 1 | 1 | <?php |
| 2 | -/** | |
| 3 | - * @package Polylang | |
| 4 | - */ | |
| 5 | 2 | |
| 6 | -/** | |
| 7 | - * Base class for both admin and frontend | |
| 8 | - * | |
| 9 | - * @since 1.2 | |
| 10 | - */ | |
| 11 | -abstract class PLL_Base { | |
| 12 | - /** | |
| 13 | - * Stores the plugin options. | |
| 14 | - * | |
| 15 | - * @var array | |
| 16 | - */ | |
| 17 | - public $options; | |
| 3 | +// setups basic functions used for admin and frontend | |
| 4 | +abstract class Polylang_Base { | |
| 5 | + var $strings; // strings to translate | |
| 6 | + | |
| 7 | + // returns the query variables of the referer | |
| 8 | + function get_referer_vars() { | |
| 9 | + $qvars = array(); | |
| 10 | + $referer = wp_get_referer(); | |
| 11 | + if ($referer) { | |
| 12 | + $urlparts = parse_url($referer); | |
| 13 | + if (isset($urlparts['query'])) { | |
| 14 | + parse_str($urlparts['query'], $qvars); | |
| 15 | + } | |
| 16 | + } | |
| 17 | + return $qvars; | |
| 18 | + } | |
| 18 | 19 | |
| 19 | - /** | |
| 20 | - * @var PLL_Model | |
| 21 | - */ | |
| 22 | - public $model; | |
| 20 | + // returns the list of available languages | |
| 21 | + function get_languages_list($hide_empty = false) { | |
| 22 | + return get_terms('language', array('hide_empty'=>$hide_empty, 'orderby'=>'term_group' )); | |
| 23 | + } | |
| 23 | 24 | |
| 24 | - /** | |
| 25 | - * Instance of a child class of PLL_Links_Model. | |
| 26 | - * | |
| 27 | - * @var PLL_Links_Model | |
| 28 | - */ | |
| 29 | - public $links_model; | |
| 25 | + // retrieves the dropdown list of the languages | |
| 26 | + function dropdown_languages($args) { | |
| 27 | + $defaults = array('name' => 'lang_choice', 'class' => '', 'show_option_none' => false, 'show_option_all' => false, | |
| 28 | + 'hide_empty' => false, 'value' => 'slug', 'selected' => ''); | |
| 29 | + extract(wp_parse_args($args, $defaults)); | |
| 30 | 30 | |
| 31 | - /** | |
| 32 | - * Registers hooks on insert / update post related actions and filters. | |
| 33 | - * | |
| 34 | - * @var PLL_CRUD_Posts | |
| 35 | - */ | |
| 36 | - public $posts; | |
| 31 | + $out = sprintf('<select name="%1$s" id="%1$s"%2$s>'."\n", esc_attr($name), $class ? ' class="'.esc_attr($class).'"' : ''); | |
| 32 | + $out .= $show_option_none ? "<option value='0'></option>\n" : ''; | |
| 33 | + $out .= $show_option_all ? "<option value='0'>".__('All languages', 'polylang')."</option>\n" : ''; | |
| 34 | + foreach ($this->get_languages_list($hide_empty) as $language) { | |
| 35 | + $out .= sprintf("<option value='%s'%s>%s</option>\n", | |
| 36 | + esc_attr($language->$value), | |
| 37 | + $language->$value == $selected ? ' selected="selected"' : '', | |
| 38 | + esc_html($language->name) | |
| 39 | + ); | |
| 40 | + } | |
| 41 | + $out .= "</select>\n"; | |
| 42 | + return $out; | |
| 43 | + } | |
| 37 | 44 | |
| 38 | - /** | |
| 39 | - * Registers hooks on insert / update term related action and filters. | |
| 40 | - * | |
| 41 | - * @var PLL_CRUD_Terms | |
| 42 | - */ | |
| 43 | - public $terms; | |
| 45 | + // returns the language by its id or its slug | |
| 46 | + // Note: it seems that a numeric value is better for performance (3.2.1) | |
| 47 | + function get_language($value) { | |
| 48 | + if (is_object($value)) | |
| 49 | + return $value; | |
| 50 | + if (is_numeric($value) || (int) $value) | |
| 51 | + return get_term((int) $value, 'language'); | |
| 52 | + elseif (is_string($value)) | |
| 53 | + return get_term_by('slug', $value , 'language'); // seems it is not cached in 3.2.1 | |
| 54 | + return null; | |
| 55 | + } | |
| 44 | 56 | |
| 45 | - /** | |
| 46 | - * Constructor. | |
| 47 | - * | |
| 48 | - * @since 1.2 | |
| 49 | - * | |
| 50 | - * @param PLL_Links_Model $links_model Links Model. | |
| 51 | - */ | |
| 52 | - public function __construct( &$links_model ) { | |
| 53 | - $this->links_model = &$links_model; | |
| 54 | - $this->model = &$links_model->model; | |
| 55 | - $this->options = &$this->model->options; | |
| 57 | + // saves translations for posts or terms | |
| 58 | + // the underscore in '_lang' hides the post meta in the Custom Fields metabox in the Edit Post screen | |
| 59 | + // $type: either 'post' or 'term' | |
| 60 | + // $id: post id or term id | |
| 61 | + // $translations: an associative array of translations with language code as key and translation id as value | |
| 62 | + function save_translations($type, $id, $translations) { | |
| 63 | + $lang = call_user_func(array(&$this, 'get_'.$type.'_language'), $id); | |
| 64 | + if (!$lang) | |
| 65 | + return; | |
| 56 | 66 | |
| 57 | - $GLOBALS['l10n_unloaded']['pll_string'] = true; // Short-circuit _load_textdomain_just_in_time() for 'pll_string' domain in WP 4.6+ | |
| 67 | + if (isset($translations) && is_array($translations)) { | |
| 68 | + $tr = serialize(array_merge(array($lang->slug => $id), $translations)); | |
| 69 | + update_metadata($type, $id, '_translations', $tr); | |
| 58 | 70 | |
| 59 | - add_action( 'widgets_init', array( $this, 'widgets_init' ) ); | |
| 71 | + foreach($translations as $key=>$p) | |
| 72 | + update_metadata($type, (int) $p, '_translations', $tr); | |
| 73 | + } | |
| 74 | + } | |
| 60 | 75 | |
| 61 | - // User defined strings translations | |
| 62 | - add_action( 'pll_language_defined', array( $this, 'load_strings_translations' ), 5 ); | |
| 63 | - add_action( 'change_locale', array( $this, 'load_strings_translations' ) ); // Since WP 4.7 | |
| 64 | - add_action( 'personal_options_update', array( $this, 'load_strings_translations' ), 1, 0 ); // Before WP, for confirmation request when changing the user email. | |
| 76 | + // deletes a translation of a post or term | |
| 77 | + function delete_translation($type, $id) { | |
| 78 | + $translations = unserialize(get_metadata($type, $id, '_translations', true)); | |
| 79 | + if (is_array($translations)) { | |
| 80 | + $slug = array_search($id, $translations); | |
| 81 | + unset($translations[$slug]); | |
| 82 | + $tr = serialize($translations); | |
| 83 | + foreach($translations as $key=>$p) | |
| 84 | + update_metadata($type, (int) $p, '_translations', $tr); | |
| 85 | + delete_metadata($type, $id, '_translations'); | |
| 86 | + } | |
| 87 | + } | |
| 65 | 88 | |
| 66 | - // Switch_to_blog | |
| 67 | - add_action( 'switch_blog', array( $this, 'switch_blog' ), 10, 2 ); | |
| 89 | + // returns the id of the translation of a post or term | |
| 90 | + // $type: either 'post' or 'term' | |
| 91 | + // $id: post id or term id | |
| 92 | + // $lang: object or slug (in the order of preference latest to avoid) | |
| 93 | + function get_translation($type, $id, $lang) { | |
| 94 | + $translations = unserialize(get_metadata($type, $id, '_translations', true)); | |
| 95 | + $slug = $this->get_language($lang)->slug; | |
| 96 | + return isset($translations[$slug]) ? (int) $translations[$slug] : ''; | |
| 68 | 97 | } |
| 69 | 98 | |
| 70 | - /** | |
| 71 | - * Instantiates classes reacting to CRUD operations on posts and terms, | |
| 72 | - * only when at least one language is defined. | |
| 73 | - * | |
| 74 | - * @since 2.6 | |
| 75 | - * | |
| 76 | - * @return void | |
| 77 | - */ | |
| 78 | - public function init() { | |
| 79 | - if ( $this->model->get_languages_list() ) { | |
| 80 | - $this->posts = new PLL_CRUD_Posts( $this ); | |
| 81 | - $this->terms = new PLL_CRUD_Terms( $this ); | |
| 99 | + // store the post language in the database | |
| 100 | + function set_post_language($post_id, $lang) { | |
| 101 | + wp_set_post_terms($post_id, $this->get_language($lang)->slug, 'language' ); | |
| 102 | + } | |
| 82 | 103 | |
| 83 | - // WordPress options. | |
| 84 | - new PLL_Translate_Option( 'blogname', array(), array( 'context' => 'WordPress' ) ); | |
| 85 | - new PLL_Translate_Option( 'blogdescription', array(), array( 'context' => 'WordPress' ) ); | |
| 86 | - new PLL_Translate_Option( 'date_format', array(), array( 'context' => 'WordPress' ) ); | |
| 87 | - new PLL_Translate_Option( 'time_format', array(), array( 'context' => 'WordPress' ) ); | |
| 88 | - } | |
| 104 | + // returns the language of a post | |
| 105 | + function get_post_language($post_id) { | |
| 106 | + $lang = get_the_terms($post_id, 'language' ); | |
| 107 | + return ($lang) ? reset($lang) : null; // there's only one language per post : first element of the array returned | |
| 89 | 108 | } |
| 90 | 109 | |
| 91 | - /** | |
| 92 | - * Registers our widgets | |
| 93 | - * | |
| 94 | - * @since 0.1 | |
| 95 | - * | |
| 96 | - * @return void | |
| 97 | - */ | |
| 98 | - public function widgets_init() { | |
| 99 | - register_widget( 'PLL_Widget_Languages' ); | |
| 110 | + // among the post and its translations, returns the id of the post which is in $lang | |
| 111 | + function get_post($post_id, $lang) { | |
| 112 | + $lang = $this->get_language($lang); | |
| 113 | + return $this->get_post_language($post_id)->term_id == $lang->term_id ? $post_id : $this->get_translation('post', $post_id, $lang); | |
| 114 | + } | |
| 100 | 115 | |
| 101 | - // Overwrites the calendar widget to filter posts by language | |
| 102 | - if ( ! defined( 'PLL_WIDGET_CALENDAR' ) || PLL_WIDGET_CALENDAR ) { | |
| 103 | - unregister_widget( 'WP_Widget_Calendar' ); | |
| 104 | - register_widget( 'PLL_Widget_Calendar' ); | |
| 105 | - } | |
| 116 | + // store the term language in the database | |
| 117 | + function set_term_language($term_id, $lang) { | |
| 118 | + update_metadata('term', $term_id, '_language', $this->get_language($lang)->term_id); | |
| 106 | 119 | } |
| 107 | 120 | |
| 108 | - /** | |
| 109 | - * Loads user defined strings translations | |
| 110 | - * | |
| 111 | - * @since 1.2 | |
| 112 | - * @since 2.1.3 $locale parameter added. | |
| 113 | - * | |
| 114 | - * @param string $locale Locale. Defaults to current locale. | |
| 115 | - * @return void | |
| 116 | - */ | |
| 117 | - public function load_strings_translations( $locale = '' ) { | |
| 118 | - if ( empty( $locale ) ) { | |
| 119 | - $locale = ( is_admin() && ! Polylang::is_ajax_on_front() ) ? get_user_locale() : get_locale(); | |
| 120 | - } | |
| 121 | + // remove the term language in the database | |
| 122 | + function delete_term_language($term_id) { | |
| 123 | + delete_metadata('term', $term_id, '_language'); | |
| 124 | + } | |
| 121 | 125 | |
| 122 | - $language = $this->model->get_language( $locale ); | |
| 126 | + // returns the language of a term | |
| 127 | + function get_term_language($value, $taxonomy = '') { | |
| 128 | + if (is_numeric($value)) | |
| 129 | + $term_id = $value; | |
| 130 | + elseif (is_string($value) && $taxonomy) | |
| 131 | + $term_id = get_term_by('slug', $value , $taxonomy)->term_id; | |
| 132 | + return $term_id ? $this->get_language(get_metadata('term', $term_id, '_language', true)) : null; | |
| 133 | + } | |
| 123 | 134 | |
| 124 | - if ( ! empty( $language ) ) { | |
| 125 | - $mo = new PLL_MO(); | |
| 126 | - $mo->import_from_db( $language ); | |
| 127 | - $GLOBALS['l10n']['pll_string'] = &$mo; | |
| 128 | - } else { | |
| 129 | - unset( $GLOBALS['l10n']['pll_string'] ); | |
| 130 | - } | |
| 135 | + // among the term and its translations, returns the id of the term which is in $lang | |
| 136 | + function get_term($term_id, $lang) { | |
| 137 | + $lang = $this->get_language($lang); | |
| 138 | + return $this->get_term_language($term_id)->term_id == $lang->term_id ? $term_id : $this->get_translation('term', $term_id, $lang); | |
| 131 | 139 | } |
| 132 | 140 | |
| 133 | - /** | |
| 134 | - * Resets some variables when the blog is switched. | |
| 135 | - * Applied only if Polylang is active on the new blog. | |
| 136 | - * | |
| 137 | - * @since 1.5.1 | |
| 138 | - * | |
| 139 | - * @param int $new_blog_id New blog ID. | |
| 140 | - * @param int $prev_blog_id Previous blog ID. | |
| 141 | - * @return void | |
| 142 | - */ | |
| 143 | - public function switch_blog( $new_blog_id, $prev_blog_id ) { | |
| 144 | - if ( $this->is_active_on_new_blog( $new_blog_id, $prev_blog_id ) ) { | |
| 145 | - $this->options = get_option( 'polylang' ); // Needed for menus. | |
| 146 | - remove_action( 'pre_option_rewrite_rules', array( $this->links_model, 'prepare_rewrite_rules' ) ); | |
| 147 | - $this->links_model = $this->model->get_links_model(); | |
| 141 | + // returns the html link to the flag if exists | |
| 142 | + // $lang: object | |
| 143 | + function get_flag($lang) { | |
| 144 | + if (file_exists(POLYLANG_DIR.($file = '/flags/'.$lang->description.'.png'))) | |
| 145 | + $url = POLYLANG_URL.$file; | |
| 146 | + | |
| 147 | + // overwrite with custom flags | |
| 148 | + if (!is_admin() && ( // never use custom flags on admin side | |
| 149 | + file_exists(PLL_LOCAL_DIR.($file = '/'.$lang->description.'.png')) || | |
| 150 | + file_exists(PLL_LOCAL_DIR.($file = '/'.$lang->description.'.jpg')) )) | |
| 151 | + $url = PLL_LOCAL_URL.$file; | |
| 152 | + | |
| 153 | + $title = apply_filters('pll_flag_title', $lang->name, $lang->slug, $lang->description); | |
| 154 | + return isset($url) ? '<img src="'.esc_url($url).'" title="'.esc_attr($title).'" alt="'.esc_attr($lang->name).'" />' : ''; | |
| 155 | + } | |
| 156 | + | |
| 157 | + // adds terms clauses to get_terms - used in both frontend and admin | |
| 158 | + function _terms_clauses($clauses, $lang, $display_all = false) { | |
| 159 | + global $wpdb; | |
| 160 | + if (isset($lang) && !is_wp_error($lang)) { | |
| 161 | + $clauses['join'] .= $wpdb->prepare(" LEFT JOIN $wpdb->termmeta AS pll_tm ON t.term_id = pll_tm.term_id"); | |
| 162 | + $where_lang = $wpdb->prepare("pll_tm.meta_key = '_language' AND pll_tm.meta_value = %d", $lang->term_id); // add terms in the right language | |
| 163 | + $where_all = $wpdb->prepare("t.term_id NOT IN (SELECT term_id FROM $wpdb->termmeta WHERE meta_key IN ('_language'))"); // add terms with no language set | |
| 164 | + $clauses['where'] .= $display_all ? " AND (($where_lang) OR ($where_all))" : " AND $where_lang"; | |
| 148 | 165 | } |
| 166 | + return $clauses; | |
| 149 | 167 | } |
| 150 | 168 | |
| 151 | - /** | |
| 152 | - * Checks if Polylang is active on the new blog when the blog is switched. | |
| 153 | - * | |
| 154 | - * @since 3.0 | |
| 155 | - * | |
| 156 | - * @param int $new_blog_id New blog ID. | |
| 157 | - * @param int $prev_blog_id Previous blog ID. | |
| 158 | - * @return bool | |
| 159 | - */ | |
| 160 | - protected function is_active_on_new_blog( $new_blog_id, $prev_blog_id ) { | |
| 161 | - $plugins = ( $sitewide_plugins = get_site_option( 'active_sitewide_plugins' ) ) && is_array( $sitewide_plugins ) ? array_keys( $sitewide_plugins ) : array(); | |
| 162 | - $plugins = array_merge( $plugins, get_option( 'active_plugins', array() ) ); | |
| 169 | + // returns all page ids *not in* language defined by $lang_id | |
| 170 | + function exclude_pages($lang_id) { | |
| 171 | + $q = array( | |
| 172 | + 'numberposts'=>-1, | |
| 173 | + 'post_type' => 'page', | |
| 174 | + 'fields' => 'ids', | |
| 175 | + 'tax_query' => array(array( | |
| 176 | + 'taxonomy'=>'language', | |
| 177 | + 'fields' => 'id', | |
| 178 | + 'terms'=>$lang_id, | |
| 179 | + 'operator'=>'NOT IN' | |
| 180 | + )) | |
| 181 | + ); | |
| 182 | + return get_posts($q); | |
| 183 | + } | |
| 163 | 184 | |
| 164 | - /* | |
| 165 | - * The 2nd test is needed when Polylang is not networked activated. | |
| 166 | - * The 3rd test is needed when Polylang is networked activated and a new site is created. | |
| 167 | - */ | |
| 168 | - return $new_blog_id !== $prev_blog_id && in_array( POLYLANG_BASENAME, $plugins ) && get_option( 'polylang' ); | |
| 185 | + // register strings for translation | |
| 186 | + function register_string($name, $string) { | |
| 187 | + $this->strings[] = array('name'=> $name, 'string' => $string); | |
| 169 | 188 | } |
| 170 | -} | |
| 189 | + | |
| 190 | +} //class Polylang_Base | |
| 191 | +?> | |