admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( self::NONCE ), 'reportUrl' => self::url( array( 'step' => 'report' ) ), 'i18n' => array( 'startError' => __( 'The import could not start. Reload the page and try again.', 'prodtabs' ), 'error' => __( 'The import stopped. Reload the page: it will resume where it stopped.', 'prodtabs' ), 'done' => __( 'Import finished.', 'prodtabs' ), 'none' => __( 'Select at least one product, or choose to import all the products.', 'prodtabs' ), ), ) ); } /* Screen --------------------------------------------------------------------------------------------------- */ /** Screen content. */ public static function render(): void { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'Sorry, you are not allowed to import tabs.', 'prodtabs' ) ); } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- navigation between read-only steps. $step = isset( $_GET['step'] ) ? sanitize_key( wp_unslash( $_GET['step'] ) ) : 'analysis'; $state = Import_Yikes::state(); if ( 'running' === ( $state['status'] ?? '' ) && 'run' !== $step ) { $step = 'resume'; } $steps = array( 'analysis' => __( 'Analysis', 'prodtabs' ), 'preview' => __( 'Preview', 'prodtabs' ), 'run' => _x( 'Import', 'noun', 'prodtabs' ), 'report' => __( 'Report', 'prodtabs' ), ); $current = 'resume' === $step ? 'run' : $step; ?>
' . esc_html__( 'Go to the shared tabs', 'prodtabs' ) . '
'; return; } $warnings = self::warnings( $a ); ?>' . esc_html( implode( ' ', $lines ) ) . '
'; } /** * Warnings of the analysis, as sentences. * * @param array $a Analysis. * @return string[] */ private static function warnings( array $a ): array { $out = array(); if ( $a['has_tabs'] ) { /* translators: %d: number of products. */ $out[] = sprintf( _n( '%d product already has ProdTabs tabs made by hand: it is skipped, with its translations, unless you choose to import it again (its tabs are then kept aside and put back if you undo the import).', '%d products already have ProdTabs tabs made by hand: they are skipped, with their translations, unless you choose to import them again (their tabs are then kept aside and put back if you undo the import).', $a['has_tabs'], 'prodtabs' ), $a['has_tabs'] ); } if ( $a['duplicate'] ) { /* translators: %d: number of tabs. */ $out[] = sprintf( _n( '%d tab was never shown by YIKES because another tab of the product has the same title: it is imported hidden, so that you can check it and show it.', '%d tabs were never shown by YIKES because another tab of the product has the same title: they are imported hidden, so that you can check them and show them.', $a['duplicate'], 'prodtabs' ), $a['duplicate'] ); } if ( $a['untitled'] ) { /* translators: %d: number of tabs. */ $out[] = sprintf( _n( '%d tab has no title, so YIKES never showed it: it is imported hidden.', '%d tabs have no title, so YIKES never showed them: they are imported hidden.', $a['untitled'], 'prodtabs' ), $a['untitled'] ); } if ( $a['replaces_native'] ) { /* translators: %d: number of tabs. */ $out[] = sprintf( _n( '%d tab has the title of a WooCommerce tab (Description, Reviews) and replaced it: it is imported, and the WooCommerce tab is hidden on that product.', '%d tabs have the title of a WooCommerce tab (Description, Reviews) and replaced it: they are imported, and the WooCommerce tab is hidden on those products.', $a['replaces_native'], 'prodtabs' ), $a['replaces_native'] ); } if ( $a['differs'] ) { /* translators: %d: number of tabs. */ $out[] = sprintf( _n( '%d tab is linked to a saved tab but its text differs: it is imported as a customized copy of the shared tab.', '%d tabs are linked to a saved tab but their text differs: they are imported as customized copies of the shared tab.', $a['differs'], 'prodtabs' ), $a['differs'] ); } if ( $a['looks_saved'] ) { /* translators: %d: number of tabs. */ $out[] = sprintf( _n( '%d tab has the same text as a saved tab without being linked to it: it is imported as a customized copy, with a button to go back to the shared version.', '%d tabs have the same text as a saved tab without being linked to it: they are imported as customized copies, with a button to go back to the shared version.', $a['looks_saved'], 'prodtabs' ), $a['looks_saved'] ); } if ( $a['unreadable'] ) { /* translators: %s: product ids. */ $out[] = sprintf( __( 'Tabs that cannot be read (damaged data), products %s: they are left out.', 'prodtabs' ), implode( ', ', array_slice( $a['unreadable'], 0, 20 ) ) ); } if ( $a['repaired'] ) { /* translators: %d: number of products. */ $out[] = sprintf( _n( '%d product has tabs edited outside WordPress (spreadsheet): they were repaired for the import, check them.', '%d products have tabs edited outside WordPress (spreadsheet): they were repaired for the import, check them.', count( $a['repaired'] ), 'prodtabs' ), count( $a['repaired'] ) ); } if ( $a['shortcodes'] ) { /* translators: %s: shortcode names. */ $out[] = sprintf( __( 'Shortcodes of plugins that are not active: %s. They will show as plain text until their plugin is active.', 'prodtabs' ), '[' . implode( '], [', array_keys( $a['shortcodes'] ) ) . ']' ); } if ( $a['groups'] && $a['groups'] < $a['products'] ) { /* translators: %d: number of groups. */ $out[] = sprintf( _n( 'Products and their translations are imported together (%d group): one list of tabs for all their languages, with the texts of each language matched by position.', 'Products and their translations are imported together (%d groups): one list of tabs for all their languages, with the texts of each language matched by position.', $a['groups'], 'prodtabs' ), $a['groups'] ); } if ( $a['missing_translation'] ) { /* translators: %d: number of tabs. */ $out[] = sprintf( _n( '%d tab of a translation had no equivalent with YIKES: that language shows the text of the default language until you translate it.', '%d tabs of translations had no equivalent with YIKES: those languages show the text of the default language until you translate them.', $a['missing_translation'], 'prodtabs' ), $a['missing_translation'] ); } if ( $a['not_in_source'] ) { /* translators: %d: number of tabs. */ $out[] = sprintf( _n( '%d tab of a translation does not exist in the default language: it is added to the list and shown only in the languages that have it.', '%d tabs of translations do not exist in the default language: they are added to the list and shown only in the languages that have them.', $a['not_in_source'], 'prodtabs' ), $a['not_in_source'] ); } if ( $a['pro'] ) { /* translators: %d: number of saved tabs. */ $out[] = sprintf( _n( '%d saved tab was a global or category tab of YIKES Pro: it is imported as a shared tab, to be added to products.', '%d saved tabs were global or category tabs of YIKES Pro: they are imported as shared tabs, to be added to products.', $a['pro'], 'prodtabs' ), $a['pro'] ); } if ( $a['imported'] ) { /* translators: %d: number of products. */ $out[] = sprintf( _n( '%d product was already imported: it is skipped, unless you choose to import it again.', '%d products were already imported: they are skipped, unless you choose to import them again.', $a['imported'], 'prodtabs' ), $a['imported'] ); } return $out; } /** * "3 products", "1 saved tab"⦠* * @param string $what products or saved. * @param int $count Number. */ private static function count_label( string $what, int $count ): string { switch ( $what ) { case 'products': /* translators: %d: number of products. */ return sprintf( _n( '%d product', '%d products', $count, 'prodtabs' ), $count ); default: /* translators: %d: number of saved tabs. */ return sprintf( _n( '%d saved tab', '%d saved tabs', $count, 'prodtabs' ), $count ); } } /** Step 2: products and the tabs they will get, options, start. */ private static function step_preview(): void { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only pagination. $page = isset( $_GET['paged'] ) ? max( 1, absint( $_GET['paged'] ) ) : 1; $per_page = 20; $preview = Import_Yikes::preview( $page, $per_page ); $pages = (int) ceil( $preview['total'] / $per_page ); ?>' . esc_html( sprintf( __( 'WooCommerce tab ā%sā hidden on this product.', 'prodtabs' ), $label ) ) . '
'; } } /** * Progress bar. * * @param int $done Products done. * @param int $total Products to do. */ private static function progress( int $done, int $total ): void { $percent = $total ? (int) floor( 100 * $done / $total ) : 0; ?>
' . esc_html__( 'No import has been run yet.', 'prodtabs' ) . '
'; echo '' . esc_html__( 'Start with the analysis', 'prodtabs' ) . '
'; return; } $c = $state['counts']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- message after a redirect. if ( isset( $_GET['deactivated'] ) ) { echo '' . esc_html__( 'Custom Product Tabs (YIKES) is deactivated. Its data stays in the database: do not delete the plugin until you have checked your products, deleting it erases its data.', 'prodtabs' ) . '