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 / modules / wizard / wizard.php

wizard.php in Polylang trunk, at src/modules/wizard/wizard.php

890 lines 26.8 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_Model
20 */
21 protected $model;
22
23 /**
24 * Reference to the Polylang options array.
25 *
26 * @var Options
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 PLL_Admin_Base $polylang Reference to Polylang global object.
65 * @since 2.7
66 */
67 public function __construct( PLL_Admin_Base &$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 pll_enqueue_style( 'admin' );
299 pll_enqueue_style( 'wizard', array( 'dashicons', 'install', 'common', 'forms' ) );
300
301 $this->styles = array( 'pll-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
486 pll_enqueue_script( 'wizard-languages', array( 'jquery', 'jquery-ui-dialog' ), array( 'in_footer' => true ) );
487 wp_localize_script(
488 'pll-wizard-languages',
489 'pll_wizard_params',
490 array(
491 'i18n_no_language_selected' => esc_html__( 'You need to select a language to be added.', 'polylang' ),
492 'i18n_language_already_added' => esc_html__( 'You already added this language.', 'polylang' ),
493 'i18n_no_language_added' => esc_html__( 'You need to add at least one language.', 'polylang' ),
494 'i18n_add_language_needed' => esc_html__( 'You selected a language, however, to be able to continue, you need to add it.', 'polylang' ),
495 'i18n_pll_add_language' => esc_html__( 'Impossible to add the language.', 'polylang' ),
496 'i18n_pll_invalid_locale' => esc_html__( 'Enter a valid WordPress locale', 'polylang' ),
497 'i18n_pll_invalid_slug' => esc_html__( 'The language code contains invalid characters', 'polylang' ),
498 'i18n_pll_non_unique_slug' => esc_html__( 'The language code must be unique', 'polylang' ),
499 'i18n_pll_invalid_name' => esc_html__( 'The language must have a name', 'polylang' ),
500 'i18n_pll_invalid_flag' => esc_html__( 'The flag does not exist', 'polylang' ),
501 'i18n_dialog_title' => esc_html__( "A language wasn't added.", 'polylang' ),
502 'i18n_dialog_yes_button' => esc_html__( 'Yes', 'polylang' ),
503 'i18n_dialog_no_button' => esc_html__( 'No', 'polylang' ),
504 'i18n_dialog_ignore_button' => esc_html__( 'Ignore', 'polylang' ),
505 'i18n_remove_language_icon' => esc_html__( 'Remove this language', 'polylang' ),
506 )
507 );
508
509 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 );
510 $steps['languages'] = array(
511 'name' => esc_html__( 'Languages', 'polylang' ),
512 'view' => array( $this, 'display_step_languages' ),
513 'handler' => array( $this, 'save_step_languages' ),
514 'scripts' => array( 'pll-wizard-languages', 'pll_settings' ),
515 'styles' => array( 'pll-wizard-selectmenu' ),
516 );
517 return $steps;
518 }
519
520 /**
521 * Display the languages step form
522 *
523 * @since 2.7
524 *
525 * @return void
526 */
527 public function display_step_languages() {
528 $model = $this->model;
529 include __DIR__ . '/view-wizard-step-languages.php';
530 }
531
532 /**
533 * Execute the languages step
534 *
535 * @since 2.7
536 *
537 * @return void
538 */
539 public function save_step_languages() {
540 check_admin_referer( 'pll-wizard', '_pll_nonce' );
541
542 $all_languages = include POLYLANG_DIR . '/src/settings/languages.php';
543 $languages = isset( $_POST['languages'] ) && is_array( $_POST['languages'] ) ? array_map( 'sanitize_locale_name', $_POST['languages'] ) : false;
544 $saved_languages = array();
545
546 // If there is no language added or defined.
547 if ( empty( $languages ) && ! $this->model->has_languages() ) {
548 // Stay on this step with an error.
549 wp_safe_redirect(
550 sanitize_url(
551 add_query_arg(
552 array(
553 'step' => $this->current_step,
554 'activate_error' => 'i18n_no_language_added',
555 )
556 )
557 )
558 );
559 exit;
560 }
561
562 // Otherwise process the languages to add or skip the step if no language has been added.
563 if ( ! empty( $languages ) ) {
564 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
565 // Remove duplicate values.
566 $languages = array_unique( $languages );
567 // For each language add it in Polylang settings.
568 foreach ( $languages as $locale ) {
569 $saved_languages = $all_languages[ $locale ];
570
571 $saved_languages['slug'] = $saved_languages['code'];
572 $saved_languages['rtl'] = (int) ( 'rtl' === $saved_languages['dir'] );
573 $saved_languages['term_group'] = 0; // Default term_group.
574
575 $language_added = $this->model->add_language( $saved_languages );
576
577 if ( $language_added instanceof WP_Error && array_key_exists( 'pll_non_unique_slug', $language_added->errors ) ) {
578 // Get the slug from the locale : lowercase and dash instead of underscore.
579 $saved_languages['slug'] = strtolower( str_replace( '_', '-', $saved_languages['locale'] ) );
580 $language_added = $this->model->add_language( $saved_languages );
581 }
582
583 if ( $language_added instanceof WP_Error ) {
584 // Stay on this step with an error.
585 $error_keys = array_keys( $language_added->errors );
586 wp_safe_redirect(
587 sanitize_url(
588 add_query_arg(
589 array(
590 'step' => $this->current_step,
591 'activate_error' => 'i18n_' . reset( $error_keys ),
592 )
593 )
594 )
595 );
596 exit;
597 }
598
599 if ( 'en_US' !== $locale && current_user_can( 'install_languages' ) ) {
600 wp_download_language_pack( $locale );
601 }
602 }
603 }
604 wp_safe_redirect( sanitize_url( $this->get_next_step_link() ) );
605 exit;
606 }
607
608 /**
609 * Add the media step to the wizard.
610 *
611 * @since 2.7
612 *
613 * @param array $steps List of steps.
614 * @return array List of steps updated.
615 */
616 public function add_step_media( $steps ) {
617 $languages = $this->model->get_languages_list();
618
619 if ( $this->is_media_step_displayable( $languages ) ) {
620 $steps['media'] = array(
621 'name' => esc_html__( 'Media', 'polylang' ),
622 'view' => array( $this, 'display_step_media' ),
623 'handler' => array( $this, 'save_step_media' ),
624 'scripts' => array(),
625 'styles' => array(),
626 );
627 }
628 return $steps;
629 }
630
631 /**
632 * Display the media step form
633 *
634 * @since 2.7
635 *
636 * @return void
637 */
638 public function display_step_media() {
639 $options = $this->options;
640 include __DIR__ . '/view-wizard-step-media.php';
641 }
642
643 /**
644 * Execute the media step
645 *
646 * @since 2.7
647 *
648 * @return void
649 */
650 public function save_step_media() {
651 check_admin_referer( 'pll-wizard', '_pll_nonce' );
652
653 $media_support = isset( $_POST['media_support'] ) ? sanitize_key( $_POST['media_support'] ) === 'yes' : false;
654
655 $this->options['media_support'] = $media_support;
656
657 update_option( 'polylang', $this->options );
658
659 wp_safe_redirect( sanitize_url( $this->get_next_step_link() ) );
660 exit;
661 }
662
663 /**
664 * Add untranslated contents step to the wizard
665 *
666 * @since 2.7
667 *
668 * @param array $steps List of steps.
669 * @return array List of steps updated.
670 */
671 public function add_step_untranslated_contents( $steps ) {
672 if ( ! $this->model->has_languages() || $this->model->get_objects_with_no_lang( 1 ) ) {
673 // 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.
674 // 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()`.
675 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 );
676 wp_localize_script( 'pll_settings', 'pll_settings', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
677 wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/build/selectmenu' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array( 'dashicons', 'install', 'common' ), POLYLANG_VERSION );
678 $steps['untranslated-contents'] = array(
679 'name' => esc_html__( 'Content', 'polylang' ),
680 'view' => array( $this, 'display_step_untranslated_contents' ),
681 'handler' => array( $this, 'save_step_untranslated_contents' ),
682 'scripts' => array( 'pll_settings' ),
683 'styles' => array( 'pll-wizard-selectmenu' ),
684 );
685 }
686 return $steps;
687 }
688
689 /**
690 * Display the untranslated contents step form
691 *
692 * @since 2.7
693 *
694 * @return void
695 */
696 public function display_step_untranslated_contents() {
697 $model = $this->model;
698 include __DIR__ . '/view-wizard-step-untranslated-contents.php';
699 }
700
701 /**
702 * Execute the untranslated contents step
703 *
704 * @since 2.7
705 *
706 * @return void
707 */
708 public function save_step_untranslated_contents() {
709 check_admin_referer( 'pll-wizard', '_pll_nonce' );
710
711 $lang = ! empty( $_POST['language'] ) && is_string( $_POST['language'] ) ? sanitize_locale_name( $_POST['language'] ) : false;
712
713 if ( empty( $lang ) ) {
714 $lang = $this->options['default_lang'];
715 }
716
717 $language = $this->model->get_language( $lang );
718
719 if ( $language instanceof PLL_Language ) {
720 $this->model->set_language_in_mass( $language );
721 }
722
723 wp_safe_redirect( sanitize_url( $this->get_next_step_link() ) );
724 exit;
725 }
726
727 /**
728 * Add home page step to the wizard
729 *
730 * @since 2.7
731 *
732 * @param array $steps List of steps.
733 * @return array List of steps updated.
734 */
735 public function add_step_home_page( $steps ) {
736 $languages = $this->model->get_languages_list();
737 $home_page_id = get_option( 'page_on_front' );
738
739 $translations = $this->model->post->get_translations( $home_page_id );
740
741 if ( $home_page_id > 0 && ( ! $languages || count( $languages ) === 1 || count( $translations ) !== count( $languages ) ) ) {
742 $steps['home-page'] = array(
743 'name' => esc_html__( 'Homepage', 'polylang' ),
744 'view' => array( $this, 'display_step_home_page' ),
745 'handler' => array( $this, 'save_step_home_page' ),
746 'scripts' => array(),
747 'styles' => array(),
748 );
749 }
750 return $steps;
751 }
752
753 /**
754 * Display the home page step form
755 *
756 * @since 2.7
757 *
758 * @return void
759 */
760 public function display_step_home_page() {
761 $model = $this->model;
762 $languages = $model->languages->get_list();
763 $home_page_id = get_option( 'page_on_front' );
764 $home_page_id = is_numeric( $home_page_id ) ? (int) $home_page_id : 0;
765 $translations = $model->post->get_translations( $home_page_id );
766 $home_page = $home_page_id > 0 ? get_post( $home_page_id ) : null;
767 $home_page_language = $model->post->get_language( $home_page_id );
768 $untranslated_languages = array();
769
770 if ( empty( $home_page ) ) {
771 return;
772 }
773
774 foreach ( $languages as $language ) {
775 if ( ! $model->post->get( $home_page_id, $language ) ) {
776 $untranslated_languages[] = $language;
777 }
778 }
779 include __DIR__ . '/view-wizard-step-home-page.php';
780 }
781
782 /**
783 * Execute the home page step
784 *
785 * @since 2.7
786 *
787 * @return void
788 */
789 public function save_step_home_page() {
790 check_admin_referer( 'pll-wizard', '_pll_nonce' );
791
792 $default_language = $this->model->has_languages() ? $this->options['default_lang'] : null;
793 $home_page = isset( $_POST['home_page'] ) ? sanitize_key( $_POST['home_page'] ) : false;
794 $home_page_title = isset( $_POST['home_page_title'] ) ? sanitize_text_field( wp_unslash( $_POST['home_page_title'] ) ) : esc_html__( 'Homepage', 'polylang' );
795 $home_page_language = isset( $_POST['home_page_language'] ) ? sanitize_key( $_POST['home_page_language'] ) : false;
796
797 $untranslated_languages = isset( $_POST['untranslated_languages'] ) ? array_map( 'sanitize_key', $_POST['untranslated_languages'] ) : array();
798
799 call_user_func(
800 apply_filters( 'pll_wizard_create_home_page_translations', array( $this, 'create_home_page_translations' ) ),
801 $default_language,
802 $home_page,
803 $home_page_title,
804 $home_page_language,
805 $untranslated_languages
806 );
807
808 $this->model->clean_languages_cache();
809
810 wp_safe_redirect( sanitize_url( $this->get_next_step_link() ) );
811 exit;
812 }
813
814 /**
815 * Create home page translations for each language defined.
816 *
817 * @since 2.7
818 *
819 * @param string $default_language Slug of the default language; null if no default language is defined.
820 * @param int $home_page Post ID of the home page if it's defined, false otherwise.
821 * @param string $home_page_title Home page title if it's defined, 'Homepage' otherwise.
822 * @param string $home_page_language Slug of the home page if it's defined, false otherwise.
823 * @param string[] $untranslated_languages Array of languages which needs to have a home page translated.
824 * @return void
825 */
826 public function create_home_page_translations( $default_language, $home_page, $home_page_title, $home_page_language, $untranslated_languages ) {
827 $translations = $this->model->post->get_translations( $home_page );
828
829 foreach ( $untranslated_languages as $language ) {
830 $language_properties = $this->model->get_language( $language );
831 $id = wp_insert_post(
832 array(
833 'post_title' => $home_page_title . ' - ' . $language_properties->name,
834 'post_type' => 'page',
835 'post_status' => 'publish',
836 )
837 );
838 $translations[ $language ] = $id;
839 pll_set_post_language( $id, $language );
840 }
841 pll_save_post_translations( $translations );
842 }
843
844 /**
845 * Add last step to the wizard
846 *
847 * @since 2.7
848 *
849 * @param array $steps List of steps.
850 * @return array List of steps updated.
851 */
852 public function add_step_last( $steps ) {
853 $steps['last'] = array(
854 'name' => esc_html__( 'Ready!', 'polylang' ),
855 'view' => array( $this, 'display_step_last' ),
856 'handler' => array( $this, 'save_step_last' ),
857 'scripts' => array(),
858 'styles' => array(),
859 );
860 return $steps;
861 }
862
863 /**
864 * Display the last step form
865 *
866 * @since 2.7
867 *
868 * @return void
869 */
870 public function display_step_last() {
871 // We ran the wizard once. So we can dismiss its notice.
872 PLL_Admin_Notices::dismiss( 'wizard' );
873 include __DIR__ . '/view-wizard-step-last.php';
874 }
875
876 /**
877 * Execute the last step
878 *
879 * @since 2.7
880 *
881 * @return void
882 */
883 public function save_step_last() {
884 check_admin_referer( 'pll-wizard', '_pll_nonce' );
885
886 wp_safe_redirect( sanitize_url( $this->get_next_step_link() ) );
887 exit;
888 }
889 }
890