PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 2.15.3
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v2.15.3
3.3.1 V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 All 138 releases
← All changes | includes/Core/Util/MailNotifier.php +56 -313 3.1.12.15.3 View file →
@@ -1,144 +1,46 @@
1 1 <?php
2 2
3 3 namespace BitCode\BitForm\Core\Util;
4 4
5 -if (!defined('ABSPATH')) {
6 - exit;
7 -}
8 -
9 -use BitCode\BitForm\Admin\Form\Helpers;
10 5 use BitCode\BitForm\Core\Database\FormEntryMetaModel;
11 -use BitCode\BitForm\Core\Form\FormManager;
12 6 use BitCode\BitForm\Core\Messages\EmailTemplateHandler;
13 7 use BitCode\BitForm\Core\Messages\PdfTemplateHandler;
8 +use BitCode\BitFormPro\Admin\AppSetting\Pdf;
14 9
15 10 final class MailNotifier
16 11 {
17 12 public static function notify($notifyDetails, $formID, $fieldValue, $entryID, $isDblOptin = false, $logId = '')
18 13 {
19 - $apiResponse = new ApiResponse();
20 - $formManager = FormManager::getInstance($formID);
21 - $entryDetails = ['formId' => $formID, 'entryId' => $entryID, 'fieldValues' => $fieldValue];
22 14 $emailTemplateHandler = new EmailTemplateHandler($formID);
23 15 $attachments = [];
24 16 $tempPdfLink = '';
25 - $pdfPassForEmail = '';
26 17 if (!empty($notifyDetails->pdfId) && is_string($notifyDetails->pdfId)) {
27 18 $pdfTemplateID = json_decode($notifyDetails->pdfId)->id;
28 19 $pdfTemplateHandler = new PdfTemplateHandler($formID);
29 20
30 21 $pdfTemplate = $pdfTemplateHandler->getById($pdfTemplateID);
22 + $pdfBody = FieldValueHandler::replaceFieldWithValue($pdfTemplate[0]->body, $fieldValue);
31 23
32 - // Bail out if the template lookup failed (WP_Error) or returned no row.
33 - // Without this guard json_decode($pdfTemplate[0]->setting) fatals and the whole email is lost.
34 - if (is_wp_error($pdfTemplate) || empty($pdfTemplate[0]) || !isset($pdfTemplate[0]->setting)) {
35 - $apiResponse->apiResponse($logId, '', ['type' => 'record', 'type_name' => 'pdf'], 'errors', 'PDF template not found, skipping PDF attachment.', $entryDetails);
36 - Log::debug_log([
37 - 'status' => 'error',
38 - 'code' => 'pdf_template_not_found',
39 - 'message' => 'PDF template not found, skipping PDF attachment',
40 - 'inputDetails' => [
41 - 'notifyDetails' => $notifyDetails,
42 - 'formID' => $formID,
43 - 'entryID' => $entryID,
44 - 'pdfTemplateID' => $pdfTemplateID,
45 - ],
46 - 'responseDetails' => is_wp_error($pdfTemplate) ? $pdfTemplate->get_error_message() : 'empty result',
47 - ]);
48 - } else {
49 - $pdfSetting = json_decode($pdfTemplate[0]->setting);
24 + $pdfSetting = json_decode($pdfTemplate[0]->setting);
50 25
51 - $path = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'pdf';
52 - // $fileName = 'bit-form-pdf-' . $formID . '-' . $entryID;
26 + $path = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'pdf';
27 + $fileName = 'bit-form-pdf-' . $formID . '-' . $entryID;
53 28
54 - if (!is_dir($path)) {
55 - wp_mkdir_p($path);
56 - }
29 + if (!is_dir($path)) {
30 + mkdir($path, 0777, true);
31 + }
57 32
58 - if (class_exists('\BitCode\BitFormPro\Admin\AppSetting\Pdf')) {
59 - $serverPath = Helpers::getFullPathWithEncryptedEntryId($formID, $entryID);
60 - $webPath = Helpers::getWebPathWithEncryptedEntryId($formID, $entryID);
33 + if (class_exists('\BitCode\BitFormPro\Admin\AppSetting\Pdf')) {
34 + $generatedPdf = Pdf::getInstance()->generator($pdfSetting, $pdfBody, $path, $entryID, 'F');
61 35
62 - if (isset($pdfSetting->password)) {
63 - if (isset($pdfSetting->password->static) && $pdfSetting->password->static && !empty($pdfSetting->password->pass)) {
64 - $pass = FieldValueHandler::replaceFieldWithValue($pdfSetting->password->pass, $fieldValue);
65 - $pdfSetting->password->pass = $pass;
66 - } elseif (isset($pdfSetting->password->dynamic)) {
67 - $pass = Helpers::PDFPassHash($entryID);
68 - $pdfSetting->password->pass = $pass;
69 - }
70 - }
71 - if (isset($pdfSetting->pdfFileName)) {
72 - $pdfSetting->pdfFileName = FieldValueHandler::replaceFieldWithValue($pdfSetting->pdfFileName, $fieldValue);
73 - // allow developers to modify PDF filename
74 - $pdfSetting->pdfFileName = apply_filters(
75 - 'bitform_filter_pdf_filename',
76 - $pdfSetting->pdfFileName,
77 - [
78 - 'form_id' => $formID,
79 - 'entry_id' => $entryID,
80 - 'field_values' => $fieldValue,
81 - 'pdf_setting' => $pdfSetting,
82 - 'template' => $pdfTemplate[0] ?? null,
83 - ]
84 - );
85 - }
36 + // $pdfFile = $path . DIRECTORY_SEPARATOR . $fileName . '.pdf';
86 37
87 - $fieldValue['entry_id'] = $entryID;
88 -
89 - $pdfBody = FieldValueHandler::replaceFieldWithValue($pdfTemplate[0]->body, $fieldValue, $formID);
90 - $pdfBody = FieldValueHandler::changeImagePathInHTMLString($pdfBody, $serverPath);
91 - $pdfBody = FieldValueHandler::changeHrefPathInHTMLString($pdfBody, $webPath); // replace anchor tag href with constructed weburl
92 -
93 - // allow developers to modify PDF body
94 - $pdfBody = apply_filters(
95 - 'bitform_filter_pdf_body',
96 - $pdfBody,
97 - [
98 - 'form_id' => $formID,
99 - 'entry_id' => $entryID,
100 - 'field_values' => $fieldValue,
101 - 'pdf_setting' => $pdfSetting,
102 - 'template' => $pdfTemplate[0] ?? null,
103 - ]
104 - );
105 -
106 - $generatedPdf = \BitCode\BitFormPro\Admin\AppSetting\Pdf::getInstance()->generator($pdfSetting, $pdfBody, $path, $entryID, 'F');
107 -
108 - if (!is_wp_error($generatedPdf) && file_exists($generatedPdf)) {
109 - $attachments[] = $generatedPdf;
110 - $tempPdfLink = $generatedPdf;
111 - $apiResponse->apiResponse($logId, '', ['type' => 'record', 'type_name' => 'pdf'], 'success', 'PDF successfully generated.', $entryDetails);
112 - Log::debug_log([
113 - 'status' => 'success',
114 - 'code' => 'pdf_generated',
115 - 'message' => 'PDF successfully generated',
116 - 'inputDetails' => [
117 - 'notifyDetails' => $notifyDetails,
118 - 'formID' => $formID,
119 - 'entryID' => $entryID,
120 - 'isDblOptin' => $isDblOptin,
121 - 'logId' => $logId
122 - ],
123 - 'responseDetails' => $generatedPdf
124 - ]);
125 - } else {
126 - $apiResponse->apiResponse($logId, '', ['type' => 'record', 'type_name' => 'pdf'], 'errors', 'Error in generating PDF.', $entryDetails);
127 - Log::debug_log([
128 - 'status' => 'error',
129 - 'code' => 'pdf_generation_error',
130 - 'message' => 'Error in generating PDF',
131 - 'inputDetails' => [
132 - 'notifyDetails' => $notifyDetails,
133 - 'formID' => $formID,
134 - 'entryID' => $entryID,
135 - 'isDblOptin' => $isDblOptin,
136 - 'logId' => $logId
137 - ],
138 - 'responseDetails' => $generatedPdf->get_error_message()
139 - ]);
140 - }
38 + if (!is_wp_error($generatedPdf) && file_exists($generatedPdf)) {
39 + $attachments[] = $generatedPdf;
40 + $tempPdfLink = $generatedPdf;
41 + } else {
42 + Log::debug_log('Error in generating PDF: ' . $generatedPdf->get_error_message());
141 43 }
142 44 }
143 45 }
144 46
@@ -151,64 +53,15 @@
151 53 $from_name = '';
152 54 if (isset($notifyDetails->from_name) && !empty($notifyDetails->from_name)) {
153 55 $from_name = $notifyDetails->from_name;
154 56 }
57 + (new MailConfig())->sendMail(['from_name' => $from_name]);
58 + $mailSubject = FieldValueHandler::replaceFieldWithValue($mailTemplate[0]->sub, $fieldValue);
59 + $mailBody = FieldValueHandler::replaceFieldWithValue($mailTemplate[0]->body, $fieldValue);
60 +
155 61 $mailHeaders = [
156 - // 'Content-Type: text/html; charset=UTF-8',
157 - // $embeddedMailHeader
62 + // "Content-Type: text/html; charset=UTF-8",
158 63 ];
159 - $from_mail = '';
160 - if (!empty($notifyDetails->from)) {
161 - $fromMail = FieldValueHandler::validateMailArry($notifyDetails->from, $fieldValue);
162 - $headerFromName = !empty($notifyDetails->fromName) ? $notifyDetails->fromName : explode('@', $fromMail[0])[0];
163 - $mailHeaders[] = "FROM: $headerFromName " . '<' . sanitize_email($fromMail[0]) . '>';
164 - $from_mail = $fromMail[0];
165 - }
166 - (new MailConfig())->sendMail(['from_name' => $from_name, 'from_email' => $from_mail]);
167 - $mailSubject = FieldValueHandler::replaceFieldWithValue($mailTemplate[0]->sub, $fieldValue, $formID);
168 -
169 - // allow developers to modify email subject
170 - $mailSubject = apply_filters(
171 - 'bitform_filter_email_subject',
172 - $mailSubject,
173 - [
174 - 'form_id' => $formID,
175 - 'entry_id' => $entryID,
176 - 'field_values' => $fieldValue,
177 - 'template' => $mailTemplate[0] ?? null,
178 - 'is_double_optin' => (bool) $isDblOptin,
179 - ]
180 - );
181 -
182 - $mailBody = $mailTemplate[0]->body;
183 - if (class_exists('\BitCode\BitFormPro\Admin\DownloadFile')) {
184 - $downloadFile = new \BitCode\BitFormPro\Admin\DownloadFile();
185 - $mailBody = $downloadFile->replacePdfShortCodeToLink($mailBody, $formID, $entryID);
186 - $mailBody = $downloadFile->replaceShortCodeToPdfPassword($mailBody, $formID, $entryID);
187 - }
188 -
189 - $mailBody = FieldValueHandler::replaceFieldWithValue($mailBody, $fieldValue, $formID);
190 - // Signature images: embed inline (cid:) so they render for non-logged-in recipients.
191 - // Must run before changeImagePathInHTMLString so the src is still the raw filename.
192 - $cidMap = [];
193 - $sigBasePath = FileHandler::getEntriesFileUploadDir($formID, $entryID) . DIRECTORY_SEPARATOR;
194 - $mailBody = self::embedSignatureImages($mailBody, $formManager, $fieldValue, $sigBasePath, $cidMap);
195 - $webUrl = Helpers::getWebPathWithEncryptedEntryId($formID, $entryID);
196 - $mailBody = FieldValueHandler::changeImagePathInHTMLString($mailBody, $webUrl);
197 - $mailBody = FieldValueHandler::changeHrefPathInHTMLString($mailBody, $webUrl); // replace anchor tag href with constructed weburl
198 -
199 - // allow developers to modify email body
200 - $mailBody = apply_filters(
201 - 'bitform_filter_email_body',
202 - $mailBody,
203 - [
204 - 'form_id' => $formID,
205 - 'entry_id' => $entryID,
206 - 'field_values' => $fieldValue,
207 - 'template' => $mailTemplate[0] ?? null,
208 - 'is_double_optin' => (bool) $isDblOptin,
209 - ]
210 - );
211 64 if (!empty($notifyDetails->replyto)) {
212 65 $mailReplyTo = FieldValueHandler::validateMailArry($notifyDetails->replyto, $fieldValue);
213 66 if (is_array($mailReplyTo)) {
214 67 foreach ($mailReplyTo as $key => $emailAddress) {
@@ -219,11 +72,11 @@
219 72 }
220 73 }
221 74 $oldMailBody = $mailBody;
222 75 $data = [];
223 - if ($isDblOptin && true === has_filter('bitform_email_body_text')) {
76 + if ($isDblOptin && true === has_filter('bf_email_body_text')) {
224 77 $urlParams = $formID . '_' . $entryID . '_' . $logId;
225 - $data = apply_filters('bitform_email_body_text', $mailBody, $urlParams);
78 + $data = apply_filters('bf_email_body_text', $mailBody, $urlParams);
226 79 $mailBody = $data['mailbody'];
227 80 }
228 81
229 82 if (!empty($notifyDetails->bcc)) {
@@ -245,100 +98,53 @@
245 98 } else {
246 99 $mailHeaders[] = 'Cc: ' . sanitize_email($mailCC);
247 100 }
248 101 }
102 + if (!empty($notifyDetails->from)) {
103 + $mailFrom = FieldValueHandler::validateMailArry($notifyDetails->from, $fieldValue);
104 + $fromName = !empty($notifyDetails->fromName) ? $notifyDetails->fromName : explode('@', $mailFrom[0])[0];
105 + $mailHeaders[] = "FROM: $fromName " . '<' . sanitize_email($mailFrom[0]) . '>';
106 + }
107 +
249 108 if (!empty($notifyDetails->attachment)) {
250 - $fileFldKeys = $notifyDetails->attachment;
251 - $fileBasePath = FileHandler::getEntriesFileUploadDir($formID, $entryID) . DIRECTORY_SEPARATOR;
252 -
253 - // Normalize to array for consistent processing
254 - $fileFldKeys = is_array($fileFldKeys) ? $fileFldKeys : [$fileFldKeys];
255 -
256 - foreach ($fileFldKeys as $fldKey) {
257 - $repeaterFieldKey = $formManager->isRepeatedField($fldKey);
258 -
259 - if ($repeaterFieldKey) {
260 - // Handle repeated file field
261 - FileHandler::processRepeaterAttachment(
262 - $repeaterFieldKey,
263 - $fldKey,
264 - $fieldValue,
265 - $fileBasePath,
266 - $attachments
267 - );
268 - } else {
269 - // Handle regular file field
270 - FileHandler::processRegularAttachment(
271 - $fldKey,
272 - $fieldValue,
273 - $fileBasePath,
274 - $attachments
275 - );
109 + $files = $notifyDetails->attachment;
110 + $fileBasePath = BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID . DIRECTORY_SEPARATOR . $entryID . DIRECTORY_SEPARATOR;
111 + if (is_array($files)) {
112 + foreach ($files as $file) {
113 + if (isset($fieldValue[$file])) {
114 + if (is_array($fieldValue[$file])) {
115 + foreach ($fieldValue[$file] as $singleFile) {
116 + if (\is_readable("{$fileBasePath}{$singleFile}")) {
117 + $attachments[] = "{$fileBasePath}{$singleFile}";
118 + }
119 + }
120 + } elseif (\is_readable("{$fileBasePath}{$fieldValue[$file]}")) {
121 + $attachments[] = "{$fileBasePath}{$fieldValue[$file]}";
122 + }
123 + }
276 124 }
125 + } elseif (isset($fieldValue[$files])) {
126 + if (is_array($fieldValue[$files])) {
127 + foreach ($fieldValue[$files] as $singleFile) {
128 + if (\is_readable("{$fileBasePath}{$singleFile}")) {
129 + $attachments[] = "{$fileBasePath}{$singleFile}";
130 + }
131 + }
132 + } elseif (\is_readable("{$fileBasePath}{$fieldValue[$files]}")) {
133 + $attachments[] = "{$fileBasePath}{$fieldValue[$files]}";
134 + }
277 135 }
278 136 }
279 137 $mailBody = stripcslashes($mailBody);
280 138 $mailSubject = stripcslashes($mailSubject);
281 - $embedCb = static function ($phpmailer) use ($cidMap) {
282 - foreach ($cidMap as $cid => $info) {
283 - try {
284 - $phpmailer->addEmbeddedImage($info['path'], $cid, $info['name']);
285 - } catch (\Throwable $e) {
286 - Log::debug_log("[Signature Embed] failed for {$info['path']} - " . $e->getMessage());
287 - }
288 - }
289 - };
290 - if (!empty($cidMap)) {
291 - add_action('phpmailer_init', $embedCb);
292 - }
293 139 add_filter('wp_mail_content_type', [self::class, 'filterMailContentType']);
294 140 $status = wp_mail($mailTo, $mailSubject, $mailBody, $mailHeaders, $attachments);
295 -
296 141 if (!$status) {
297 - Log::debug_log([
298 - 'status' => 'error',
299 - 'code' => 'mail_not_sent',
300 - 'message' => 'Mail not sent',
301 - 'inputDetails' => [
302 - 'to' => $mailTo,
303 - 'subject' => $mailSubject,
304 - 'body' => $mailBody,
305 - 'headers' => $mailHeaders,
306 - 'attachments' => $attachments,
307 - 'notifyDetails' => $notifyDetails,
308 - 'formID' => $formID,
309 - 'entryID' => $entryID,
310 - 'isDblOptin' => $isDblOptin,
311 - 'logId' => $logId
312 - ],
313 - 'responseDetails' => $status
314 - ]);
315 - $apiResponse->apiResponse($logId, '', ['type' => 'record', 'type_name' => 'smtp'], 'errors', 'Mail dose not send successfully', $entryDetails);
316 - } else {
317 - Log::debug_log([
318 - 'status' => 'success',
319 - 'code' => 'mail_sent',
320 - 'message' => 'Mail successfully sent',
321 - 'inputDetails' => [
322 - 'to' => $mailTo,
323 - 'subject' => $mailSubject,
324 - 'body' => $mailBody,
325 - 'headers' => $mailHeaders,
326 - 'attachments' => $attachments,
327 - 'notifyDetails' => $notifyDetails,
328 - 'formID' => $formID,
329 - 'entryID' => $entryID,
330 - 'isDblOptin' => $isDblOptin,
331 - 'logId' => $logId
332 - ],
333 - 'responseDetails' => $status
334 - ]);
335 - $apiResponse->apiResponse($logId, '', ['type' => 'record', 'type_name' => 'smtp'], 'success', 'Mail successfully send.', $entryDetails);
142 + $status = wp_mail($mailTo, $mailSubject, $mailBody, $mailHeaders);
336 143 }
337 144 if ($status && $isDblOptin && false !== strpos($oldMailBody, 'entry_confirmation_url')) {
338 145 $entryMeta = new FormEntryMetaModel();
339 - $apiResponse->apiResponse($logId, '', ['type' => 'record', 'type_name' => 'smtp'], 'success', 'Mail successfully send.', $entryDetails);
340 - // Form entry meta insert; meta_key/meta_value required to store dynamic field data per entry.
146 +
341 147 $entryMeta->insert(
342 148 [
343 149 'bitforms_form_entry_id' => $entryID,
344 150 'meta_key' => 'entry_confirm_activation',
@@ -346,11 +152,8 @@
346 152 ]
347 153 );
348 154 }
349 155 remove_filter('wp_mail_content_type', [self::class, 'filterMailContentType']);
350 - if (!empty($cidMap)) {
351 - remove_action('phpmailer_init', $embedCb);
352 - }
353 156 }
354 157 }
355 158 }
356 159
@@ -361,66 +164,6 @@
361 164
362 165 public static function filterMailContentType()
363 166 {
364 167 return 'text/html; charset=UTF-8';
365 - }
366 -
367 - /**
368 - * Rewrite signature <img> tags to inline cid: references and collect the files
369 - * to embed. Only signature-field images that are local & readable are embedded;
370 - * external URLs and non-signature images are left untouched. When the form has
371 - * no signature (or none is in the body) $cidMap stays empty and nothing changes.
372 - */
373 - private static function embedSignatureImages($html, $formManager, $fieldValue, $baseDir, &$cidMap)
374 - {
375 - if (empty($html)) {
376 - return $html;
377 - }
378 -
379 - // Collect signature filenames for this entry.
380 - $sigFiles = [];
381 - foreach ($formManager->getFields() as $key => $detail) {
382 - if (!isset($detail['type']) || 'signature' !== $detail['type']) {
383 - continue;
384 - }
385 - $val = isset($fieldValue[$key]) ? $fieldValue[$key] : '';
386 - foreach ((array) $val as $fn) {
387 - $fn = is_string($fn) ? trim($fn) : '';
388 - if ('' !== $fn && 'signature-failed.png' !== $fn) {
389 - $sigFiles[basename($fn)] = true;
390 - }
391 - }
392 - }
393 - if (empty($sigFiles)) {
394 - return $html;
395 - }
396 -
397 - return preg_replace_callback(
398 - '/<img\s+[^>]*src=[\'"]([^\'"]+)[\'"][^>]*>/i',
399 - function ($m) use ($baseDir, $sigFiles, &$cidMap) {
400 - $src = $m[1];
401 - if (filter_var($src, FILTER_VALIDATE_URL)) {
402 - return $m[0]; // external URL, leave as-is
403 - }
404 - $name = basename($src);
405 - if (!isset($sigFiles[$name])) {
406 - return $m[0]; // not a signature image
407 - }
408 - $file = $baseDir . $name;
409 - if (!is_readable($file)) {
410 - return $m[0];
411 - }
412 - $cid = 'bfsig_' . md5($file);
413 - $cidMap[$cid] = ['path' => $file, 'name' => $name];
414 -
415 - // Replace only the src attribute value (leave alt untouched).
416 - return preg_replace(
417 - '/(src=[\'"])' . preg_quote($src, '/') . '([\'"])/i',
418 - '${1}cid:' . $cid . '${2}',
419 - $m[0],
420 - 1
421 - );
422 - },
423 - $html
424 - );
425 168 }
426 169 }