PluginProbe
Polylang / 3.4.2
Polylang v3.4.2
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.4.2, at modules/wizard/wizard.php

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