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

412 lines 16.6 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
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');
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 // registers the language taxonomy
308 // codex: use the init action to call this function
309 // object types will be set later once all custom post types are registered
310 register_taxonomy('language', null, array(
311 'label' => false,
312 'public' => false, // avoid displaying the 'like post tags text box' in the quick edit
313 'query_var'=>'lang',
314 'update_count_callback' => '_update_post_term_count'));
315
316 // optionaly removes 'language' in permalinks so that we get http://www.myblog/en/ instead of http://www.myblog/language/en/
317 // language information always in front of the uri ('with_front' => false)
318 // the 3rd parameter structure has been modified in WP 3.4
319 add_permastruct('language', $options['rewrite'] ? '%language%' : 'language/%language%', version_compare($GLOBALS['wp_version'], '3.4' , '<') ? false : array('with_front' => false));
320
321 load_plugin_textdomain('polylang', false, basename(POLYLANG_DIR).'/languages'); // plugin i18n
322 }
323
324 // registers our widgets
325 function widgets_init() {
326 require_once(PLL_INC.'/widget.php');
327 require_once(PLL_INC.'/calendar.php'); // loads this only now otherwise it breaks widgets not registered in a widgets_init hook
328
329 register_widget('Polylang_Widget');
330
331 // overwrites the calendar widget to filter posts by language
332 unregister_widget('WP_Widget_Calendar');
333 register_widget('Polylang_Widget_Calendar');
334 }
335
336 // complete our taxonomy and add rewrite rules filters once custom post types and taxonomies are registered (normally in init)
337 function prepare_rewrite_rules() {
338 foreach ($this->post_types as $post_type)
339 register_taxonomy_for_object_type('language', $post_type);
340
341 // don't modify the rules if there is no languages created yet
342 if (!$this->get_languages_list())
343 return;
344
345 $types = array_merge(array('post', 'date', 'root', 'comments', 'search', 'author', 'page'), array_keys($GLOBALS['wp_rewrite']->extra_permastructs));
346 $types = apply_filters('pll_rewrite_rules', $types); // allow plugins to add rewrite rules to the language filter
347 foreach ($types as $type)
348 add_filter($type . '_rewrite_rules', array(&$this, 'rewrite_rules'));
349
350 add_filter('rewrite_rules_array', array(&$this, 'rewrite_rules')); // needed for post type archives
351 }
352
353 // the rewrite rules !
354 // always make sure the default language is at the end in case the language information is hidden for default language
355 // thanks to brbrbr http://wordpress.org/support/topic/plugin-polylang-rewrite-rules-not-correct
356 function rewrite_rules($rules) {
357 // suppress the rules created by WordPress for our taxonomy
358 if (($current_filter = current_filter()) == 'language_rewrite_rules')
359 return array();
360
361 global $wp_rewrite;
362 $options = get_option('polylang');
363 $always_rewrite = in_array(str_replace('_rewrite_rules', '', $current_filter), array('date', 'root', 'comments', 'author', 'post_format'));
364 $newrules = array();
365
366 foreach ($this->get_languages_list() as $language)
367 if (!$options['hide_default'] || $options['default_lang'] != $language->slug)
368 $languages[] = $language->slug;
369
370 if (isset($languages))
371 $slug = $wp_rewrite->root . ($options['rewrite'] ? '' : 'language/') . '('.implode('|', $languages).')/';
372
373 foreach ($rules as $key => $rule) {
374 // we don't need the lang parameter for post types and taxonomies
375 // moreover adding it would create issues for pages and taxonomies
376 if ($options['force_lang'] && in_array(str_replace('_rewrite_rules', '', $current_filter), array_merge($this->post_types, $this->taxonomies))) {
377 if (isset($slug))
378 $newrules[$slug.str_replace($wp_rewrite->root, '', $key)] = str_replace(array('[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]', '[1]'),
379 array('[9]', '[8]', '[7]', '[6]', '[5]', '[4]', '[3]', '[2]'), $rule); // hopefully it is sufficient !
380
381 if ($options['hide_default'])
382 $newrules[$key] = $rules[$key];
383
384 unset($rules[$key]); // now useless
385 }
386
387 // rewrite rules filtered by language
388 elseif ($always_rewrite || (strpos($rule, 'post_type=') && !strpos($rule, 'name=')) || ($current_filter != 'rewrite_rules_array' && $options['force_lang'])) {
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]', '?lang=$matches[1]&'), $rule); // hopefully it is sufficient !
392
393 if ($options['hide_default'])
394 $newrules[$key] = str_replace('?', '?lang='.$options['default_lang'].'&', $rule);
395
396 unset($rules[$key]); // now useless
397 }
398 }
399
400 // the home rewrite rule
401 if ($current_filter == 'root_rewrite_rules' && isset($slug))
402 $newrules[$slug.'?$'] = 'index.php?lang=$matches[1]';
403
404 return $newrules + $rules;
405 }
406
407 } // class Polylang
408
409 if (class_exists("Polylang"))
410 new Polylang();
411 ?>
412