PluginProbe
Polylang / 0.9.4
Polylang v0.9.4
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.4, at polylang.php

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