PluginProbe
Polylang / trunk
Polylang vtrunk
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 / src / settings / settings.php

settings.php in Polylang trunk, at src/settings/settings.php

437 lines 12.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 defined( 'ABSPATH' ) || exit;
7
8 /**
9 * A class for the Polylang settings pages, accessible from @see PLL().
10 *
11 * @since 1.2
12 */
13 class PLL_Settings extends PLL_Admin_Base {
14 /**
15 * Array of modules classes.
16 *
17 * @var PLL_Settings_Module[]|null
18 */
19 protected $modules;
20
21 /**
22 * Constructor
23 *
24 * @since 1.2
25 *
26 * @param PLL_Links_Model $links_model Reference to the links model.
27 */
28 public function __construct( &$links_model ) {
29 parent::__construct( $links_model );
30
31 PLL_Admin_Strings::init();
32
33 add_action( 'admin_init', array( $this, 'register_settings_modules' ) );
34
35 // Adds screen options and the about box in the languages admin panel.
36 add_action( 'load-' . self::get_screen_id( 'lang' ), array( $this, 'load_page' ) );
37 add_action( 'load-' . self::get_screen_id( 'strings' ), array( $this, 'load_page_strings' ) );
38
39 // Saves the per-page value in screen options.
40 add_filter( 'set_screen_option_pll_lang_per_page', array( $this, 'set_screen_option' ), 10, 3 );
41 add_filter( 'set_screen_option_pll_strings_per_page', array( $this, 'set_screen_option' ), 10, 3 );
42 }
43
44 /**
45 * Initializes the modules
46 *
47 * @since 1.8
48 *
49 * @return void
50 */
51 public function register_settings_modules() {
52 $modules = array();
53
54 if ( $this->model->has_languages() ) {
55 $modules = array(
56 'PLL_Settings_Url',
57 'PLL_Settings_Browser',
58 'PLL_Settings_Media',
59 'PLL_Settings_CPT',
60 );
61 }
62
63 $modules[] = 'PLL_Settings_Licenses';
64
65 /**
66 * Filter the list of setting modules
67 *
68 * @since 1.8
69 *
70 * @param array $modules the list of module classes
71 */
72 $modules = apply_filters( 'pll_settings_modules', $modules );
73
74 foreach ( $modules as $key => $class ) {
75 $key = is_numeric( $key ) ? strtolower( str_replace( 'PLL_Settings_', '', $class ) ) : $key;
76 $this->modules[ $key ] = new $class( $this );
77 }
78 }
79
80 /**
81 * Loads the about metabox
82 *
83 * @since 0.8
84 *
85 * @return void
86 */
87 public function metabox_about() {
88 include __DIR__ . '/view-about.php';
89 }
90
91 /**
92 * Adds screen options and the about box in the languages admin panel
93 *
94 * @since 0.9.5
95 *
96 * @return void
97 */
98 public function load_page() {
99 if ( ! defined( 'PLL_DISPLAY_ABOUT' ) || PLL_DISPLAY_ABOUT ) {
100 add_meta_box(
101 'pll-about-box',
102 __( 'About Polylang', 'polylang' ),
103 array( $this, 'metabox_about' ),
104 self::get_screen_id( 'lang' ),
105 'normal'
106 );
107 }
108
109 add_screen_option(
110 'per_page',
111 array(
112 'label' => __( 'Languages', 'polylang' ),
113 'default' => 10,
114 'option' => 'pll_lang_per_page',
115 )
116 );
117
118 add_action( 'admin_notices', array( $this, 'notice_objects_with_no_lang' ) );
119 }
120
121 /**
122 * Adds screen options in the strings translations admin panel
123 *
124 * @since 2.1
125 *
126 * @return void
127 */
128 public function load_page_strings() {
129 add_screen_option(
130 'per_page',
131 array(
132 'label' => __( 'Strings translations', 'polylang' ),
133 'default' => 10,
134 'option' => 'pll_strings_per_page',
135 )
136 );
137 }
138
139 /**
140 * Saves the number of rows in the languages or strings table set by this user.
141 *
142 * @since 0.9.5
143 *
144 * @param mixed $screen_option False or value returned by a previous filter, not used.
145 * @param string $option The name of the option, not used.
146 * @param int $value The new value of the option to save.
147 * @return int The new value of the option.
148 */
149 public function set_screen_option( $screen_option, $option, $value ) {
150 return (int) $value;
151 }
152
153 /**
154 * Manages the user input for the languages pages.
155 *
156 * @since 1.9
157 *
158 * @param string $action The action name.
159 * @return void
160 *
161 * @phpstan-param non-empty-string $action
162 * @phpstan-return never
163 */
164 public function handle_actions( string $action ): void {
165 switch ( $action ) {
166 case 'add':
167 check_admin_referer( 'add-lang', '_wpnonce_add-lang' );
168 $language = $this->model->add_language( $_POST );
169
170 if ( is_wp_error( $language ) ) {
171 pll_add_notice( $language );
172 } else {
173 pll_add_notice( new WP_Error( 'pll_languages_created', __( 'Language added.', 'polylang' ), 'success' ) );
174
175 if ( 'en_US' !== $language->locale && current_user_can( 'install_languages' ) ) {
176 // Attempts to install the language pack
177 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
178 if ( ! wp_download_language_pack( $language->locale ) ) {
179 pll_add_notice( new WP_Error( 'pll_download_mo', __( 'The language was created, but the WordPress language file was not downloaded. Please install it manually.', 'polylang' ), 'warning' ) );
180 }
181
182 // Force checking for themes and plugins translations updates
183 wp_clean_themes_cache();
184 wp_clean_plugins_cache();
185 }
186 }
187 break;
188
189 case 'delete':
190 check_admin_referer( 'delete-lang' );
191
192 if ( ! empty( $_GET['lang'] ) && $this->model->delete_language( (int) $_GET['lang'] ) ) {
193 pll_add_notice( new WP_Error( 'pll_languages_deleted', __( 'Language deleted.', 'polylang' ), 'success' ) );
194 }
195
196 break;
197
198 case 'update':
199 check_admin_referer( 'add-lang', '_wpnonce_add-lang' );
200 $errors = $this->model->update_language( $_POST );
201
202 if ( is_wp_error( $errors ) ) {
203 pll_add_notice( $errors );
204 } else {
205 pll_add_notice( new WP_Error( 'pll_languages_updated', __( 'Language updated.', 'polylang' ), 'success' ) );
206 }
207
208 break;
209
210 case 'default-lang':
211 check_admin_referer( 'default-lang' );
212
213 if ( $lang = $this->model->get_language( (int) $_GET['lang'] ) ) {
214 $this->model->update_default_lang( $lang->slug );
215 }
216
217 break;
218
219 case 'content-default-lang':
220 check_admin_referer( 'content-default-lang' );
221
222 $this->model->set_language_in_mass();
223
224 break;
225
226 case 'activate':
227 check_admin_referer( 'pll_activate' );
228 if ( isset( $_GET['module'] ) ) {
229 $module = sanitize_key( $_GET['module'] );
230 if ( isset( $this->modules[ $module ] ) ) {
231 $this->modules[ $module ]->activate();
232 }
233 }
234 break;
235
236 case 'deactivate':
237 check_admin_referer( 'pll_deactivate' );
238 if ( isset( $_GET['module'] ) ) {
239 $module = sanitize_key( $_GET['module'] );
240 if ( isset( $this->modules[ $module ] ) ) {
241 $this->modules[ $module ]->deactivate();
242 }
243 }
244 break;
245
246 default:
247 /**
248 * Fires when a non default action has been sent to Polylang settings
249 *
250 * @since 1.8
251 */
252 do_action( "mlang_action_$action" );
253 break;
254 }
255
256 self::redirect();
257 }
258
259 /**
260 * Displays the 3 tabs pages: languages, strings translations, settings
261 * Also manages user input for these pages
262 *
263 * @since 0.1
264 *
265 * @return void
266 */
267 public function languages_page() {
268 if ( ! isset( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
269 return; // Should not happen.
270 }
271
272 $active_tab = 'mlang' === $_GET['page'] ? 'lang' : substr( sanitize_key( $_GET['page'] ), 6 ); // phpcs:ignore WordPress.Security.NonceVerification
273
274 switch ( $active_tab ) {
275 case 'lang':
276 // Prepare the list table of languages
277 $list_table = new PLL_Table_Languages();
278 $list_table->prepare_items( $this->model->get_languages_list() );
279 break;
280
281 case 'strings':
282 $string_table = new PLL_Table_String( $this->model->languages );
283 $string_table->prepare_items();
284 break;
285 }
286
287 // Handle user input.
288 $action = isset( $_REQUEST['pll_action'] ) && is_string( $_REQUEST['pll_action'] ) ? sanitize_key( $_REQUEST['pll_action'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
289 if ( 'edit' === $action && ! empty( $_GET['lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
290 // phpcs:ignore WordPress.Security.NonceVerification, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
291 $edit_lang = $this->model->get_language( (int) $_GET['lang'] );
292 } elseif ( ! empty( $action ) ) {
293 $this->handle_actions( $action );
294 }
295
296 // Displays the page.
297 $modules = $this->modules;
298 include __DIR__ . '/view-languages.php';
299 }
300
301 /**
302 * Enqueues scripts and styles
303 *
304 * @return void
305 */
306 public function admin_enqueue_scripts() {
307 parent::admin_enqueue_scripts();
308
309 $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
310
311 wp_enqueue_script( 'pll_settings', plugins_url( '/js/build/settings' . $suffix . '.js', POLYLANG_ROOT_FILE ), array( 'jquery', 'wp-ajax-response', 'postbox', 'jquery-ui-selectmenu', 'wp-hooks' ), POLYLANG_VERSION, true );
312 wp_localize_script( 'pll_settings', 'pll_settings', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
313
314 wp_enqueue_style( 'pll_selectmenu', plugins_url( '/css/build/selectmenu' . $suffix . '.css', POLYLANG_ROOT_FILE ), array(), POLYLANG_VERSION );
315 }
316
317 /**
318 * Displays a notice when there are objects with no language assigned
319 *
320 * @since 1.8
321 *
322 * @return void
323 */
324 public function notice_objects_with_no_lang() {
325 if ( ! empty( $this->options['default_lang'] ) && $this->model->get_objects_with_no_lang( 1 ) ) {
326 printf(
327 '<div class="error"><p>%s <a href="%s">%s</a></p></div>',
328 esc_html__( 'There are posts, pages, categories or tags without language.', 'polylang' ),
329 esc_url( wp_nonce_url( '?page=mlang&pll_action=content-default-lang&noheader=true', 'content-default-lang' ) ),
330 esc_html__( 'You can set them all to the default language.', 'polylang' )
331 );
332 }
333 }
334
335 /**
336 * Redirects to language page ( current active tab )
337 * saves error messages in a transient for reuse in redirected page
338 *
339 * @since 1.5
340 *
341 * @param array $args query arguments to add to the url
342 * @return void
343 *
344 * @phpstan-return never
345 */
346 public static function redirect( array $args = array() ): void {
347 $errors = get_settings_errors( 'polylang' );
348 if ( ! empty( $errors ) ) {
349 set_transient( 'settings_errors', $errors, 30 );
350 $args['settings-updated'] = 1;
351 }
352 // Remove all known query args from the referer before redirecting.
353 $to_remove = array( 'lang', 'module', 'pll_action', 'noheader', '_wpnonce' );
354 wp_safe_redirect( add_query_arg( $args, remove_query_arg( $to_remove, wp_get_referer() ) ) );
355 exit;
356 }
357
358 /**
359 * Get the list of predefined languages.
360 *
361 * @since 2.3
362 *
363 * @return string[][] {
364 * An array of array of language properties.
365 *
366 * @type string[] {
367 * @type string $code ISO 639-1 language code.
368 * @type string $locale WordPress locale.
369 * @type string $name Native language name.
370 * @type string $dir Text direction: 'ltr' or 'rtl'.
371 * @type string $flag Flag code, generally the country code.
372 * @type string $w3c W3C locale.
373 * @type string $facebook Facebook locale.
374 * }
375 * }
376 */
377 public static function get_predefined_languages() {
378 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
379
380 $languages = include __DIR__ . '/languages.php';
381 $translations = wp_get_available_translations();
382
383 /*
384 * Keep only languages with existing WP language pack
385 * unless the transient has expired and we don't have an internet connection to refresh it.
386 */
387 if ( ! empty( $translations ) ) {
388 $translations['en_US'] = ''; // Languages packs don't include en_US.
389 $languages = array_intersect_key( $languages, $translations );
390 }
391
392 /**
393 * Filters the list of predefined languages.
394 *
395 * @see src/settings/languages.php the list of predefined languages.
396 *
397 * @since 1.7.10
398 * @since 2.3 The languages arrays use associative keys instead of numerical keys.
399 *
400 * @param array $languages
401 */
402 $languages = apply_filters( 'pll_predefined_languages', $languages );
403
404 // Keep only languages with all necessary information.
405 foreach ( $languages as $k => $lang ) {
406 if ( ! isset( $lang['code'], $lang['locale'], $lang['name'], $lang['dir'], $lang['flag'] ) ) {
407 unset( $languages[ $k ] );
408 }
409 }
410
411 return $languages;
412 }
413
414 /**
415 * Get the list of the default flags, sorted by alphabetical order.
416 *
417 * @since 3.9
418 *
419 * @return string[]
420 */
421 public static function get_default_flags() {
422 $flags = include __DIR__ . '/flags.php';
423
424 if ( ! class_exists( 'Collator' ) ) {
425 asort( $flags );
426 return $flags;
427 }
428
429 $co = Collator::create( get_user_locale() );
430 if ( $co ) {
431 $co->asort( $flags );
432 }
433
434 return $flags;
435 }
436 }
437