PluginProbe
Polylang / 0.9.5
Polylang v0.9.5
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.5, at polylang.php

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