PluginProbe
Polylang / 0.8.7
Polylang v0.8.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 / polylang.php

polylang.php in Polylang 0.8.7, at polylang.php

402 lines 16.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Polylang
4 Plugin URI: http://wordpress.org/extend/plugins/polylang/
5 Version: 0.8.7
6 Author: F. Demarle
7 Description: Adds multilingual capability to Wordpress
8 */
9
10 /* Copyright 2011-2012 F. Demarle
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License, published by
14 the Free Software Foundation, either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 */
26
27 define('POLYLANG_VERSION', '0.8.7');
28 define('PLL_MIN_WP_VERSION', '3.1');
29
30 define('POLYLANG_DIR', dirname(__FILE__)); // our directory
31 define('PLL_INC', POLYLANG_DIR.'/include');
32
33 define('POLYLANG_URL', WP_PLUGIN_URL.'/'.basename(POLYLANG_DIR)); // our url
34
35 if (!defined('PLL_LOCAL_DIR'))
36 define('PLL_LOCAL_DIR', WP_CONTENT_DIR.'/polylang'); // default directory to store user data such as custom flags
37
38 if (!defined('PLL_LOCAL_URL'))
39 define('PLL_LOCAL_URL', WP_CONTENT_URL.'/polylang'); // default url to access user data such as custom flags
40
41 if (file_exists(PLL_LOCAL_DIR.'/pll-config.php'))
42 include_once(PLL_LOCAL_DIR.'/pll-config.php'); // includes local config file if exists
43
44 if (!defined('PLL_DISPLAY_ABOUT'))
45 define('PLL_DISPLAY_ABOUT', true); // displays the "About Polylang" metabox by default
46
47 if (!defined('PLL_DISPLAY_ALL'))
48 define('PLL_DISPLAY_ALL', false); // diplaying posts & terms with undefined language is disabled by default (unsupported since 0.7)
49
50 if (!defined('PLL_FILTER_HOME_URL'))
51 define('PLL_FILTER_HOME_URL', true); // filters the home url (to return the homepage in the right langage) by default
52
53 if (!defined('PLL_SYNC'))
54 define('PLL_SYNC', true); // synchronisation is enabled by default
55
56 require_once(PLL_INC.'/base.php');
57 require_once(PLL_INC.'/widget.php');
58 require_once(PLL_INC.'/calendar.php');
59
60 // controls the plugin, deals with activation, deactivation, upgrades, initialization as well as rewrite rules
61 class Polylang extends Polylang_Base {
62
63 function __construct() {
64 parent::__construct();
65 global $polylang; // globalize the variable to access it in the API
66
67 // manages plugin activation and deactivation
68 register_activation_hook( __FILE__, array(&$this, 'activate') );
69 register_deactivation_hook( __FILE__, array(&$this, 'deactivate') );
70
71 // stopping here if we are going to deactivate the plugin avoids breaking rewrite rules
72 if (isset($_GET['action']) && $_GET['action'] == 'deactivate' && isset($_GET['plugin']) && $_GET['plugin'] == 'polylang/polylang.php')
73 return;
74
75 // manages plugin upgrade
76 add_filter('upgrader_post_install', array(&$this, 'post_upgrade'));
77 add_action('admin_init', array(&$this, 'admin_init'));
78
79 // plugin and widget initialization
80 add_action('init', array(&$this, 'init'));
81 add_action('widgets_init', array(&$this, 'widgets_init'));
82 add_action('wp_loaded', array(&$this, 'prepare_rewrite_rules'), 20); // after Polylang_base::add_post_types_taxonomies
83
84 // separate admin and frontend
85 if (is_admin() && isset($_GET['page']) && $_GET['page'] == 'mlang') {
86 require_once(PLL_INC.'/admin-base.php');
87 require_once(PLL_INC.'/admin.php');
88 $polylang = new Polylang_Admin();
89 }
90 // avoid loading polylang admin filters for frontend ajax requests if 'pll_load_front' is set (thanks to g100g)
91 elseif (is_admin() && !(defined('DOING_AJAX') && isset($_REQUEST['pll_load_front']))) {
92 require_once(PLL_INC.'/admin-base.php');
93 require_once(PLL_INC.'/admin-filters.php');
94 $polylang = new Polylang_Admin_Filters();
95 }
96 else {
97 require_once(PLL_INC.'/core.php');
98 $polylang = new Polylang_Core();
99 }
100
101 // loads the API
102 require_once(PLL_INC.'/api.php');
103 }
104
105 // plugin activation for multisite
106 function activate() {
107 global $wp_version, $wpdb;
108 $style = '<p style = "font-family: sans-serif; font-size: 12px; color: #333; margin: -5px">%s</p>';
109
110 if (version_compare($wp_version, PLL_MIN_WP_VERSION , '<'))
111 die (sprintf($style, sprintf(__('You are using WordPress %s. Polylang requires at least WordPress %s.', 'polylang'), $wp_version, PLL_MIN_WP_VERSION)));
112
113 // check if it is a network activation - if so, run the activation function for each blog
114 if (is_multisite() && isset($_GET['networkwide']) && ($_GET['networkwide'] == 1)) {
115 foreach ($wpdb->get_col($wpdb->prepare("SELECT blog_id FROM $wpdb->blogs")) as $blog_id) {
116 switch_to_blog($blog_id);
117 $r = $this->_activate();
118 }
119 restore_current_blog();
120 }
121 else
122 $r = $this->_activate();
123
124 if (!$r)
125 die (sprintf($style, __('For some reasons, Polylang could not create a table in your database.', 'polylang')));
126 }
127
128 // plugin activation
129 function _activate() {
130 // create the termmeta table - not provided by WP by default - if it does not already exists
131 // uses exactly the same model as other meta tables to be able to use access functions provided by WP
132 global $wpdb;
133 $charset_collate = empty($wpdb->charset) ? '' : "DEFAULT CHARACTER SET $wpdb->charset";
134 $charset_collate .= empty($wpdb->collate) ? '' : " COLLATE $wpdb->collate";
135 $table = $wpdb->prefix . 'termmeta';
136
137 $r = $wpdb->query("CREATE TABLE IF NOT EXISTS $table (
138 meta_id bigint(20) unsigned NOT NULL auto_increment,
139 term_id bigint(20) unsigned NOT NULL default '0',
140 meta_key varchar(255) default NULL,
141 meta_value longtext,
142 PRIMARY KEY (meta_id),
143 KEY term_id (term_id),
144 KEY meta_key (meta_key)
145 ) $charset_collate;");
146
147 if ($r === false)
148 return false;
149
150 // codex tells to use the init action to call register_taxonomy but I need it now for my rewrite rules
151 register_taxonomy('language', null , array('label' => false, 'query_var'=>'lang'));
152
153 // defines default values for options in case this is the first installation
154 $options = get_option('polylang');
155 if (!$options) {
156 $options['browser'] = 1; // default language for the front page is set by browser preference
157 $options['rewrite'] = 1; // remove /language/ in permalinks (was the opposite before 0.7.2)
158 $options['hide_default'] = 0; // do not remove URL language information for default language
159 $options['force_lang'] = 0; // do not add URL language information when useless
160 $options['redirect_lang'] = 0; // do not redirect the language page to the homepage
161 }
162 $options['version'] = POLYLANG_VERSION;
163 update_option('polylang', $options);
164
165 // add our rewrite rules
166 $this->add_post_types_taxonomies();
167 $this->prepare_rewrite_rules();
168 flush_rewrite_rules();
169 return true;
170 }
171
172 // plugin deactivation for multisite
173 function deactivate() {
174 global $wpdb;
175
176 // check if it is a network deactivation - if so, run the deactivation function for each blog
177 if (is_multisite() && isset($_GET['networkwide']) && ($_GET['networkwide'] == 1)) {
178 foreach ($wpdb->get_col($wpdb->prepare("SELECT blog_id FROM $wpdb->blogs")) as $blog_id) {
179 switch_to_blog($blog_id);
180 $this->_deactivate();
181 }
182 restore_current_blog();
183 }
184 else
185 $this->_deactivate();
186 }
187
188 // plugin deactivation
189 function _deactivate() {
190 flush_rewrite_rules();
191 }
192
193 // restores the local_flags directory after upgrade from version 0.5.1 or older
194 function post_upgrade() {
195 // nothing to restore
196 if (!@is_dir($upgrade_dir = WP_CONTENT_DIR . '/upgrade/polylang/local_flags'))
197 return true;
198
199 // don't move if the directory is empty
200 $contents = @scandir($upgrade_dir);
201 if (is_array($contents) && ($files = array_diff($contents, array(".", "..", ".DS_Store", "_notes", "Thumbs.db"))) && empty($files))
202 return true;
203
204 // move the directory to wp-content
205 if (!@rename($upgrade_dir, PLL_LOCAL_DIR))
206 return new WP_Error('polylang_restore_error', sprintf('%s<br />%s',
207 __('Error: Restore of local flags failed!', 'polylang'),
208 sprintf(__('Please move your local flags from %s to %s', 'polylang'), esc_html($upgrade_dir), '<strong>'.esc_html(PLL_LOCAL_DIR).'</strong>')
209 ));
210
211 @rmdir(WP_CONTENT_DIR . '/upgrade/polylang');
212 return true;
213 }
214
215 // upgrades from old translation used up to V0.4.4 to new model used in V0.5+
216 function upgrade_translations($type, $ids) {
217 $listlanguages = $this->get_languages_list();
218 foreach ($ids as $id) {
219 $lang = call_user_func(array(&$this, 'get_'.$type.'_language'), $id);
220 if (!$lang)
221 continue;
222
223 $tr = array();
224 foreach ($listlanguages as $language) {
225 if ($meta = get_metadata($type, $id, '_lang-'.$language->slug, true))
226 $tr[$language->slug] = $meta;
227 }
228
229 if (!empty($tr)) {
230 $tr = serialize(array_merge(array($lang->slug => $id), $tr));
231 update_metadata($type, $id, '_translations', $tr);
232 }
233 }
234 }
235
236 // manage upgrade even when it is done manually
237 function admin_init() {
238 $options = get_option('polylang');
239 if (version_compare($options['version'], POLYLANG_VERSION, '<')) {
240
241 if (version_compare($options['version'], '0.4', '<'))
242 $options['hide_default'] = 0; // option introduced in 0.4
243
244 // translation model changed in V0.5
245 if (version_compare($options['version'], '0.5', '<')) {
246 $ids = get_posts(array('numberposts' => -1, 'fields' => 'ids', 'post_type' => 'any', 'post_status' => 'any'));
247 $this->upgrade_translations('post', $ids);
248 $ids = get_terms($this->taxonomies, array('get' => 'all', 'fields' => 'ids'));
249 $this->upgrade_translations('term', $ids);
250 }
251
252 // translation model changed in V0.5
253 // deleting the old one has been delayed in V0.6 (just in case...)
254 if (version_compare($options['version'], '0.6', '<')) {
255 $listlanguages = $this->get_languages_list();
256
257 $ids = get_posts(array('numberposts'=> -1, 'fields' => 'ids', 'post_type'=>'any', 'post_status'=>'any'));
258 foreach ($ids as $id) {
259 foreach ($listlanguages as $lang)
260 delete_post_meta($id, '_lang-'.$lang->slug);
261 }
262
263 $ids = get_terms($this->taxonomies, array('get' => 'all', 'fields' => 'ids'));
264 foreach ($ids as $id) {
265 foreach ($listlanguages as $lang)
266 delete_metadata('term', $id, '_lang-'.$lang->slug);
267 }
268 }
269
270 if (version_compare($options['version'], '0.7', '<'))
271 $options['force_lang'] = 0; // option introduced in 0.7
272
273 // string translation storage model changed and option added in 0.8
274 if (version_compare($options['version'], '0.8dev1', '<')) {
275 if (function_exists('base64_decode')) {
276 $mo = new MO();
277 foreach ($this->get_languages_list() as $language) {
278 $reader = new POMO_StringReader(base64_decode(get_option('polylang_mo'.$language->term_id)));
279 $mo->import_from_reader($reader);
280 $this->mo_export($mo, $language);
281 }
282 }
283 $options['redirect_lang'] = 0; // option introduced in 0.8
284 }
285
286 if (version_compare($options['version'], '0.8.2', '<'))
287 flush_rewrite_rules(); // rewrite rules have been modified in 0.7.1 & 0.7.2 & 0.8 & 0.8.1 & 0.8.2
288
289 $options['version'] = POLYLANG_VERSION;
290 update_option('polylang', $options);
291 }
292 }
293
294 // some initialization
295 function init() {
296 global $wpdb;
297 $wpdb->termmeta = $wpdb->prefix . 'termmeta'; // registers the termmeta table in wpdb
298 $options = get_option('polylang');
299
300 // registers the language taxonomy
301 // codex: use the init action to call this function
302 // object types will be set later once all custom post types are registered
303 register_taxonomy('language', null, array(
304 'label' => false,
305 'public' => false, // avoid displaying the 'like post tags text box' in the quick edit
306 'query_var'=>'lang',
307 'update_count_callback' => '_update_post_term_count'));
308
309 // optionaly removes 'language' in permalinks so that we get http://www.myblog/en/ instead of http://www.myblog/language/en/
310 // language information always in front of the uri ('with_front' => false)
311 // the 3rd parameter structure has been modified in WP 3.4
312 add_permastruct('language', $options['rewrite'] ? '%language%' : 'language/%language%', version_compare($GLOBALS['wp_version'], '3.4' , '<') ? false : array('with_front' => false));
313
314 load_plugin_textdomain('polylang', false, basename(POLYLANG_DIR).'/languages'); // plugin i18n
315 }
316
317 // registers our widgets
318 function widgets_init() {
319 register_widget('Polylang_Widget');
320
321 // overwrites the calendar widget to filter posts by language
322 unregister_widget('WP_Widget_Calendar');
323 register_widget('Polylang_Widget_Calendar');
324 }
325
326 // complete our taxonomy and add rewrite rules filters once custom post types and taxonomies are registered (normally in init)
327 function prepare_rewrite_rules() {
328 foreach ($this->post_types as $post_type)
329 register_taxonomy_for_object_type('language', $post_type);
330
331 // don't modify the rules if there is no languages created yet
332 if (!$this->get_languages_list())
333 return;
334
335 $types = array_merge(array('post', 'date', 'root', 'comments', 'search', 'author', 'page'), array_keys($GLOBALS['wp_rewrite']->extra_permastructs));
336 $types = apply_filters('pll_rewrite_rules', $types); // allow plugins to add rewrite rules to the language filter
337 foreach ($types as $type)
338 add_filter($type . '_rewrite_rules', array(&$this, 'rewrite_rules'));
339
340 add_filter('rewrite_rules_array', array(&$this, 'rewrite_rules')); // needed for post type archives
341 }
342
343 // the rewrite rules !
344 // always make sure the default language is at the end in case the language information is hidden for default language
345 // thanks to brbrbr http://wordpress.org/support/topic/plugin-polylang-rewrite-rules-not-correct
346 function rewrite_rules($rules) {
347 // suppress the rules created by WordPress for our taxonomy
348 if (($current_filter = current_filter()) == 'language_rewrite_rules')
349 return array();
350
351 global $wp_rewrite;
352 $options = get_option('polylang');
353 $always_rewrite = in_array(str_replace('_rewrite_rules', '', $current_filter), array('date', 'root', 'comments', 'author', 'post_format'));
354 $newrules = array();
355
356 foreach ($this->get_languages_list() as $language)
357 if (!$options['hide_default'] || $options['default_lang'] != $language->slug)
358 $languages[] = $language->slug;
359
360 if (isset($languages))
361 $slug = $wp_rewrite->root . ($options['rewrite'] ? '' : 'language/') . '('.implode('|', $languages).')/';
362
363 foreach ($rules as $key => $rule) {
364 // we don't need the lang parameter for post types and taxonomies
365 // moreover adding it would create issues for pages and taxonomies
366 if ($options['force_lang'] && in_array(str_replace('_rewrite_rules', '', $current_filter), array_merge($this->post_types, $this->taxonomies))) {
367 if (isset($slug))
368 $newrules[$slug.str_replace($wp_rewrite->root, '', $key)] = str_replace(array('[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]'),
369 array('[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]'), $rule); // hopefully it is sufficient !
370
371 if ($options['hide_default'])
372 $newrules[$key] = $rules[$key];
373
374 unset($rules[$key]); // now useless
375 }
376
377 // rewrite rules filtered by language
378 elseif ($always_rewrite || strpos($rule, 'post_type=') || ($current_filter != 'rewrite_rules_array' && $options['force_lang'])) {
379 if (isset($slug))
380 $newrules[$slug.str_replace($wp_rewrite->root, '', $key)] = str_replace(array('[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]', '?'),
381 array('[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '?lang=$matches[1]&'), $rule); // hopefully it is sufficient !
382
383 if ($options['hide_default'])
384 $newrules[$key] = str_replace('?', '?lang='.$options['default_lang'].'&', $rule);
385
386 unset($rules[$key]); // now useless
387 }
388 }
389
390 // the home rewrite rule
391 if ($current_filter == 'root_rewrite_rules' && isset($slug))
392 $newrules[$slug.'?$'] = 'index.php?lang=$matches[1]';
393
394 return $newrules + $rules;
395 }
396
397 } // class Polylang
398
399 if (class_exists("Polylang"))
400 new Polylang();
401 ?>
402