repository = new EmailTemplateRepository(); $this->htmlGenerator = new EmailHtmlGenerator(); } /** * Initialize hooks. * * @return void */ public function init(): void { // Hook into template loading Ajax add_action( 'wp_ajax_f12_doi_templateloader', array( $this, 'handleTemplateLoaderAjax' ), 5 ); // Hook into template rendering for email sending add_filter( 'f12_cf7_doubleoptin_template_body', array( $this, 'renderCustomTemplateBody' ), 10, 4 ); // Add custom templates to CF7 panel add_action( 'f12_cf7_doubleoptin_admin_panel_templates', array( $this, 'renderCustomTemplateOptions' ), 10, 2 ); } /** * Get all available templates (built-in + custom). * * @return array Array of template options with 'value' => 'label' format. */ public function getAvailableTemplates(): array { $templates = array( 'blank' => __( 'Blank', 'double-opt-in' ), 'newsletter_en' => __( 'Newsletter EN', 'double-opt-in' ), 'newsletter_en_2' => __( 'Newsletter EN 2', 'double-opt-in' ), 'newsletter_en_3' => __( 'Newsletter EN 3', 'double-opt-in' ), ); // Add custom templates $customTemplates = $this->repository->findAll( array( 'post_status' => 'publish', ) ); foreach ( $customTemplates as $template ) { $templates[ 'custom_' . $template['id'] ] = sprintf( __( '%s (Custom)', 'double-opt-in' ), $template['title'] ); } return $templates; } /** * Get custom templates for display. * * @return array Array of custom template data. */ public function getCustomTemplates(): array { return $this->repository->findAll( array( 'post_status' => 'publish', ) ); } /** * Check if a template is a custom template. * * @param string $templateKey Template key. * @return bool True if custom template. */ public function isCustomTemplate( string $templateKey ): bool { return strpos( $templateKey, 'custom_' ) === 0; } /** * Get custom template ID from template key. * * @param string $templateKey Template key (e.g., 'custom_123'). * @return int|null Template ID or null. */ public function getCustomTemplateId( string $templateKey ): ?int { if ( ! $this->isCustomTemplate( $templateKey ) ) { return null; } return (int) str_replace( 'custom_', '', $templateKey ); } /** * Handle template loader Ajax request for custom templates. * * @return void */ public function handleTemplateLoaderAjax(): void { // Check nonce if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( wp_unslash( $_POST['nonce'] ), 'f12_doi_templateloader' ) ) { return; // Let default handler run } // A nonce proves intent, not authorization — a template's rendered HTML // must only be readable by users who may manage the plugin. if ( ! current_user_can( 'manage_options' ) ) { return; } $templateKey = isset( $_POST['template'] ) ? sanitize_text_field( $_POST['template'] ) : ''; if ( ! $this->isCustomTemplate( $templateKey ) ) { return; // Let default handler run } $templateId = $this->getCustomTemplateId( $templateKey ); if ( ! $templateId ) { // Match the expected response format for the template loader JS // Don't set Content-Type header - jQuery would auto-parse and the JS does JSON.parse manually echo wp_json_encode( array( 'status' => 400, 'content' => '', 'message' => __( 'Invalid template.', 'double-opt-in' ), ) ); wp_die( '', '', array( 'response' => null ) ); } $template = $this->repository->findById( $templateId ); if ( ! $template ) { // Match the expected response format for the template loader JS echo wp_json_encode( array( 'status' => 404, 'content' => '', 'message' => __( 'Template not found.', 'double-opt-in' ), ) ); wp_die( '', '', array( 'response' => null ) ); } // Generate HTML from blocks $blocks = json_decode( $template['blocks_json'], true ) ?: array(); $globalStyles = json_decode( $template['global_styles'], true ) ?: array(); $html = $this->htmlGenerator->generate( $blocks, $globalStyles ); // Return in the expected format for the template loader JS // The JS expects: { status: 200, content: "..." } as a string (not auto-parsed JSON) echo wp_json_encode( array( 'status' => 200, 'content' => $html, ) ); wp_die( '', '', array( 'response' => null ) ); } /** * Render custom template body for email sending. * * @param string $body Current email body. * @param string $templateKey Template key (e.g., 'blank', 'custom_123'). * @param array $parameter Form parameters. * @param mixed $optIn OptIn object. * @return string Modified email body. */ public function renderCustomTemplateBody( string $body, string $templateKey, array $parameter, $optIn ): string { if ( ! $this->isCustomTemplate( $templateKey ) ) { return $body; } $templateId = $this->getCustomTemplateId( $templateKey ); if ( ! $templateId ) { return $body; } $template = $this->repository->findById( $templateId ); if ( ! $template ) { return $body; } // Generate HTML from blocks $blocks = json_decode( $template['blocks_json'], true ) ?: array(); $globalStyles = json_decode( $template['global_styles'], true ) ?: array(); return $this->htmlGenerator->generate( $blocks, $globalStyles ); } /** * Render custom template options in the CF7 admin panel. * * @param array $metadata Current form metadata. * @param string $id Form element ID prefix. * @return void */ public function renderCustomTemplateOptions( array $metadata, string $id ): void { $customTemplates = $this->getCustomTemplates(); if ( empty( $customTemplates ) ) { return; } $selectedTemplate = $metadata['template'] ?? ''; // Add inline script via WordPress hook to avoid sanitization issues add_action( 'admin_footer', function () { ?>