PluginProbe
Polylang / 3.7
Polylang v3.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
polylang / modules / wizard / wizard.php

wizard.php in Polylang 3.7, at modules/wizard/wizard.php

889 lines 27.1 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 use WP_Syntex\Polylang\Options\Options;
9
10 /**
11 * Main class for Polylang wizard.
12 *
13 * @since 2.7
14 */
15 class PLL_Wizard {
16 /**
17 * Reference to the model object
18 *
19 * @var PLL_Admin_Model
20 */
21 protected $model;
22
23 /**
24 * Reference to the Polylang options array.
25 *
26 * @var array
27 */
28 protected $options;
29
30 /**
31 * List of steps.
32 *
33 * @var array[] $steps {
34 * @type array {
35 * List of step properties.
36 *
37 * @type string $name I18n string which names the step.
38 * @type callable $view The callback function use to display the step content.
39 * @type callable $handler The callback function use to process the step after it is submitted.
40 * @type array $scripts List of scripts handle needed by the step.
41 * @type array $styles The list of styles handle needed by the step.
42 * }
43 * }
44 */
45 protected $steps = array();
46
47 /**
48 * The current step.
49 *
50 * @var string|null
51 */
52 protected $current_step;
53
54 /**
55 * List of WordPress CSS file handles.
56 *
57 * @var string[]
58 */
59 protected $styles = array();
60
61 /**
62 * Constructor
63 *
64 * @param object $polylang Reference to Polylang global object.
65 * @since 2.7
66 */
67 public function __construct( &$polylang ) {
68 $this->options = &$polylang->options;
69 $this->model = &$polylang->model;
70
71 // Display Wizard page before any other action to ensure displaying it outside the WordPress admin context.
72 // Hooked on admin_init with priority 40 to ensure PLL_Wizard_Pro is correctly initialized.
73 add_action( 'admin_init', array( $this, 'setup_wizard_page' ), 40 );
74 // Add Wizard submenu.
75 add_filter( 'pll_settings_tabs', array( $this, 'settings_tabs' ), 10, 1 );
76 // Add filter to select screens where to display the notice.
77 add_filter( 'pll_can_display_notice', array( $this, 'can_display_notice' ), 10, 2 );
78
79 // Default steps.
80 add_filter( 'pll_wizard_steps', array( $this, 'add_step_licenses' ), 100 );
81 add_filter( 'pll_wizard_steps', array( $this, 'add_step_languages' ), 200 );
82 add_filter( 'pll_wizard_steps', array( $this, 'add_step_media' ), 300 );
83 add_filter( 'pll_wizard_steps', array( $this, 'add_step_untranslated_contents' ), 400 );
84 add_filter( 'pll_wizard_steps', array( $this, 'add_step_home_page' ), 500 );
85 add_filter( 'pll_wizard_steps', array( $this, 'add_step_last' ), 999 );
86 }
87
88 /**
89 * Save an activation transient when Polylang is activating to redirect to the wizard
90 *
91 * @since 2.7
92 *
93 * @param bool $network_wide if activated for all sites in the network.
94 * @return void
95 */
96 public static function start_wizard( $network_wide ) {
97 $options = (array) get_option( Options::OPTION_NAME, array() );
98
99 if ( wp_doing_ajax() || $network_wide || ! empty( $options['version'] ) ) {
100 return;
101 }
102 set_transient( 'pll_activation_redirect', 1, 30 );
103 }
104
105 /**
106 * Redirect to the wizard depending on the context
107 *
108 * @since 2.7
109 *
110 * @return void
111 */
112 public function redirect_to_wizard() {
113 if ( get_transient( 'pll_activation_redirect' ) ) {
114 $do_redirect = true;
115 if ( ( isset( $_GET['page'] ) && 'mlang_wizard' === sanitize_key( $_GET['page'] ) ) || isset( $_GET['activate-multi'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
116 delete_transient( 'pll_activation_redirect' );
117 $do_redirect = false;
118 }
119
120 if ( $do_redirect ) {
121 wp_safe_redirect(
122 sanitize_url(
123 add_query_arg(
124 array(
125 'page' => 'mlang_wizard',
126 ),
127 admin_url( 'admin.php' )
128 )
129 )
130 );
131 exit;
132 }
133 }
134 }
135
136 /**
137 * Add an admin Polylang submenu to access the wizard
138 *
139 * @since 2.7
140 *
141 * @param string[] $tabs Submenus list.
142 * @return string[] Submenus list updated.
143 */
144 public function settings_tabs( $tabs ) {
145 $tabs['wizard'] = esc_html__( 'Setup', 'polylang' );
146 return $tabs;
147 }
148
149 /**
150 * Returns true if the media step is displayable, false otherwise.
151 *
152 * @since 2.7
153 *
154 * @param PLL_Language[] $languages List of language objects.
155 * @return bool
156 */
157 public function is_media_step_displayable( $languages ) {
158 $media = array();
159 // If there is no language or only one the media step is displayable.
160 if ( ! $languages || count( $languages ) < 2 ) {
161 return true;
162 }
163 foreach ( $languages as $language ) {
164 $media[ $language->slug ] = $this->model->count_posts(
165 $language,
166 array(
167 'post_type' => array( 'attachment' ),
168 'post_status' => 'inherit',
169 )
170 );
171 }
172 return count( array_filter( $media ) ) === 0;
173 }
174
175 /**
176 * Check if the licenses step is displayable
177 *
178 * @since 2.7
179 *
180 * @return bool
181 */
182 public function is_licenses_step_displayable() {
183 $licenses = apply_filters( 'pll_settings_licenses', array() );
184 return count( $licenses ) > 0;
185 }
186
187 /**
188 * Setup the wizard page
189 *
190 * @since 2.7
191 *
192 * @return void
193 */
194 public function setup_wizard_page() {
195
196 PLL_Admin_Notices::add_notice( 'wizard', $this->wizard_notice() );
197
198 $this->redirect_to_wizard();
199 if ( ! Polylang::is_wizard() ) {
200 return;
201 }
202 if ( ! current_user_can( 'manage_options' ) ) {
203 wp_die( esc_html__( 'Sorry, you are not allowed to manage options for this site.', 'polylang' ) );
204 }
205
206 // Enqueue scripts and styles especially for the wizard.
207 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
208
209 $this->steps = apply_filters( 'pll_wizard_steps', $this->steps );
210 $step = isset( $_GET['step'] ) ? sanitize_key( $_GET['step'] ) : false; // phpcs:ignore WordPress.Security.NonceVerification
211
212 $this->current_step = $step && array_key_exists( $step, $this->steps ) ? $step : current( array_keys( $this->steps ) );
213
214 $has_languages = $this->model->has_languages();
215
216 if ( ! $has_languages && ! in_array( $this->current_step, array( 'licenses', 'languages' ) ) ) {
217 wp_safe_redirect( sanitize_url( $this->get_step_link( 'languages' ) ) );
218 exit;
219 }
220
221 if ( $has_languages && $this->model->get_objects_with_no_lang( 1 ) && ! in_array( $this->current_step, array( 'licenses', 'languages', 'media', 'untranslated-contents' ) ) ) {
222 wp_safe_redirect( sanitize_url( $this->get_step_link( 'untranslated-contents' ) ) );
223 exit;
224 }
225
226 // Call the handler of the step for going to the next step.
227 // Be careful nonce verification with check_admin_referer must be done in each handler.
228 if ( ! empty( $_POST['save_step'] ) && isset( $this->steps[ $this->current_step ]['handler'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
229 call_user_func( $this->steps[ $this->current_step ]['handler'] );
230 }
231
232 $this->display_wizard_page();
233 // Ensure nothing is done after including the page.
234 exit;
235 }
236
237 /**
238 * Adds some admin screens where to display the wizard notice
239 *
240 * @since 2.7
241 *
242 * @param bool $can_display_notice Whether the notice can be displayed.
243 * @param string $notice The notice name.
244 * @return bool
245 */
246 public function can_display_notice( $can_display_notice, $notice ) {
247 if ( ! $can_display_notice && 'wizard' === $notice ) {
248 $screen = get_current_screen();
249 $can_display_notice = ! empty( $screen ) && in_array(
250 $screen->base,
251 array(
252 'edit',
253 'upload',
254 'options-general',
255 )
256 );
257 }
258 return $can_display_notice;
259 }
260
261 /**
262 * Return html code of the wizard notice
263 *
264 * @since 2.7
265 *
266 * @return string
267 */
268 public function wizard_notice() {
269 ob_start();
270 include __DIR__ . '/html-wizard-notice.php';
271 return ob_get_clean();
272 }
273
274 /**
275 * Display the wizard page
276 *
277 * @since 2.7
278 *
279 * @return void
280 */
281 public function display_wizard_page() {
282 set_current_screen( 'pll-wizard' );
283 do_action( 'admin_enqueue_scripts' );
284 $steps = $this->steps;
285 $current_step = $this->current_step;
286 $styles = $this->styles;
287 include __DIR__ . '/view-wizard-page.php';
288 }
289
290 /**
291 * Enqueue scripts and styles for the wizard
292 *
293 * @since 2.7
294 *
295 * @return void
296 */
297 public function enqueue_scripts() {
298 wp_enqueue_style( 'polylang_admin', plugins_url( '/css/build/admin' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array(), POLYLANG_VERSION );
299 wp_enqueue_style( 'pll-wizard', plugins_url( '/css/build/wizard' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array( 'dashicons', 'install', 'common', 'forms' ), POLYLANG_VERSION );
300
301 $this->styles = array( 'polylang_admin', 'pll-wizard' );
302 }
303
304 /**
305 * Get the suffix to enqueue non minified files in a Debug context
306 *
307 * @since 2.7
308 *
309 * @return string Empty when SCRIPT_DEBUG equal to true
310 * otherwise .min
311 */
312 public function get_suffix() {
313 return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
314 }
315
316 /**
317 * Get the URL for the step's screen.
318 *
319 * @since 2.7
320 *
321 * @param string $step slug (default: current step).
322 * @return string URL for the step if it exists.
323 * Empty string on failure.
324 */
325 public function get_step_link( $step = '' ) {
326 if ( ! $step ) {
327 $step = $this->current_step;
328 }
329
330 $keys = array_keys( $this->steps );
331
332 $step_index = array_search( $step, $keys, true );
333 if ( false === $step_index ) {
334 return '';
335 }
336
337 return add_query_arg( 'step', $keys[ $step_index ], remove_query_arg( 'activate_error' ) );
338 }
339
340 /**
341 * Get the URL for the next step's screen.
342 *
343 * @since 2.7
344 *
345 * @param string $step slug (default: current step).
346 * @return string URL for next step if a next step exists.
347 * Admin URL if it's the last step.
348 * Empty string on failure.
349 */
350 public function get_next_step_link( $step = '' ) {
351 if ( ! $step ) {
352 $step = $this->current_step;
353 }
354
355 $keys = array_keys( $this->steps );
356 if ( end( $keys ) === $step ) {
357 return admin_url();
358 }
359
360 $step_index = array_search( $step, $keys, true );
361 if ( false === $step_index ) {
362 return '';
363 }
364
365 return add_query_arg( 'step', $keys[ $step_index + 1 ], remove_query_arg( 'activate_error' ) );
366 }
367
368 /**
369 * Add licenses step to the wizard
370 *
371 * @since 2.7
372 *
373 * @param array $steps List of steps.
374 * @return array List of steps updated.
375 */
376 public function add_step_licenses( $steps ) {
377 // Add ajax action on deactivate button in licenses step.
378 add_action( 'wp_ajax_pll_deactivate_license', array( $this, 'deactivate_license' ) );
379
380 // Be careful `settings` script is enqueued here without dependency except jquery because only code useful for deactivate license button is needed.
381 // To be really loaded the script needs to be passed to the `$steps['licenses']['scripts']` array below with the same handle than in `wp_enqueue_script()`.
382 wp_enqueue_script( 'pll_settings', plugins_url( '/js/build/settings' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery' ), POLYLANG_VERSION, true );
383 wp_localize_script( 'pll_settings', 'pll_settings', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
384
385 if ( $this->is_licenses_step_displayable() ) {
386 $steps['licenses'] = array(
387 'name' => esc_html__( 'Licenses', 'polylang' ),
388 'view' => array( $this, 'display_step_licenses' ),
389 'handler' => array( $this, 'save_step_licenses' ),
390 'scripts' => array( 'pll_settings' ), // Polylang admin script used by deactivate license button.
391 'styles' => array(),
392 );
393 }
394 return $steps;
395 }
396
397 /**
398 * Display the languages step form
399 *
400 * @since 2.7
401 *
402 * @return void
403 */
404 public function display_step_licenses() {
405 include __DIR__ . '/view-wizard-step-licenses.php';
406 }
407
408 /**
409 * Execute the languages step
410 *
411 * @since 2.7
412 *
413 * @return void
414 */
415 public function save_step_licenses() {
416 check_admin_referer( 'pll-wizard', '_pll_nonce' );
417
418 $redirect = $this->get_next_step_link();
419 $licenses = apply_filters( 'pll_settings_licenses', array() );
420
421 foreach ( $licenses as $license ) {
422 if ( ! empty( $_POST['licenses'][ $license->id ] ) ) {
423 $updated_license = $license->activate_license( sanitize_key( $_POST['licenses'][ $license->id ] ) );
424 if ( ! empty( $updated_license->license_data ) && false === $updated_license->license_data->success ) {
425 // Stay on this step with an error.
426 $redirect = add_query_arg(
427 array(
428 'step' => $this->current_step,
429 'activate_error' => 'i18n_license_key_error',
430 )
431 );
432 }
433 }
434 }
435
436 wp_safe_redirect( sanitize_url( $redirect ) );
437 exit;
438 }
439
440 /**
441 * Ajax method to deactivate a license
442 *
443 * @since 2.7
444 *
445 * @return void
446 */
447 public function deactivate_license() {
448 check_ajax_referer( 'pll-wizard', '_pll_nonce' );
449
450 if ( ! current_user_can( 'manage_options' ) ) {
451 wp_die( -1 );
452 }
453
454 if ( ! isset( $_POST['id'] ) ) {
455 wp_die( 0 );
456 }
457
458 $id = substr( sanitize_text_field( wp_unslash( $_POST['id'] ) ), 11 );
459 $licenses = apply_filters( 'pll_settings_licenses', array() );
460 $license = $licenses[ $id ];
461 $license->deactivate_license();
462
463 wp_send_json(
464 array(
465 'id' => $id,
466 'html' => $license->get_form_field(),
467 )
468 );
469 }
470
471 /**
472 * Add languages step to the wizard
473 *
474 * @since 2.7
475 *
476 * @param array $steps List of steps.
477 * @return array List of steps updated.
478 */
479 public function add_step_languages( $steps ) {
480 wp_deregister_script( 'pll_settings' ); // Deregister after the licenses step enqueue to update jquery-ui-selectmenu dependency.
481 // The wp-ajax-response and postbox dependencies is useless in wizard steps especially postbox which triggers a javascript error otherwise.
482 // To be really loaded the script needs to be passed to the `$steps['languages']['scripts']` array below with the same handle than in `wp_enqueue_script()`.
483 wp_enqueue_script( 'pll_settings', plugins_url( '/js/build/settings' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
484 wp_localize_script( 'pll_settings', 'pll_settings', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
485 wp_register_script( 'pll-wizard-languages', plugins_url( '/js/build/languages-step' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery', 'jquery-ui-dialog' ), POLYLANG_VERSION, true );
486 wp_localize_script(
487 'pll-wizard-languages',
488 'pll_wizard_params',
489 array(
490 'i18n_no_language_selected' => esc_html__( 'You need to select a language to be added.', 'polylang' ),
491 'i18n_language_already_added' => esc_html__( 'You already added this language.', 'polylang' ),
492 'i18n_no_language_added' => esc_html__( 'You need to add at least one language.', 'polylang' ),
493 'i18n_add_language_needed' => esc_html__( 'You selected a language, however, to be able to continue, you need to add it.', 'polylang' ),
494 'i18n_pll_add_language' => esc_html__( 'Impossible to add the language.', 'polylang' ),
495 'i18n_pll_invalid_locale' => esc_html__( 'Enter a valid WordPress locale', 'polylang' ),
496 'i18n_pll_invalid_slug' => esc_html__( 'The language code contains invalid characters', 'polylang' ),
497 'i18n_pll_non_unique_slug' => esc_html__( 'The language code must be unique', 'polylang' ),
498 'i18n_pll_invalid_name' => esc_html__( 'The language must have a name', 'polylang' ),
499 'i18n_pll_invalid_flag' => esc_html__( 'The flag does not exist', 'polylang' ),
500 'i18n_dialog_title' => esc_html__( "A language wasn't added.", 'polylang' ),
501 'i18n_dialog_yes_button' => esc_html__( 'Yes', 'polylang' ),
502 'i18n_dialog_no_button' => esc_html__( 'No', 'polylang' ),
503 'i18n_dialog_ignore_button' => esc_html__( 'Ignore', 'polylang' ),
504 'i18n_remove_language_icon' => esc_html__( 'Remove this language', 'polylang' ),
505 )
506 );
507 wp_enqueue_script( 'pll-wizard-languages' );
508 wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/build/selectmenu' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array( 'dashicons', 'install', 'common', 'wp-jquery-ui-dialog' ), POLYLANG_VERSION );
509 $steps['languages'] = array(
510 'name' => esc_html__( 'Languages', 'polylang' ),
511 'view' => array( $this, 'display_step_languages' ),
512 'handler' => array( $this, 'save_step_languages' ),
513 'scripts' => array( 'pll-wizard-languages', 'pll_settings' ),
514 'styles' => array( 'pll-wizard-selectmenu' ),
515 );
516 return $steps;
517 }
518
519 /**
520 * Display the languages step form
521 *
522 * @since 2.7
523 *
524 * @return void
525 */
526 public function display_step_languages() {
527 $model = $this->model;
528 include __DIR__ . '/view-wizard-step-languages.php';
529 }
530
531 /**
532 * Execute the languages step
533 *
534 * @since 2.7
535 *
536 * @return void
537 */
538 public function save_step_languages() {
539 check_admin_referer( 'pll-wizard', '_pll_nonce' );
540
541 $all_languages = include POLYLANG_DIR . '/settings/languages.php';
542 $languages = isset( $_POST['languages'] ) && is_array( $_POST['languages'] ) ? array_map( 'sanitize_locale_name', $_POST['languages'] ) : false;
543 $saved_languages = array();
544
545 // If there is no language added or defined.
546 if ( empty( $languages ) && ! $this->model->has_languages() ) {
547 // Stay on this step with an error.
548 wp_safe_redirect(
549 sanitize_url(
550 add_query_arg(
551 array(
552 'step' => $this->current_step,
553 'activate_error' => 'i18n_no_language_added',
554 )
555 )
556 )
557 );
558 exit;
559 }
560
561 // Otherwise process the languages to add or skip the step if no language has been added.
562 if ( ! empty( $languages ) ) {
563 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
564 // Remove duplicate values.
565 $languages = array_unique( $languages );
566 // For each language add it in Polylang settings.
567 foreach ( $languages as $locale ) {
568 $saved_languages = $all_languages[ $locale ];
569
570 $saved_languages['slug'] = $saved_languages['code'];
571 $saved_languages['rtl'] = (int) ( 'rtl' === $saved_languages['dir'] );
572 $saved_languages['term_group'] = 0; // Default term_group.
573
574 $language_added = $this->model->add_language( $saved_languages );
575
576 if ( $language_added instanceof WP_Error && array_key_exists( 'pll_non_unique_slug', $language_added->errors ) ) {
577 // Get the slug from the locale : lowercase and dash instead of underscore.
578 $saved_languages['slug'] = strtolower( str_replace( '_', '-', $saved_languages['locale'] ) );
579 $language_added = $this->model->add_language( $saved_languages );
580 }
581
582 if ( $language_added instanceof WP_Error ) {
583 // Stay on this step with an error.
584 $error_keys = array_keys( $language_added->errors );
585 wp_safe_redirect(
586 sanitize_url(
587 add_query_arg(
588 array(
589 'step' => $this->current_step,
590 'activate_error' => 'i18n_' . reset( $error_keys ),
591 )
592 )
593 )
594 );
595 exit;
596 }
597
598 if ( 'en_US' !== $locale && current_user_can( 'install_languages' ) ) {
599 wp_download_language_pack( $locale );
600 }
601 }
602 }
603 wp_safe_redirect( sanitize_url( $this->get_next_step_link() ) );
604 exit;
605 }
606
607 /**
608 * Add the media step to the wizard.
609 *
610 * @since 2.7
611 *
612 * @param array $steps List of steps.
613 * @return array List of steps updated.
614 */
615 public function add_step_media( $steps ) {
616 $languages = $this->model->get_languages_list();
617
618 if ( $this->is_media_step_displayable( $languages ) ) {
619 $steps['media'] = array(
620 'name' => esc_html__( 'Media', 'polylang' ),
621 'view' => array( $this, 'display_step_media' ),
622 'handler' => array( $this, 'save_step_media' ),
623 'scripts' => array(),
624 'styles' => array(),
625 );
626 }
627 return $steps;
628 }
629
630 /**
631 * Display the media step form
632 *
633 * @since 2.7
634 *
635 * @return void
636 */
637 public function display_step_media() {
638 $options = $this->options;
639 include __DIR__ . '/view-wizard-step-media.php';
640 }
641
642 /**
643 * Execute the media step
644 *
645 * @since 2.7
646 *
647 * @return void
648 */
649 public function save_step_media() {
650 check_admin_referer( 'pll-wizard', '_pll_nonce' );
651
652 $media_support = isset( $_POST['media_support'] ) ? sanitize_key( $_POST['media_support'] ) === 'yes' : false;
653
654 $this->options['media_support'] = $media_support;
655
656 update_option( 'polylang', $this->options );
657
658 wp_safe_redirect( sanitize_url( $this->get_next_step_link() ) );
659 exit;
660 }
661
662 /**
663 * Add untranslated contents step to the wizard
664 *
665 * @since 2.7
666 *
667 * @param array $steps List of steps.
668 * @return array List of steps updated.
669 */
670 public function add_step_untranslated_contents( $steps ) {
671 if ( ! $this->model->has_languages() || $this->model->get_objects_with_no_lang( 1 ) ) {
672 // Even if `pll_settings` is already enqueued with the same dependencies by the languages step, it is interesting to keep that it's also useful for the untranslated-contents step.
673 // To be really loaded the script needs to be passed to the `$steps['untranslated-contents']['scripts']` array below with the same handle than in `wp_enqueue_script()`.
674 wp_enqueue_script( 'pll_settings', plugins_url( '/js/build/settings' . $this->get_suffix() . '.js', POLYLANG_ROOT_FILE ), array( 'jquery', 'jquery-ui-selectmenu' ), POLYLANG_VERSION, true );
675 wp_localize_script( 'pll_settings', 'pll_settings', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
676 wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/build/selectmenu' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array( 'dashicons', 'install', 'common' ), POLYLANG_VERSION );
677 $steps['untranslated-contents'] = array(
678 'name' => esc_html__( 'Content', 'polylang' ),
679 'view' => array( $this, 'display_step_untranslated_contents' ),
680 'handler' => array( $this, 'save_step_untranslated_contents' ),
681 'scripts' => array( 'pll_settings' ),
682 'styles' => array( 'pll-wizard-selectmenu' ),
683 );
684 }
685 return $steps;
686 }
687
688 /**
689 * Display the untranslated contents step form
690 *
691 * @since 2.7
692 *
693 * @return void
694 */
695 public function display_step_untranslated_contents() {
696 $model = $this->model;
697 include __DIR__ . '/view-wizard-step-untranslated-contents.php';
698 }
699
700 /**
701 * Execute the untranslated contents step
702 *
703 * @since 2.7
704 *
705 * @return void
706 */
707 public function save_step_untranslated_contents() {
708 check_admin_referer( 'pll-wizard', '_pll_nonce' );
709
710 $lang = ! empty( $_POST['language'] ) && is_string( $_POST['language'] ) ? sanitize_locale_name( $_POST['language'] ) : false;
711
712 if ( empty( $lang ) ) {
713 $lang = $this->options['default_lang'];
714 }
715
716 $language = $this->model->get_language( $lang );
717
718 if ( $language instanceof PLL_Language ) {
719 $this->model->set_language_in_mass( $language );
720 }
721
722 wp_safe_redirect( sanitize_url( $this->get_next_step_link() ) );
723 exit;
724 }
725
726 /**
727 * Add home page step to the wizard
728 *
729 * @since 2.7
730 *
731 * @param array $steps List of steps.
732 * @return array List of steps updated.
733 */
734 public function add_step_home_page( $steps ) {
735 $languages = $this->model->get_languages_list();
736 $home_page_id = get_option( 'page_on_front' );
737
738 $translations = $this->model->post->get_translations( $home_page_id );
739
740 if ( $home_page_id > 0 && ( ! $languages || count( $languages ) === 1 || count( $translations ) !== count( $languages ) ) ) {
741 $steps['home-page'] = array(
742 'name' => esc_html__( 'Homepage', 'polylang' ),
743 'view' => array( $this, 'display_step_home_page' ),
744 'handler' => array( $this, 'save_step_home_page' ),
745 'scripts' => array(),
746 'styles' => array(),
747 );
748 }
749 return $steps;
750 }
751
752 /**
753 * Display the home page step form
754 *
755 * @since 2.7
756 *
757 * @return void
758 */
759 public function display_step_home_page() {
760 $model = $this->model;
761 $languages = $model->languages->get_list();
762 $home_page_id = get_option( 'page_on_front' );
763 $home_page_id = is_numeric( $home_page_id ) ? (int) $home_page_id : 0;
764 $translations = $model->post->get_translations( $home_page_id );
765 $home_page = $home_page_id > 0 ? get_post( $home_page_id ) : null;
766 $home_page_language = $model->post->get_language( $home_page_id );
767 $untranslated_languages = array();
768
769 if ( empty( $home_page ) ) {
770 return;
771 }
772
773 foreach ( $languages as $language ) {
774 if ( ! $model->post->get( $home_page_id, $language ) ) {
775 $untranslated_languages[] = $language;
776 }
777 }
778 include __DIR__ . '/view-wizard-step-home-page.php';
779 }
780
781 /**
782 * Execute the home page step
783 *
784 * @since 2.7
785 *
786 * @return void
787 */
788 public function save_step_home_page() {
789 check_admin_referer( 'pll-wizard', '_pll_nonce' );
790
791 $default_language = $this->model->has_languages() ? $this->options['default_lang'] : null;
792 $home_page = isset( $_POST['home_page'] ) ? sanitize_key( $_POST['home_page'] ) : false;
793 $home_page_title = isset( $_POST['home_page_title'] ) ? sanitize_text_field( wp_unslash( $_POST['home_page_title'] ) ) : esc_html__( 'Homepage', 'polylang' );
794 $home_page_language = isset( $_POST['home_page_language'] ) ? sanitize_key( $_POST['home_page_language'] ) : false;
795
796 $untranslated_languages = isset( $_POST['untranslated_languages'] ) ? array_map( 'sanitize_key', $_POST['untranslated_languages'] ) : array();
797
798 call_user_func(
799 apply_filters( 'pll_wizard_create_home_page_translations', array( $this, 'create_home_page_translations' ) ),
800 $default_language,
801 $home_page,
802 $home_page_title,
803 $home_page_language,
804 $untranslated_languages
805 );
806
807 $this->model->clean_languages_cache();
808
809 wp_safe_redirect( sanitize_url( $this->get_next_step_link() ) );
810 exit;
811 }
812
813 /**
814 * Create home page translations for each language defined.
815 *
816 * @since 2.7
817 *
818 * @param string $default_language Slug of the default language; null if no default language is defined.
819 * @param int $home_page Post ID of the home page if it's defined, false otherwise.
820 * @param string $home_page_title Home page title if it's defined, 'Homepage' otherwise.
821 * @param string $home_page_language Slug of the home page if it's defined, false otherwise.
822 * @param string[] $untranslated_languages Array of languages which needs to have a home page translated.
823 * @return void
824 */
825 public function create_home_page_translations( $default_language, $home_page, $home_page_title, $home_page_language, $untranslated_languages ) {
826 $translations = $this->model->post->get_translations( $home_page );
827
828 foreach ( $untranslated_languages as $language ) {
829 $language_properties = $this->model->get_language( $language );
830 $id = wp_insert_post(
831 array(
832 'post_title' => $home_page_title . ' - ' . $language_properties->name,
833 'post_type' => 'page',
834 'post_status' => 'publish',
835 )
836 );
837 $translations[ $language ] = $id;
838 pll_set_post_language( $id, $language );
839 }
840 pll_save_post_translations( $translations );
841 }
842
843 /**
844 * Add last step to the wizard
845 *
846 * @since 2.7
847 *
848 * @param array $steps List of steps.
849 * @return array List of steps updated.
850 */
851 public function add_step_last( $steps ) {
852 $steps['last'] = array(
853 'name' => esc_html__( 'Ready!', 'polylang' ),
854 'view' => array( $this, 'display_step_last' ),
855 'handler' => array( $this, 'save_step_last' ),
856 'scripts' => array(),
857 'styles' => array(),
858 );
859 return $steps;
860 }
861
862 /**
863 * Display the last step form
864 *
865 * @since 2.7
866 *
867 * @return void
868 */
869 public function display_step_last() {
870 // We ran the wizard once. So we can dismiss its notice.
871 PLL_Admin_Notices::dismiss( 'wizard' );
872 include __DIR__ . '/view-wizard-step-last.php';
873 }
874
875 /**
876 * Execute the last step
877 *
878 * @since 2.7
879 *
880 * @return void
881 */
882 public function save_step_last() {
883 check_admin_referer( 'pll-wizard', '_pll_nonce' );
884
885 wp_safe_redirect( sanitize_url( $this->get_next_step_link() ) );
886 exit;
887 }
888 }
889