PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.5.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.5.0
5.5.0 5.4.0 5.3.2 5.3.1 5.1.6 5.1.5 trunk 2.1.5 2.11 2.12 2.13 2.15 3.0.0 3.0.1 3.0.2 3.0.3 3.0.5 3.0.51 3.0.60 3.0.61 3.0.62 3.0.70 3.0.71 3.0.72 3.1.0 All 34 releases
double-opt-in / src / EmailTemplates / EmailTemplateIntegration.php

EmailTemplateIntegration.php in Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification 5.5.0, at src/EmailTemplates/EmailTemplateIntegration.php

342 lines 9.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Email Template Integration
4 *
5 * Provides integration with CF7 and Avada Forms for custom email templates.
6 *
7 * @package Forge12\DoubleOptIn\EmailTemplates
8 * @since 4.0.0
9 */
10
11 namespace Forge12\DoubleOptIn\EmailTemplates;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Class EmailTemplateIntegration
19 *
20 * Helper class for integrating custom email templates with form plugins.
21 */
22 class EmailTemplateIntegration {
23
24 /**
25 * Repository instance.
26 *
27 * @var EmailTemplateRepository
28 */
29 private EmailTemplateRepository $repository;
30
31 /**
32 * HTML Generator instance.
33 *
34 * @var EmailHtmlGenerator
35 */
36 private EmailHtmlGenerator $htmlGenerator;
37
38 /**
39 * Constructor.
40 */
41 public function __construct() {
42 $this->repository = new EmailTemplateRepository();
43 $this->htmlGenerator = new EmailHtmlGenerator();
44 }
45
46 /**
47 * Initialize hooks.
48 *
49 * @return void
50 */
51 public function init(): void {
52 // Hook into template loading Ajax
53 add_action( 'wp_ajax_f12_doi_templateloader', array( $this, 'handleTemplateLoaderAjax' ), 5 );
54
55 // Hook into template rendering for email sending
56 add_filter( 'f12_cf7_doubleoptin_template_body', array( $this, 'renderCustomTemplateBody' ), 10, 4 );
57
58 // Add custom templates to CF7 panel
59 add_action( 'f12_cf7_doubleoptin_admin_panel_templates', array( $this, 'renderCustomTemplateOptions' ), 10, 2 );
60 }
61
62 /**
63 * Get all available templates (built-in + custom).
64 *
65 * @return array Array of template options with 'value' => 'label' format.
66 */
67 public function getAvailableTemplates(): array {
68 $templates = array(
69 'blank' => __( 'Blank', 'double-opt-in' ),
70 'newsletter_en' => __( 'Newsletter EN', 'double-opt-in' ),
71 'newsletter_en_2' => __( 'Newsletter EN 2', 'double-opt-in' ),
72 'newsletter_en_3' => __( 'Newsletter EN 3', 'double-opt-in' ),
73 );
74
75 // Add custom templates
76 $customTemplates = $this->repository->findAll(
77 array(
78 'post_status' => 'publish',
79 )
80 );
81
82 foreach ( $customTemplates as $template ) {
83 $templates[ 'custom_' . $template['id'] ] = sprintf(
84 __( '%s (Custom)', 'double-opt-in' ),
85 $template['title']
86 );
87 }
88
89 return $templates;
90 }
91
92 /**
93 * Get custom templates for display.
94 *
95 * @return array Array of custom template data.
96 */
97 public function getCustomTemplates(): array {
98 return $this->repository->findAll(
99 array(
100 'post_status' => 'publish',
101 )
102 );
103 }
104
105 /**
106 * Check if a template is a custom template.
107 *
108 * @param string $templateKey Template key.
109 * @return bool True if custom template.
110 */
111 public function isCustomTemplate( string $templateKey ): bool {
112 return strpos( $templateKey, 'custom_' ) === 0;
113 }
114
115 /**
116 * Get custom template ID from template key.
117 *
118 * @param string $templateKey Template key (e.g., 'custom_123').
119 * @return int|null Template ID or null.
120 */
121 public function getCustomTemplateId( string $templateKey ): ?int {
122 if ( ! $this->isCustomTemplate( $templateKey ) ) {
123 return null;
124 }
125
126 return (int) str_replace( 'custom_', '', $templateKey );
127 }
128
129 /**
130 * Handle template loader Ajax request for custom templates.
131 *
132 * @return void
133 */
134 public function handleTemplateLoaderAjax(): void {
135 // Check nonce
136 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( wp_unslash( $_POST['nonce'] ), 'f12_doi_templateloader' ) ) {
137 return; // Let default handler run
138 }
139
140 // A nonce proves intent, not authorization — a template's rendered HTML
141 // must only be readable by users who may manage the plugin.
142 if ( ! current_user_can( 'manage_options' ) ) {
143 return;
144 }
145
146 $templateKey = isset( $_POST['template'] ) ? sanitize_text_field( $_POST['template'] ) : '';
147
148 if ( ! $this->isCustomTemplate( $templateKey ) ) {
149 return; // Let default handler run
150 }
151
152 $templateId = $this->getCustomTemplateId( $templateKey );
153 if ( ! $templateId ) {
154 // Match the expected response format for the template loader JS
155 // Don't set Content-Type header - jQuery would auto-parse and the JS does JSON.parse manually
156 echo wp_json_encode(
157 array(
158 'status' => 400,
159 'content' => '',
160 'message' => __( 'Invalid template.', 'double-opt-in' ),
161 )
162 );
163 wp_die( '', '', array( 'response' => null ) );
164 }
165
166 $template = $this->repository->findById( $templateId );
167 if ( ! $template ) {
168 // Match the expected response format for the template loader JS
169 echo wp_json_encode(
170 array(
171 'status' => 404,
172 'content' => '',
173 'message' => __( 'Template not found.', 'double-opt-in' ),
174 )
175 );
176 wp_die( '', '', array( 'response' => null ) );
177 }
178
179 // Generate HTML from blocks
180 $blocks = json_decode( $template['blocks_json'], true ) ?: array();
181 $globalStyles = json_decode( $template['global_styles'], true ) ?: array();
182
183 $html = $this->htmlGenerator->generate( $blocks, $globalStyles );
184
185 // Return in the expected format for the template loader JS
186 // The JS expects: { status: 200, content: "..." } as a string (not auto-parsed JSON)
187 echo wp_json_encode(
188 array(
189 'status' => 200,
190 'content' => $html,
191 )
192 );
193 wp_die( '', '', array( 'response' => null ) );
194 }
195
196 /**
197 * Render custom template body for email sending.
198 *
199 * @param string $body Current email body.
200 * @param string $templateKey Template key (e.g., 'blank', 'custom_123').
201 * @param array $parameter Form parameters.
202 * @param mixed $optIn OptIn object.
203 * @return string Modified email body.
204 */
205 public function renderCustomTemplateBody( string $body, string $templateKey, array $parameter, $optIn ): string {
206 if ( ! $this->isCustomTemplate( $templateKey ) ) {
207 return $body;
208 }
209
210 $templateId = $this->getCustomTemplateId( $templateKey );
211 if ( ! $templateId ) {
212 return $body;
213 }
214
215 $template = $this->repository->findById( $templateId );
216 if ( ! $template ) {
217 return $body;
218 }
219
220 // Generate HTML from blocks
221 $blocks = json_decode( $template['blocks_json'], true ) ?: array();
222 $globalStyles = json_decode( $template['global_styles'], true ) ?: array();
223
224 return $this->htmlGenerator->generate( $blocks, $globalStyles );
225 }
226
227 /**
228 * Render custom template options in the CF7 admin panel.
229 *
230 * @param array $metadata Current form metadata.
231 * @param string $id Form element ID prefix.
232 * @return void
233 */
234 public function renderCustomTemplateOptions( array $metadata, string $id ): void {
235 $customTemplates = $this->getCustomTemplates();
236
237 if ( empty( $customTemplates ) ) {
238 return;
239 }
240
241 $selectedTemplate = $metadata['template'] ?? '';
242
243 // Add inline script via WordPress hook to avoid sanitization issues
244 add_action(
245 'admin_footer',
246 function () {
247 ?>
248 <script type="text/javascript">
249 jQuery(document).ready(function($) {
250 $('.custom-template-preview').on('click', function() {
251 var template = $(this).data('template');
252 $('.f12-cf7-templateloader').val(template).trigger('change');
253 $('.preview-item-inner').removeClass('active');
254 $('.f12-cf7-templateloader-preview').parent().removeClass('active');
255 $(this).closest('.preview-item-inner').addClass('active');
256 });
257 });
258 </script>
259 <style type="text/css">
260 .custom-templates-section {
261 margin-top: 20px;
262 padding-top: 20px;
263 border-top: 1px solid #ddd;
264 }
265 .custom-templates-section h4 {
266 margin-bottom: 15px;
267 }
268 .custom-templates-section .preview {
269 display: flex;
270 flex-wrap: wrap;
271 gap: 10px;
272 }
273 .custom-template-box {
274 width: 120px;
275 height: 90px;
276 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
277 border-radius: 4px;
278 display: flex;
279 flex-direction: column;
280 align-items: center;
281 justify-content: center;
282 color: #fff;
283 cursor: pointer;
284 transition: transform 0.2s, box-shadow 0.2s;
285 }
286 .custom-template-box:hover {
287 transform: translateY(-2px);
288 box-shadow: 0 4px 12px rgba(0,0,0,0.15);
289 }
290 .custom-template-box .template-icon {
291 font-size: 24px;
292 margin-bottom: 5px;
293 }
294 .custom-template-box .template-name {
295 font-size: 11px;
296 font-weight: 600;
297 text-align: center;
298 padding: 0 5px;
299 white-space: nowrap;
300 overflow: hidden;
301 text-overflow: ellipsis;
302 max-width: 100%;
303 }
304 .preview-item-inner.active .custom-template-box {
305 box-shadow: 0 0 0 3px #0073aa;
306 }
307 </style>
308 <?php
309 }
310 );
311 ?>
312 <div class="custom-templates-section">
313 <h4><?php _e( 'Custom Templates', 'double-opt-in' ); ?></h4>
314 <p style="margin-bottom: 15px; color: #666; font-size: 12px;">
315 <?php _e( 'Or select a custom template from the Email Editor:', 'double-opt-in' ); ?>
316 <a href="<?php echo esc_url( admin_url( 'admin.php?page=f12-doi-admin#/email-templates' ) ); ?>" target="_blank">
317 <?php _e( 'Manage Templates', 'double-opt-in' ); ?>
318 </a>
319 </p>
320 <div class="preview">
321 <?php
322 foreach ( $customTemplates as $template ) :
323 $templateKey = 'custom_' . $template['id'];
324 $isActive = $selectedTemplate === $templateKey;
325 ?>
326 <div class="preview-item">
327 <div class="preview-item-inner <?php echo $isActive ? 'active' : ''; ?>">
328 <div class="f12-cf7-templateloader-preview custom-template-preview custom-template-box"
329 data-template="<?php echo esc_attr( $templateKey ); ?>"
330 title="<?php echo esc_attr( $template['title'] ); ?>">
331 <span class="template-icon"></span>
332 <span class="template-name"><?php echo esc_html( $template['title'] ); ?></span>
333 </div>
334 </div>
335 </div>
336 <?php endforeach; ?>
337 </div>
338 </div>
339 <?php
340 }
341 }
342