PluginProbe
Polylang / 0.9.1
Polylang v0.9.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 / polylang.php

polylang.php in Polylang 0.9.1, at polylang.php

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