connection = $connection; } public function register(): void { add_action( 'admin_menu', array( $this, 'add_menu' ) ); add_action( 'admin_post_mowl_save_form', array( $this, 'save_form' ) ); add_action( 'admin_post_mowl_clear_log', array( $this, 'clear_log' ) ); add_action( 'admin_post_mowl_wc_save', array( $this, 'save_woocommerce' ) ); add_action( 'admin_post_mowl_wc_retry', array( $this, 'retry_woocommerce_events' ) ); add_action( 'admin_post_mowl_wc_import_preview', array( $this, 'import_preview' ) ); add_action( 'admin_post_mowl_wc_import_start', array( $this, 'import_start' ) ); add_action( 'admin_post_mowl_wc_import_resume', array( $this, 'import_resume' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); } /** @return array fül-slug => címke */ private function tabs(): array { $tabs = array( 'connection' => __( 'Connection', 'mowl-email' ), 'form' => __( 'Form', 'mowl-email' ), 'log' => __( 'Log', 'mowl-email' ), 'woocommerce' => __( 'WooCommerce', 'mowl-email' ), ); if ( ! Plugin::has_woocommerce() ) { unset( $tabs['woocommerce'] ); } return $tabs; } public function add_menu(): void { add_menu_page( __( 'Mowl', 'mowl-email' ), __( 'Mowl', 'mowl-email' ), 'manage_options', ConnectController::PAGE, array( $this, 'render' ), 'dashicons-email-alt', 58 ); } public function enqueue( string $hook ): void { if ( 'toplevel_page_' . ConnectController::PAGE === $hook ) { wp_enqueue_style( 'mowl-admin', MOWL_URL . 'assets/admin.css', array(), MOWL_VERSION ); wp_enqueue_script( 'mowl-admin-import', MOWL_URL . 'assets/admin-import.js', array( 'wp-api-fetch' ), MOWL_VERSION, true ); } } public function render(): void { if ( ! current_user_can( 'manage_options' ) ) { return; } $tabs = $this->tabs(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- fül-választás, nem művelet. $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'connection'; $tab = isset( $tabs[ $tab ] ) ? $tab : 'connection'; echo '

' . esc_html__( 'Mowl', 'mowl-email' ) . '

'; echo ''; if ( 'form' === $tab ) { $this->render_form_tab(); } elseif ( 'log' === $tab ) { $this->render_log_tab(); } elseif ( 'woocommerce' === $tab ) { $this->render_woocommerce_tab(); } else { $this->render_connection_tab(); } echo '
'; } private function render_connection_tab(): void { if ( ! $this->connection->is_connected() ) { ?>

' . esc_html( $home_host ) . '', '' . esc_html( (string) wp_parse_url( admin_url(), PHP_URL_HOST ) ) . '' ); ?>

'disabled' ) : null ); ?>

connection->health(); // Csak tömb `plan`-nal dolgozunk: egy skalár az array_key_exists()-nél fatal lenne. // A csomag neve (`name`, pl. „Starter”); a régi Mowl csak a `slug`-ot küldi. $plan = is_array( $health['plan'] ?? null ) ? $health['plan'] : array(); ?>

connection->tags(); ?>

[mowl_form]

shortcode_attributes() as $example => $help ) : ?>

[mowl_form tags="Newsletter" fields="email,first_name" doi="1"]

*/ private function shortcode_attributes(): array { return array( 'tags="Newsletter,Deals"' => __( 'Tag names or IDs, comma-separated. Replaces the default tags. Unknown names are skipped.', 'mowl-email' ), 'fields="email,first_name,last_name"' => __( 'Which fields to show. The email field is always shown.', 'mowl-email' ), 'doi="1"' => __( 'Double opt-in: 1 turns it on, 0 turns it off.', 'mowl-email' ), 'consent="1"' => __( 'Shows the consent checkbox (1) or hides it (0).', 'mowl-email' ), 'consent_text="…"' => __( 'The label of the consent checkbox.', 'mowl-email' ), 'button="…"' => __( 'The button label.', 'mowl-email' ), 'success="…"' => __( 'The message shown when the subscription is complete.', 'mowl-email' ), 'pending="…"' => __( 'The “check your inbox” message shown when double opt-in is on.', 'mowl-email' ), 'redirect="…"' => __( 'Where subscribers land after clicking the link in the confirmation email — not after submitting the form. Must be a page on this site.', 'mowl-email' ), ); } private function render_log_tab(): void { $entries = Log::all(); ?>
__( '15 minutes', 'mowl-email' ), 30 => __( '30 minutes', 'mowl-email' ), 60 => __( '1 hour', 'mowl-email' ), 120 => __( '2 hours', 'mowl-email' ), 240 => __( '4 hours', 'mowl-email' ), ); ?>
connection->is_connected() && $s['sync_enabled'] ) { $this->render_import_card(); } ?>

type ); ?> email ); ?> status ); ?> attempts ); ?> last_error ); ?> created_at ); ?>
connection ); $state = BackfillRunner::state(); $preview = get_transient( BackfillRunner::PREVIEW_TRANSIENT ); $preview = is_array( $preview ) ? $preview : null; $done = BackfillRunner::STATUS_DONE === $state['status']; ?>

render_import_progress(); } elseif ( BackfillRunner::STATUS_PAUSED_QUOTA === $state['status'] ) { $this->render_import_paused( $runner->status(), $preview ); } else { if ( $done ) { $status = $runner->status(); echo '

'; printf( /* translators: 1: number of imported customers, 2: number of skipped customers */ esc_html__( 'Import finished: %1$d imported, %2$d skipped.', 'mowl-email' ), (int) $status['processed'], (int) $status['skipped'] ); echo '

'; } $this->render_import_preview( $preview, $done ); } ?>

render_import_preview( $preview, true ); } private function render_import_preview( ?array $preview, bool $again ): void { if ( null === $preview ) { ?>
= (int) $preview['total']; ?>

'form' ) ) ); } /** A tárolt (titkosított) secret maszkolt alakja: „••••” + az utolsó 4 karakter, vagy üres. */ private function masked_secret( string $stored ): string { if ( '' === $stored ) { return ''; } $plain = Crypto::decrypt( $stored ); return null === $plain || '' === $plain ? '' : '••••' . substr( $plain, -4 ); } public function clear_log(): void { Guard::admin_action( 'mowl_clear_log' ); Log::clear(); Redirect::to( ConnectController::settings_url( array( 'tab' => 'log' ) ) ); } public function save_woocommerce(): void { Guard::admin_action( 'mowl_wc_save' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- a guard ellenőrizte. $post = (array) wp_unslash( $_POST ); WooSettings::save( array( 'sync_enabled' => ! empty( $post['sync_enabled'] ), 'consent_enabled' => ! empty( $post['consent_enabled'] ), 'consent_text' => isset( $post['consent_text'] ) ? sanitize_text_field( (string) $post['consent_text'] ) : '', 'account_consent_enabled' => ! empty( $post['account_consent_enabled'] ), 'cart_enabled' => ! empty( $post['cart_enabled'] ), 'cart_threshold_minutes' => isset( $post['cart_threshold_minutes'] ) ? (int) $post['cart_threshold_minutes'] : 0, ) ); // Külön opció (lásd DoubleOptIn); a kipipálatlan checkbox nem érkezik meg, ezért a hiánya a kikapcsolás. DoubleOptIn::set_enabled( ! empty( $post['consent_double_opt_in'] ) ); Notices::flash( 'success', __( 'WooCommerce settings saved.', 'mowl-email' ) ); Redirect::to( ConnectController::settings_url( array( 'tab' => 'woocommerce' ) ) ); } /** * Csak a `dead` sorokat próbálja újra: a `failed` érvénytelen eseményt * jelent (a Mowl elutasította), azt újraküldeni értelmetlen. */ public function retry_woocommerce_events(): void { Guard::admin_action( 'mowl_wc_retry' ); $count = EventQueue::retry_dead(); // Az elakadt eseményekre figyelmeztető notice oka megszűnt; egy újabb // elakadás új időbélyeggel újra beállítja. Options::unflag( 'wc_dead_events' ); Dispatcher::schedule(); Notices::flash( 'success', sprintf( /* translators: %d: number of re-queued events */ _n( '%d event re-queued.', '%d events re-queued.', $count, 'mowl-email' ), $count ) ); Redirect::to( ConnectController::settings_url( array( 'tab' => 'woocommerce' ) ) ); } public function import_preview(): void { $this->import_action( 'mowl_wc_import_preview', function (): void { ( new BackfillRunner( $this->connection ) )->preview(); } ); } public function import_start(): void { $this->import_action( 'mowl_wc_import_start', function (): void { ( new BackfillRunner( $this->connection ) )->start(); Notices::flash( 'success', __( 'Import started.', 'mowl-email' ) ); } ); } public function import_resume(): void { $this->import_action( 'mowl_wc_import_resume', function (): void { ( new BackfillRunner( $this->connection ) )->resume(); Notices::flash( 'success', __( 'Import resumed.', 'mowl-email' ) ); } ); } /** Az import-műveletek közös váza: guard, WooCommerce-ellenőrzés, Mowl-hiba → flash, vissza a WooCommerce fülre. */ private function import_action( string $nonce_action, callable $run ): void { Guard::admin_action( $nonce_action ); if ( Plugin::has_woocommerce() ) { try { $run(); } catch ( ApiException $e ) { /* translators: %s: error message */ Notices::flash( 'error', sprintf( __( 'Could not reach Mowl: %s', 'mowl-email' ), $e->getMessage() ) ); } } Redirect::to( ConnectController::settings_url( array( 'tab' => 'woocommerce' ) ) ); } }