array( __( 'Shop Setup', '7labs-debug-branding-for-commerce7' ), 'render_routes' ), 'c7dbt-menus' => array( __( 'Menus & Fixes', '7labs-debug-branding-for-commerce7' ), 'render_menus' ), 'c7dbt-design' => array( __( 'Design', '7labs-debug-branding-for-commerce7' ), 'render_design' ), 'c7dbt-prelaunch' => array( __( 'Launch & QA', '7labs-debug-branding-for-commerce7' ), 'render_prelaunch' ), 'c7dbt-help' => array( __( 'Get Help', '7labs-debug-branding-for-commerce7' ), 'render_help' ), 'c7dbt-settings' => array( __( 'Settings', '7labs-debug-branding-for-commerce7' ), 'render_settings' ), ); foreach ( $pages as $slug => $data ) { add_submenu_page( 'c7dbt', $data[0], $data[0], 'manage_options', $slug, array( $this, $data[1] ) ); } } /** * Whether current admin screen belongs to this plugin. * * @param string $hook Hook suffix. * @return bool */ private function is_plugin_screen( $hook = '' ) { if ( $hook ) { if ( 'toplevel_page_c7dbt' === $hook ) { return true; } if ( 0 === strpos( $hook, 'c7dbt_page_' ) ) { return true; } } if ( empty( $_GET['page'] ) ) { // phpcs:ignore return false; } $page = sanitize_key( wp_unslash( $_GET['page'] ) ); // phpcs:ignore return ( 'c7dbt' === $page || 0 === strpos( $page, 'c7dbt-' ) ); } /** * Assets — only on our screens (FixSeal pattern). * * @param string $hook Hook. */ public function enqueue( $hook ) { if ( ! $this->is_plugin_screen( $hook ) ) { return; } wp_enqueue_style( 'wp-color-picker' ); wp_enqueue_script( 'wp-color-picker' ); wp_enqueue_style( 'c7dbt-admin', C7DBT_URL . 'admin/css/admin.css', array( 'wp-color-picker' ), C7DBT_VERSION ); wp_enqueue_script( 'c7dbt-admin', C7DBT_URL . 'admin/js/admin.js', array( 'jquery', 'wp-color-picker' ), C7DBT_VERSION, true ); wp_localize_script( 'c7dbt-admin', 'c7dbtAdmin', array( 'i18n' => array( 'copied' => __( 'Copied', '7labs-debug-branding-for-commerce7' ), 'reportCopied' => __( 'Report copied to clipboard', '7labs-debug-branding-for-commerce7' ), ), ) ); } /** * POST / GET actions. */ public function handle_actions() { if ( ! current_user_can( 'manage_options' ) ) { return; } // Export brand kit (GET). if ( isset( $_GET['c7dbt_export'] ) && isset( $_GET['_wpnonce'] ) ) { if ( wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'c7dbt_export' ) ) { $payload = ( new C7DBT_Brand_Kit() )->export_payload(); nocache_headers(); header( 'Content-Type: application/json; charset=utf-8' ); header( 'Content-Disposition: attachment; filename=c7dbt-brand-kit.json' ); echo wp_json_encode( $payload, JSON_PRETTY_PRINT ); exit; } } // Developer / migration handoff report (GET). Full config + compiled CSS + // environment. Raw developer output — intended for a developer or 7Labs // integrating Commerce7 natively, not a one-click self-serve migration. if ( isset( $_GET['c7dbt_handoff'] ) && isset( $_GET['_wpnonce'] ) ) { if ( wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'c7dbt_handoff' ) ) { $report = $this->build_handoff_report(); nocache_headers(); header( 'Content-Type: application/json; charset=utf-8' ); header( 'Content-Disposition: attachment; filename=c7dbt-handoff-report.json' ); echo wp_json_encode( $report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ); exit; } } if ( empty( $_POST['c7dbt_action'] ) || empty( $_POST['_wpnonce'] ) ) { return; } $action = sanitize_key( wp_unslash( $_POST['c7dbt_action'] ) ); if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'c7dbt_admin' ) ) { return; } $redirect = admin_url( 'admin.php?page=c7dbt' ); $input = isset( $_POST['c7dbt'] ) ? wp_unslash( $_POST['c7dbt'] ) : array(); // phpcs:ignore $input = is_array( $input ) ? $input : array(); switch ( $action ) { case 'save_shop': case 'save_routes': case 'save_redirects': if ( isset( $input['redirect_from'], $input['redirect_to'] ) && is_array( $input['redirect_from'] ) ) { $custom = array(); foreach ( $input['redirect_from'] as $i => $from ) { $to = isset( $input['redirect_to'][ $i ] ) ? $input['redirect_to'][ $i ] : ''; if ( $from && $to ) { $custom[] = array( 'from' => $from, 'to' => $to ); } } $input['custom_redirects'] = $custom; } C7DBT_Settings::update( C7DBT_Settings::sanitize( $input ) ); $result = ( new C7DBT_Shop_Routes() )->apply(); $messages = array_merge( array( __( 'Shop settings saved.', '7labs-debug-branding-for-commerce7' ) ), isset( $result['messages'] ) ? $result['messages'] : array() ); $this->flash( $result['success'] ? 'success' : 'error', $messages ); $redirect = admin_url( 'admin.php?page=c7dbt-routes' ); break; case 'flush_permalinks': flush_rewrite_rules(); $this->flash( 'success', array( __( 'Permalinks flushed.', '7labs-debug-branding-for-commerce7' ) ) ); $redirect = admin_url( 'admin.php?page=c7dbt-routes' ); break; case 'fix_menus': $result = ( new C7DBT_Menu_Scanner() )->fix_all(); $this->flash( 'success', $result['messages'] ); $redirect = admin_url( 'admin.php?page=c7dbt-menus' ); break; case 'save_brand': $input['c7dbt_form'] = 'brand'; C7DBT_Settings::update( C7DBT_Settings::sanitize( $input ) ); $this->flash( 'success', array( __( 'Brand Kit saved. View any Commerce7 page to see changes.', '7labs-debug-branding-for-commerce7' ) ) ); $redirect = admin_url( 'admin.php?page=c7dbt-design' ); break; case 'save_layout': $input['c7dbt_form'] = 'layout'; C7DBT_Settings::update( C7DBT_Settings::sanitize( $input ) ); $this->flash( 'success', array( __( 'Shop Layout saved. Refresh a collection or product page to see changes.', '7labs-debug-branding-for-commerce7' ) ) ); $redirect = admin_url( 'admin.php?page=c7dbt-design' ); break; case 'import_font': if ( empty( $_FILES['c7dbt_font'] ) ) { $this->flash( 'error', array( __( 'No font file uploaded.', '7labs-debug-branding-for-commerce7' ) ) ); } else { $result = ( new C7DBT_Fonts() )->import_upload( $_FILES['c7dbt_font'] ); // phpcs:ignore if ( is_wp_error( $result ) ) { $this->flash( 'error', array( $result->get_error_message() ) ); } else { $this->flash( 'success', array( sprintf( /* translators: %s: font family */ __( 'Font “%s” uploaded. Select it under Heading or Body font, then save Brand Kit.', '7labs-debug-branding-for-commerce7' ), $result['family'] ), ) ); } } $redirect = admin_url( 'admin.php?page=c7dbt-design' ); break; case 'delete_font': $id = isset( $_POST['font_id'] ) ? sanitize_text_field( wp_unslash( $_POST['font_id'] ) ) : ''; if ( $id && ( new C7DBT_Fonts() )->delete_custom( $id ) ) { $this->flash( 'success', array( __( 'Custom font removed.', '7labs-debug-branding-for-commerce7' ) ) ); } else { $this->flash( 'error', array( __( 'Could not remove font.', '7labs-debug-branding-for-commerce7' ) ) ); } $redirect = admin_url( 'admin.php?page=c7dbt-design' ); break; case 'apply_preset': $key = isset( $_POST['preset'] ) ? sanitize_key( wp_unslash( $_POST['preset'] ) ) : ''; if ( C7DBT_Presets::apply( $key ) ) { $this->flash( 'success', array( __( 'Preset applied to Brand Kit.', '7labs-debug-branding-for-commerce7' ) ) ); } else { $this->flash( 'error', array( __( 'Unknown preset.', '7labs-debug-branding-for-commerce7' ) ) ); } $redirect = admin_url( 'admin.php?page=c7dbt-design' ); break; case 'detect_theme': ( new C7DBT_Brand_Kit() )->detect_theme_references(); $this->flash( 'success', array( __( 'Theme references detected from Elementor / theme mods where available.', '7labs-debug-branding-for-commerce7' ) ) ); $redirect = admin_url( 'admin.php?page=c7dbt-design' ); break; case 'match_theme': C7DBT_Settings::update( C7DBT_Settings::sanitize( $input ) ); ( new C7DBT_Brand_Kit() )->match_theme_to_brand(); $this->flash( 'success', array( __( 'Theme references copied into Brand Kit.', '7labs-debug-branding-for-commerce7' ) ) ); $redirect = admin_url( 'admin.php?page=c7dbt-design' ); break; case 'import_brand': $upload = isset( $_FILES['c7dbt_import'] ) ? $_FILES['c7dbt_import'] : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $tmp_name = isset( $upload['tmp_name'] ) ? $upload['tmp_name'] : ''; $err_code = isset( $upload['error'] ) ? (int) $upload['error'] : UPLOAD_ERR_NO_FILE; $size = isset( $upload['size'] ) ? (int) $upload['size'] : 0; $max_size = 1048576; // 1 MB — a Brand Kit JSON is only a few KB. if ( UPLOAD_ERR_OK !== $err_code || '' === $tmp_name || ! is_uploaded_file( $tmp_name ) ) { $this->flash( 'error', array( __( 'No file uploaded.', '7labs-debug-branding-for-commerce7' ) ) ); } elseif ( $size < 1 || $size > $max_size ) { $this->flash( 'error', array( __( 'That file is empty or too large for a Brand Kit.', '7labs-debug-branding-for-commerce7' ) ) ); } else { $raw = file_get_contents( $tmp_name ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents $data = json_decode( (string) $raw, true ); if ( ! is_array( $data ) || ! ( new C7DBT_Brand_Kit() )->import_payload( $data ) ) { $this->flash( 'error', array( __( 'Invalid Brand Kit JSON.', '7labs-debug-branding-for-commerce7' ) ) ); } else { $this->flash( 'success', array( __( 'Brand Kit imported.', '7labs-debug-branding-for-commerce7' ) ) ); } } $redirect = admin_url( 'admin.php?page=c7dbt-design' ); break; case 'reset_brand': C7DBT_Settings::update( array( 'brand' => C7DBT_Settings::default_brand() ) ); $this->flash( 'success', array( __( 'Brand Kit reset (no overrides).', '7labs-debug-branding-for-commerce7' ) ) ); $redirect = admin_url( 'admin.php?page=c7dbt-design' ); break; case 'save_settings': C7DBT_Settings::update( C7DBT_Settings::sanitize( $input ) ); $this->flash( 'success', array( __( 'Settings saved.', '7labs-debug-branding-for-commerce7' ) ) ); $redirect = admin_url( 'admin.php?page=c7dbt-settings' ); break; case 'quick_fix': // One-click: enable redirect + apply routes + fix menus. $slug = isset( $input['default_collection_slug'] ) ? C7DBT_Helpers::normalize_collection_slug( $input['default_collection_slug'] ) : ''; if ( ! $slug ) { $slug = C7DBT_Helpers::collection_slug(); } C7DBT_Settings::update( array( 'default_collection_slug' => $slug, 'redirect_collection_root' => 1, 'update_continue_shopping' => 1, ) ); $r1 = ( new C7DBT_Shop_Routes() )->apply(); $r2 = ( new C7DBT_Menu_Scanner() )->fix_all(); $this->flash( 'success', array_merge( $r1['messages'], $r2['messages'] ) ); $redirect = admin_url( 'admin.php?page=c7dbt&c7dbt_fixed=1' ); break; case 'run_site_qa': $report = ( new C7DBT_Site_QA() )->run(); $this->flash( $report['failed'] ? 'error' : 'success', array( sprintf( /* translators: 1: passed, 2: failed */ __( 'Site QA finished: %1$d passed, %2$d failed.', '7labs-debug-branding-for-commerce7' ), $report['passed'], $report['failed'] ), ) ); $redirect = admin_url( 'admin.php?page=c7dbt-prelaunch' ); break; } wp_safe_redirect( $redirect ); exit; } /** * Redirect legacy submenu slugs to merged screens. */ public function legacy_page_redirects() { if ( ! current_user_can( 'manage_options' ) || wp_doing_ajax() ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; $map = array( 'c7dbt-redirects' => 'c7dbt-routes', 'c7dbt-conflicts' => 'c7dbt-menus', 'c7dbt-qa' => 'c7dbt-prelaunch', 'c7dbt-brand' => 'c7dbt-design', 'c7dbt-layout' => 'c7dbt-design', 'c7dbt-visual' => 'c7dbt-design', ); if ( empty( $map[ $page ] ) ) { return; } wp_safe_redirect( admin_url( 'admin.php?page=' . $map[ $page ] ) ); exit; } /** * Flash notice. * * @param string $type Type. * @param array $messages Messages. */ private function flash( $type, array $messages ) { set_transient( 'c7dbt_admin_notice', array( 'type' => $type, 'messages' => $messages, ), 60 ); } /** * Notices — scoped to our screens only (avoid WP-admin noise). */ public function admin_notices() { if ( ! $this->is_plugin_screen() ) { return; } $notice = get_transient( 'c7dbt_admin_notice' ); if ( $notice && is_array( $notice ) ) { delete_transient( 'c7dbt_admin_notice' ); $class = ( 'error' === $notice['type'] ) ? 'notice-error' : 'notice-success'; echo '
' . esc_html( implode( ' ', (array) $notice['messages'] ) ) . '