PluginProbe
Polylang / 0.7
Polylang v0.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
← All changes | polylang.php +358 -94 3.8.70.7 View file →
@@ -1,94 +1,358 @@
1 -<?php
2 -/**
3 - * Polylang
4 - *
5 - * @package Polylang
6 - * @author WP SYNTEX
7 - * @license GPL-3.0-or-later
8 - *
9 - * @wordpress-plugin
10 - * Plugin Name: Polylang
11 - * Plugin URI: https://polylang.pro
12 - * Description: Adds multilingual capability to WordPress
13 - * Version: 3.8.7
14 - * Requires at least: 6.5
15 - * Requires PHP: 7.4
16 - * Author: WP SYNTEX
17 - * Author URI: https://polylang.pro
18 - * Text Domain: polylang
19 - * License: GPL v3 or later
20 - * License URI: https://www.gnu.org/licenses/gpl-3.0.txt
21 - *
22 - * Copyright 2011-2019 Frédéric Demarle
23 - * Copyright 2019-2026 WP SYNTEX
24 - *
25 - * This program is free software: you can redistribute it and/or modify
26 - * it under the terms of the GNU General Public License as published by
27 - * the Free Software Foundation, either version 3 of the License, or
28 - * (at your option) any later version.
29 - *
30 - * This program is distributed in the hope that it will be useful,
31 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 - * GNU General Public License for more details.
34 - *
35 - * You should have received a copy of the GNU General Public License
36 - * along with this program. If not, see <https://www.gnu.org/licenses/>.
37 - */
38 -
39 -defined( 'ABSPATH' ) || exit;
40 -
41 -if ( defined( 'POLYLANG_VERSION' ) ) {
42 - // The user is attempting to activate a second plugin instance, typically Polylang and Polylang Pro.
43 - require_once ABSPATH . 'wp-admin/includes/plugin.php';
44 - require_once ABSPATH . 'wp-includes/pluggable.php';
45 -
46 - if ( is_plugin_active( plugin_basename( __FILE__ ) ) ) {
47 - deactivate_plugins( plugin_basename( __FILE__ ) ); // Deactivate this plugin.
48 - // WP does not allow us to send a custom meaningful message, so just tell the plugin has been deactivated.
49 - wp_safe_redirect( add_query_arg( 'deactivate', 'true', remove_query_arg( 'activate' ) ) );
50 - exit;
51 - }
52 - return;
53 -}
54 -
55 -// Go on loading the plugin.
56 -define( 'POLYLANG_VERSION', '3.8.7' );
57 -define( 'PLL_MIN_WP_VERSION', '6.5' );
58 -define( 'PLL_MIN_PHP_VERSION', '7.4' );
59 -
60 -define( 'POLYLANG_FILE', __FILE__ );
61 -define( 'POLYLANG_DIR', __DIR__ );
62 -
63 -// Whether we are using Polylang or Polylang Pro, get the filename of the plugin in use.
64 -if ( ! defined( 'POLYLANG_ROOT_FILE' ) ) {
65 - define( 'POLYLANG_ROOT_FILE', __FILE__ );
66 -}
67 -
68 -if ( ! defined( 'POLYLANG_BASENAME' ) ) {
69 - define( 'POLYLANG_BASENAME', plugin_basename( __FILE__ ) ); // Plugin name as known by WP.
70 - require __DIR__ . '/vendor/autoload.php';
71 -}
72 -
73 -define( 'POLYLANG', ucwords( str_replace( '-', ' ', dirname( POLYLANG_BASENAME ) ) ) );
74 -
75 -if ( ! empty( $_GET['deactivate-polylang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
76 - return;
77 -}
78 -
79 -require POLYLANG_DIR . '/src/constant-functions.php';
80 -if ( ! PLL_Usable::can_activate() ) {
81 - // WP version or php version is too old.
82 - return;
83 -}
84 -
85 -define( 'POLYLANG_ACTIVE', true );
86 -
87 -if ( PLL_Deactivate::is_deactivation() ) {
88 - // Stopping here if we are going to deactivate the plugin (avoids breaking rewrite rules).
89 - PLL_Deactivate::add_hooks();
90 - return;
91 -}
92 -
93 -PLL_Activate::add_hooks();
94 -new Polylang();
1 +<?php
2 +/*
3 +Plugin Name: Polylang
4 +Plugin URI: http://wordpress.org/extend/plugins/polylang/
5 +Version: 0.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, version 2, as
14 + published by the Free Software Foundation.
15 +
16 + This program is distributed in the hope that it will be useful,
17 + but WITHOUT ANY WARRANTY; without even the implied warranty of
18 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 + GNU General Public License for more details.
20 +
21 + You should have received a copy of the GNU General Public License
22 + along with this program; if not, write to the Free Software
23 + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 +*/
25 +
26 +define('POLYLANG_VERSION', '0.7');
27 +define('PLL_MIN_WP_VERSION', '3.1');
28 +
29 +define('POLYLANG_DIR', dirname(__FILE__)); // our directory
30 +define('PLL_INC', POLYLANG_DIR.'/include');
31 +
32 +define('POLYLANG_URL', WP_PLUGIN_URL.'/'.basename(POLYLANG_DIR)); // our url
33 +
34 +if (!defined('PLL_LOCAL_DIR'))
35 + define('PLL_LOCAL_DIR', WP_CONTENT_DIR.'/polylang'); // default directory to store user data such as custom flags
36 +
37 +if (!defined('PLL_LOCAL_URL'))
38 + define('PLL_LOCAL_URL', WP_CONTENT_URL.'/polylang'); // default url to access user data such as custom flags
39 +
40 +if (!defined('PLL_DISPLAY_ALL'))
41 + define('PLL_DISPLAY_ALL', false); // diplaying posts & terms with undefined language is disabled by default
42 +
43 +require_once(PLL_INC.'/base.php');
44 +require_once(PLL_INC.'/widget.php');
45 +require_once(PLL_INC.'/calendar.php');
46 +
47 +// controls the plugin, deals with activation, deactivation, upgrades, initialization as well as rewrite rules
48 +class Polylang extends Polylang_Base {
49 +
50 + function __construct() {
51 + global $polylang; // globalize the variable to access it in the API
52 +
53 + // manages plugin activation and deactivation
54 + register_activation_hook( __FILE__, array(&$this, 'activate') );
55 + register_deactivation_hook( __FILE__, array(&$this, 'deactivate') );
56 +
57 + // stopping here if we are going to deactivate the plugin avoids breaking rewrite rules
58 + if (isset($_GET['action']) && $_GET['action'] == 'deactivate' && isset($_GET['plugin']) && $_GET['plugin'] == 'polylang/polylang.php')
59 + return;
60 +
61 + // manages plugin upgrade
62 + add_filter('upgrader_post_install', array(&$this, 'post_upgrade'));
63 + add_action('admin_init', array(&$this, 'admin_init'));
64 +
65 + // plugin and widget initialization
66 + add_action('init', array(&$this, 'init'));
67 + add_action('widgets_init', array(&$this, 'widgets_init'));
68 +
69 + // rewrite rules
70 + add_filter('rewrite_rules_array', array(&$this, 'rewrite_rules_array' ));
71 +
72 + // separate admin and frontend
73 + if (is_admin()) {
74 + require_once(PLL_INC.'/admin.php');
75 + $polylang = new Polylang_Admin();
76 + }
77 + else {
78 + require_once(PLL_INC.'/core.php');
79 + $polylang = new Polylang_Core();
80 + }
81 +
82 + // loads the API
83 + require_once(PLL_INC.'/api.php');
84 + }
85 +
86 + // plugin activation for multisite
87 + function activate() {
88 + global $wp_version, $wpdb;
89 +
90 + if (version_compare($wp_version, PLL_MIN_WP_VERSION , '<'))
91 + die (sprintf('<p style = "font-family: sans-serif; font-size: 12px; color: #333; margin: -5px">%s</p>',
92 + sprintf(__('You are using WordPress %s. Polylang requires at least WordPress %s.', 'polylang'), $wp_version, PLL_MIN_WP_VERSION)));
93 +
94 + // check if it is a network activation - if so, run the activation function for each blog
95 + if (is_multisite() && isset($_GET['networkwide']) && ($_GET['networkwide'] == 1)) {
96 + foreach ($wpdb->get_col($wpdb->prepare("SELECT blog_id FROM $wpdb->blogs")) as $blog_id) {
97 + switch_to_blog($blog_id);
98 + $this->_activate();
99 + }
100 + restore_current_blog();
101 + }
102 + else
103 + $this->_activate();
104 + }
105 +
106 + // plugin activation
107 + function _activate() {
108 + // create the termmeta table - not provided by WP by default - if it does not already exists
109 + // uses exactly the same model as other meta tables to be able to use access functions provided by WP
110 + global $wpdb;
111 + $charset_collate = '';
112 + if ( ! empty($wpdb->charset) )
113 + $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
114 + if ( ! empty($wpdb->collate) )
115 + $charset_collate .= " COLLATE $wpdb->collate";
116 +
117 + $table = $wpdb->prefix . 'termmeta';
118 +
119 + $wpdb->query("CREATE TABLE IF NOT EXISTS $table (
120 + meta_id bigint(20) unsigned NOT NULL auto_increment,
121 + term_id bigint(20) unsigned NOT NULL default '0',
122 + meta_key varchar(255) default NULL,
123 + meta_value longtext,
124 + PRIMARY KEY (meta_id),
125 + KEY term_id (term_id),
126 + KEY meta_key (meta_key)
127 + ) $charset_collate;");
128 +
129 + // codex tells to use the init action to call register_taxonomy but I need it now for my rewrite rules
130 + register_taxonomy('language', get_post_types(array('show_ui' => true)), array('label' => false, 'query_var'=>'lang'));
131 +
132 + // defines default values for options in case this is the first installation
133 + $options = get_option('polylang');
134 + if (!$options) {
135 + $options['browser'] = 1; // default language for the front page is set by browser preference
136 + $options['rewrite'] = 0; // do not remove /language/ in permalinks
137 + $options['hide_default'] = 0; // do not remove URL language information for default language
138 + $options['force_lang'] = 0; // do not add URL language information when useless
139 + }
140 + $options['version'] = POLYLANG_VERSION;
141 + update_option('polylang', $options);
142 +
143 + // add our rewrite rules
144 + global $wp_rewrite;
145 + $wp_rewrite->flush_rules();
146 + }
147 +
148 + // plugin deactivation for multisite
149 + function deactivate() {
150 + global $wpdb;
151 +
152 + // check if it is a network deactivation - if so, run the deactivation function for each blog
153 + if (is_multisite() && isset($_GET['networkwide']) && ($_GET['networkwide'] == 1)) {
154 + foreach ($wpdb->get_col($wpdb->prepare("SELECT blog_id FROM $wpdb->blogs")) as $blog_id) {
155 + switch_to_blog($blog_id);
156 + $this->_deactivate();
157 + }
158 + restore_current_blog();
159 + }
160 + else
161 + $this->_deactivate();
162 + }
163 +
164 + // plugin deactivation
165 + function _deactivate() {
166 + global $wp_rewrite;
167 + $wp_rewrite->flush_rules();
168 + }
169 +
170 + // restores the local_flags directory after upgrade from version 0.5.1 or older
171 + function post_upgrade() {
172 + // nothing to restore
173 + if (!@is_dir($upgrade_dir = WP_CONTENT_DIR . '/upgrade/polylang/local_flags'))
174 + return true;
175 +
176 + // don't move if the directory is empty
177 + $contents = @scandir($upgrade_dir);
178 + if (is_array($contents) && ($files = array_diff($contents, array(".", "..", ".DS_Store", "_notes", "Thumbs.db"))) && empty($files))
179 + return true;
180 +
181 + // move the directory to wp-content
182 + if (!@rename($upgrade_dir, PLL_LOCAL_DIR))
183 + return new WP_Error('polylang_restore_error', sprintf('%s<br />%s',
184 + __('Error: Restore of local flags failed!', 'polylang'),
185 + sprintf(__('Please move your local flags from %s to %s', 'polylang'), esc_html($upgrade_dir), '<strong>'.esc_html(PLL_LOCAL_DIR).'</strong>')
186 + ));
187 +
188 + @rmdir(WP_CONTENT_DIR . '/upgrade/polylang');
189 + return true;
190 + }
191 +
192 + // upgrades from old translation used up to V0.4.4 to new model used in V0.5+
193 + function upgrade_translations($type, $ids) {
194 + $listlanguages = $this->get_languages_list();
195 + foreach ($ids as $id) {
196 + $lang = call_user_func(array(&$this, 'get_'.$type.'_language'), $id);
197 + if (!$lang)
198 + continue;
199 +
200 + $tr = array();
201 + foreach ($listlanguages as $language) {
202 + if ($meta = get_metadata($type, $id, '_lang-'.$language->slug, true))
203 + $tr[$language->slug] = $meta;
204 + }
205 +
206 + if(!empty($tr)) {
207 + $tr = serialize(array_merge(array($lang->slug => $id), $tr));
208 + update_metadata($type, $id, '_translations', $tr);
209 + }
210 + }
211 + }
212 +
213 + // manage upgrade even when it is done manually
214 + function admin_init() {
215 + $options = get_option('polylang');
216 + if (version_compare($options['version'], POLYLANG_VERSION, '<')) {
217 +
218 + if (version_compare($options['version'], '0.4', '<'))
219 + $options['hide_default'] = 0; // option introduced in 0.4
220 +
221 + // translation model changed in V0.5
222 + if (version_compare($options['version'], '0.5', '<')) {
223 + $ids = get_posts(array('numberposts'=>-1, 'fields' => 'ids', 'post_type'=>'any', 'post_status'=>'any'));
224 + $this->upgrade_translations('post', $ids);
225 + $ids = get_terms(get_taxonomies(array('show_ui'=>true)), array('get'=>'all', 'fields'=>'ids'));
226 + $this->upgrade_translations('term', $ids);
227 + }
228 +
229 + // translation model changed in V0.5
230 + // deleting the old one has been delayed in V0.6 (just in case...)
231 + if (version_compare($options['version'], '0.6', '<')) {
232 + $listlanguages = $this->get_languages_list();
233 +
234 + $ids = get_posts(array('numberposts'=> -1, 'fields' => 'ids', 'post_type'=>'any', 'post_status'=>'any'));
235 + foreach ($ids as $id) {
236 + foreach ($listlanguages as $lang)
237 + delete_post_meta($id, '_lang-'.$lang->slug);
238 + }
239 +
240 + $ids = get_terms(get_taxonomies(array('show_ui'=>true)), array('get'=>'all', 'fields'=>'ids'));
241 + foreach ($ids as $id) {
242 + foreach ($listlanguages as $lang)
243 + delete_metadata('term', $id, '_lang-'.$lang->slug);
244 + }
245 + }
246 +
247 + if (version_compare($options['version'], '0.7', '<'))
248 + $options['force_lang'] = 0; // option introduced in 0.7
249 +
250 + $options['version'] = POLYLANG_VERSION;
251 + update_option('polylang', $options);
252 + }
253 + }
254 +
255 + // some initialization
256 + function init() {
257 + global $wpdb;
258 + $wpdb->termmeta = $wpdb->prefix . 'termmeta'; // registers the termmeta table in wpdb
259 +
260 + // registers the language taxonomy
261 + // codex: use the init action to call this function
262 + register_taxonomy('language', get_post_types(array('show_ui' => true)), array(
263 + 'label' => false,
264 + 'public' => false, // avoid displaying the 'like post tags text box' in the quick edit
265 + 'query_var'=>'lang',
266 + 'update_count_callback' => '_update_post_term_count'));
267 +
268 + // optionaly removes 'language' in permalinks so that we get http://www.myblog/en/ instead of http://www.myblog/language/en/
269 + // the simple line of code is inspired by the WP No Category Base plugin: http://wordpresssupplies.com/wordpress-plugins/no-category-base/
270 + global $wp_rewrite;
271 + $options = get_option('polylang');
272 + if ($options['rewrite'] && $wp_rewrite->extra_permastructs)
273 + $wp_rewrite->extra_permastructs['language'][0] = '%language%';
274 +
275 + load_plugin_textdomain('polylang', false, basename(POLYLANG_DIR).'/languages'); // plugin i18n
276 + }
277 +
278 + // registers our widgets
279 + function widgets_init() {
280 + register_widget('Polylang_Widget');
281 +
282 + // overwrites the calendar widget to filter posts by language
283 + unregister_widget('WP_Widget_Calendar');
284 + register_widget('Polylang_Widget_Calendar');
285 + }
286 +
287 + // rewrites rules if pretty permalinks are used
288 + function rewrite_rules_array($rules) {
289 + $options = get_option('polylang');
290 + $newrules = array();
291 +
292 + // don't modify the rules if there is no languages created
293 + if (!($listlanguages = $this->get_languages_list()))
294 + return $rules;
295 +
296 + // modifies the rules created by WordPress when '/language/' is removed in permalinks
297 + if ($options['rewrite']) {
298 + foreach ($listlanguages as $language) {
299 + $slug = $options['default_lang'] == $language->slug && $options['hide_default'] ? '' : $language->slug . '/';
300 + $newrules[$slug.'feed/(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?lang='.$language->slug.'&feed=$matches[1]';
301 + $newrules[$slug.'(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?lang='.$language->slug.'&feed=$matches[1]';
302 + $newrules[$slug.'page/?([0-9]{1,})/?$'] = 'index.php?lang='.$language->slug.'&paged=$matches[1]';
303 + if ($slug)
304 + $newrules[$slug.'?$'] = 'index.php?lang='.$language->slug;
305 + }
306 + unset($rules['([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$']); // => index.php?lang=$matches[1]&feed=$matches[2]
307 + unset($rules['([^/]+)/(feed|rdf|rss|rss2|atom)/?$']); // => index.php?lang=$matches[1]&feed=$matches[2]
308 + unset($rules['([^/]+)/page/?([0-9]{1,})/?$']); // => index.php?lang=$matches[1]&paged=$matches[2]
309 + unset($rules['([^/]+)/?$']); // => index.php?lang=$matches[1]
310 + }
311 +
312 + $base = $options['rewrite'] ? '' : 'language/';
313 +
314 + // rewrite rules for comments feed filtered by language
315 + foreach ($listlanguages as $language) {
316 + $slug = $options['default_lang'] == $language->slug && $options['hide_default'] ? '' : $base.$language->slug . '/';
317 + $newrules[$slug.'comments/feed/(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?lang='.$language->slug.'&feed=$matches[1]&withcomments=1';
318 + $newrules[$slug.'comments/(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?lang='.$language->slug.'&feed=$matches[1]&withcomments=1';
319 + }
320 + unset($rules['comments/feed/(feed|rdf|rss|rss2|atom)/?$']); // => index.php?&feed=$matches[1]&withcomments=1
321 + unset($rules['comments/(feed|rdf|rss|rss2|atom)/?$']); // => index.php?&feed=$matches[1]&withcomments=1
322 +
323 + // rewrite rules for archives filtered by language
324 + foreach ($rules as $key => $rule) {
325 + $is_archive = strpos($rule, 'post_format=') || strpos($rule, 'author_name=') || strpos($rule, 'post_type=') || strpos($rule, 'year=') && !(
326 + strpos($rule, 'p=') ||
327 + strpos($rule, 'name=') ||
328 + strpos($rule, 'page=') ||
329 + strpos($rule, 'cpage=') );
330 +
331 + if ($is_archive) {
332 + foreach ($listlanguages as $language) {
333 + $slug = $options['default_lang'] == $language->slug && $options['hide_default'] ? '' : $base.$language->slug . '/';
334 + $newrules[$slug.$key] = str_replace('?', '?lang='.$language->slug.'&', $rule);
335 + }
336 + unset($rules[$key]); // now useless
337 + }
338 + }
339 +
340 + // optionally add the language information to all urls
341 + if ($options['force_lang']) {
342 + foreach ($rules as $key => $rule) {
343 + foreach ($listlanguages as $language) {
344 + $slug = $options['default_lang'] == $language->slug && $options['hide_default'] ? '' : $base.$language->slug . '/';
345 + $newrules[$slug.$key] = $rules[$key];
346 + }
347 + unset($rules[$key]); // now useless
348 + }
349 + }
350 +
351 + return $newrules + $rules;
352 + }
353 +
354 +} // class Polylang
355 +
356 +if (class_exists("Polylang"))
357 + new Polylang();
358 +?>