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

865 lines 26.2 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
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 $languages = $this->model->get_languages_list();
207
208 if ( count( $languages ) === 0 && ! 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 ( count( $languages ) > 0 && $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();
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 $existing_languages = $this->model->get_languages_list();
529
530 $all_languages = include POLYLANG_DIR . '/settings/languages.php';
531 $languages = isset( $_POST['languages'] ) && is_array( $_POST['languages'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['languages'] ) ) : false;
532 $saved_languages = array();
533
534 // If there is no language added or defined.
535 if ( empty( $languages ) && empty( $existing_languages ) ) {
536 // Stay on this step with an error.
537 wp_safe_redirect(
538 esc_url_raw(
539 add_query_arg(
540 array(
541 'step' => $this->step,
542 'activate_error' => 'i18n_no_language_added',
543 )
544 )
545 )
546 );
547 exit;
548 }
549
550 // Otherwise process the languages to add or skip the step if no language has been added.
551 if ( ! empty( $languages ) ) {
552 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
553 // Remove duplicate values.
554 $languages = array_unique( $languages );
555 // For each language add it in Polylang settings.
556 foreach ( $languages as $locale ) {
557 $saved_languages = $all_languages[ $locale ];
558
559 $saved_languages['slug'] = $saved_languages['code'];
560 $saved_languages['rtl'] = (int) ( 'rtl' === $saved_languages['dir'] );
561 $saved_languages['term_group'] = 0; // Default term_group.
562
563 $language_added = $this->model->add_language( $saved_languages );
564
565 if ( $language_added instanceof WP_Error && array_key_exists( 'pll_non_unique_slug', $language_added->errors ) ) {
566 // Get the slug from the locale : lowercase and dash instead of underscore.
567 $saved_languages['slug'] = strtolower( str_replace( '_', '-', $saved_languages['locale'] ) );
568 $language_added = $this->model->add_language( $saved_languages );
569 }
570
571 if ( $language_added instanceof WP_Error ) {
572 // Stay on this step with an error.
573 $error_keys = array_keys( $language_added->errors );
574 wp_safe_redirect(
575 esc_url_raw(
576 add_query_arg(
577 array(
578 'step' => $this->step,
579 'activate_error' => 'i18n_' . reset( $error_keys ),
580 )
581 )
582 )
583 );
584 exit;
585 }
586
587 if ( 'en_US' !== $locale && current_user_can( 'install_languages' ) ) {
588 wp_download_language_pack( $locale );
589 }
590 }
591 }
592 wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
593 exit;
594 }
595
596 /**
597 * Add the media step to the wizard.
598 *
599 * @since 2.7
600 *
601 * @param array $steps List of steps.
602 * @return array List of steps updated.
603 */
604 public function add_step_media( $steps ) {
605 $languages = $this->model->get_languages_list();
606
607 if ( $this->is_media_step_displayable( $languages ) ) {
608 $steps['media'] = array(
609 'name' => esc_html__( 'Media', 'polylang' ),
610 'view' => array( $this, 'display_step_media' ),
611 'handler' => array( $this, 'save_step_media' ),
612 'scripts' => array(),
613 'styles' => array(),
614 );
615 }
616 return $steps;
617 }
618
619 /**
620 * Display the media step form
621 *
622 * @since 2.7
623 *
624 * @return void
625 */
626 public function display_step_media() {
627 include __DIR__ . '/view-wizard-step-media.php';
628 }
629
630 /**
631 * Execute the media step
632 *
633 * @since 2.7
634 *
635 * @return void
636 */
637 public function save_step_media() {
638 check_admin_referer( 'pll-wizard', '_pll_nonce' );
639
640 $media_support = isset( $_POST['media_support'] ) ? sanitize_key( $_POST['media_support'] ) === 'yes' : false;
641
642 $this->options['media_support'] = $media_support;
643
644 update_option( 'polylang', $this->options );
645
646 wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
647 exit;
648 }
649
650 /**
651 * Add untranslated contents step to the wizard
652 *
653 * @since 2.7
654 *
655 * @param array $steps List of steps.
656 * @return array List of steps updated.
657 */
658 public function add_step_untranslated_contents( $steps ) {
659 if ( ! $this->model->get_languages_list() || $this->model->get_objects_with_no_lang( 1 ) ) {
660 // 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.
661 // 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().
662 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 );
663 wp_localize_script( 'pll_admin', 'pll_admin', array( 'dismiss_notice' => esc_html__( 'Dismiss this notice.', 'polylang' ) ) );
664 wp_enqueue_style( 'pll-wizard-selectmenu', plugins_url( '/css/build/selectmenu' . $this->get_suffix() . '.css', POLYLANG_ROOT_FILE ), array( 'dashicons', 'install', 'common' ), POLYLANG_VERSION );
665 $steps['untranslated-contents'] = array(
666 'name' => esc_html__( 'Content', 'polylang' ),
667 'view' => array( $this, 'display_step_untranslated_contents' ),
668 'handler' => array( $this, 'save_step_untranslated_contents' ),
669 'scripts' => array( 'pll_admin' ),
670 'styles' => array( 'pll-wizard-selectmenu' ),
671 );
672 }
673 return $steps;
674 }
675
676 /**
677 * Display the untranslated contents step form
678 *
679 * @since 2.7
680 *
681 * @return void
682 */
683 public function display_step_untranslated_contents() {
684 include __DIR__ . '/view-wizard-step-untranslated-contents.php';
685 }
686
687 /**
688 * Execute the untranslated contents step
689 *
690 * @since 2.7
691 *
692 * @return void
693 */
694 public function save_step_untranslated_contents() {
695 check_admin_referer( 'pll-wizard', '_pll_nonce' );
696
697 $lang = isset( $_POST['language'] ) ? sanitize_text_field( wp_unslash( $_POST['language'] ) ) : false;
698
699 if ( empty( $lang ) ) {
700 $lang = $this->options['default_lang'];
701 }
702
703 $language = $this->model->get_language( $lang );
704
705 while ( $nolang = $this->model->get_objects_with_no_lang( 1000 ) ) {
706 if ( ! empty( $nolang['posts'] ) ) {
707 $this->model->set_language_in_mass( 'post', $nolang['posts'], $language->slug );
708 }
709 if ( ! empty( $nolang['terms'] ) ) {
710 $this->model->set_language_in_mass( 'term', $nolang['terms'], $language->slug );
711 }
712 }
713
714 wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
715 exit;
716 }
717
718 /**
719 * Add home page step to the wizard
720 *
721 * @since 2.7
722 *
723 * @param array $steps List of steps.
724 * @return array List of steps updated.
725 */
726 public function add_step_home_page( $steps ) {
727 $languages = $this->model->get_languages_list();
728 $home_page_id = get_option( 'page_on_front' );
729
730 $translations = $this->model->post->get_translations( $home_page_id );
731
732 if ( $home_page_id > 0 && ( ! $languages || count( $languages ) === 1 || count( $translations ) !== count( $languages ) ) ) {
733 $steps['home-page'] = array(
734 'name' => esc_html__( 'Homepage', 'polylang' ),
735 'view' => array( $this, 'display_step_home_page' ),
736 'handler' => array( $this, 'save_step_home_page' ),
737 'scripts' => array(),
738 'styles' => array(),
739 );
740 }
741 return $steps;
742 }
743
744 /**
745 * Display the home page step form
746 *
747 * @since 2.7
748 *
749 * @return void
750 */
751 public function display_step_home_page() {
752 include __DIR__ . '/view-wizard-step-home-page.php';
753 }
754
755 /**
756 * Execute the home page step
757 *
758 * @since 2.7
759 *
760 * @return void
761 */
762 public function save_step_home_page() {
763 check_admin_referer( 'pll-wizard', '_pll_nonce' );
764
765 $languages = $this->model->get_languages_list();
766
767 $default_language = count( $languages ) > 0 ? $this->options['default_lang'] : null;
768 $home_page = isset( $_POST['home_page'] ) ? sanitize_key( $_POST['home_page'] ) : false;
769 $home_page_title = isset( $_POST['home_page_title'] ) ? sanitize_text_field( wp_unslash( $_POST['home_page_title'] ) ) : esc_html__( 'Homepage', 'polylang' );
770 $home_page_language = isset( $_POST['home_page_language'] ) ? sanitize_key( $_POST['home_page_language'] ) : false;
771
772 $untranslated_languages = isset( $_POST['untranslated_languages'] ) ? array_map( 'sanitize_key', $_POST['untranslated_languages'] ) : array();
773
774 call_user_func(
775 apply_filters( 'pll_wizard_create_home_page_translations', array( $this, 'create_home_page_translations' ) ),
776 $default_language,
777 $home_page,
778 $home_page_title,
779 $home_page_language,
780 $untranslated_languages
781 );
782
783 $this->model->clean_languages_cache();
784
785 wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
786 exit;
787 }
788
789 /**
790 * Create home page translations for each language defined.
791 *
792 * @since 2.7
793 *
794 * @param string $default_language Slug of the default language; null if no default language is defined.
795 * @param int $home_page Post ID of the home page if it's defined, false otherwise.
796 * @param string $home_page_title Home page title if it's defined, 'Homepage' otherwise.
797 * @param string $home_page_language Slug of the home page if it's defined, false otherwise.
798 * @param string[] $untranslated_languages Array of languages which needs to have a home page translated.
799 * @return void
800 */
801 public function create_home_page_translations( $default_language, $home_page, $home_page_title, $home_page_language, $untranslated_languages ) {
802 $translations = $this->model->post->get_translations( $home_page );
803
804 foreach ( $untranslated_languages as $language ) {
805 $language_properties = $this->model->get_language( $language );
806 $id = wp_insert_post(
807 array(
808 'post_title' => $home_page_title . ' - ' . $language_properties->name,
809 'post_type' => 'page',
810 'post_status' => 'publish',
811 )
812 );
813 $translations[ $language ] = $id;
814 pll_set_post_language( $id, $language );
815 }
816 pll_save_post_translations( $translations );
817 }
818
819 /**
820 * Add last step to the wizard
821 *
822 * @since 2.7
823 *
824 * @param array $steps List of steps.
825 * @return array List of steps updated.
826 */
827 public function add_step_last( $steps ) {
828 $steps['last'] = array(
829 'name' => esc_html__( 'Ready!', 'polylang' ),
830 'view' => array( $this, 'display_step_last' ),
831 'handler' => array( $this, 'save_step_last' ),
832 'scripts' => array(),
833 'styles' => array(),
834 );
835 return $steps;
836 }
837
838 /**
839 * Display the last step form
840 *
841 * @since 2.7
842 *
843 * @return void
844 */
845 public function display_step_last() {
846 // We ran the wizard once. So we can dismiss its notice.
847 PLL_Admin_Notices::dismiss( 'wizard' );
848 include __DIR__ . '/view-wizard-step-last.php';
849 }
850
851 /**
852 * Execute the last step
853 *
854 * @since 2.7
855 *
856 * @return void
857 */
858 public function save_step_last() {
859 check_admin_referer( 'pll-wizard', '_pll_nonce' );
860
861 wp_safe_redirect( esc_url_raw( $this->get_next_step_link() ) );
862 exit;
863 }
864 }
865