PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 3.3.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v3.3.1
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 -127 3.1.33.3.1 View file →
@@ -9,9 +9,8 @@
9 9 use BitCode\BitForm\Admin\Form\Helpers;
10 10 use BitCode\BitForm\Core\Database\FormEntryMetaModel;
11 11 use BitCode\BitForm\Core\Form\FormManager;
12 12 use BitCode\BitForm\Core\Messages\EmailTemplateHandler;
13 -use BitCode\BitForm\Core\Messages\PdfTemplateHandler;
14 13
15 14 final class MailNotifier
16 15 {
17 16 public static function notify($notifyDetails, $formID, $fieldValue, $entryID, $isDblOptin = false, $logId = '')
@@ -20,134 +19,20 @@
20 19 $formManager = FormManager::getInstance($formID);
21 20 $entryDetails = ['formId' => $formID, 'entryId' => $entryID, 'fieldValues' => $fieldValue];
22 21 $emailTemplateHandler = new EmailTemplateHandler($formID);
23 22 $attachments = [];
24 - $tempPdfLink = '';
25 - $pdfPassForEmail = '';
26 - if (!empty($notifyDetails->pdfId) && is_string($notifyDetails->pdfId)) {
27 - $pdfTemplateID = json_decode($notifyDetails->pdfId)->id;
28 - $pdfTemplateHandler = new PdfTemplateHandler($formID);
29 -
30 - $pdfTemplate = $pdfTemplateHandler->getById($pdfTemplateID);
31 -
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);
50 -
51 - $path = BITFORMS_CONTENT_DIR . DIRECTORY_SEPARATOR . 'pdf';
52 - // $fileName = 'bit-form-pdf-' . $formID . '-' . $entryID;
53 -
54 - if (!is_dir($path)) {
55 - wp_mkdir_p($path);
56 - }
57 -
58 - if (class_exists('\BitCode\BitFormPro\Admin\AppSetting\Pdf')) {
59 - $serverPath = Helpers::getFullPathWithEncryptedEntryId($formID, $entryID);
60 - $webPath = Helpers::getWebPathWithEncryptedEntryId($formID, $entryID);
61 -
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 - }
86 -
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 - }
141 - }
142 - }
143 - }
144 -
23 + $tempPdfLinks = [];
145 24 if (is_string($notifyDetails->id)) {
146 - $mailTemplateID = json_decode($notifyDetails->id)->id;
25 + $mailTemplateID = Utilities::jsonObj($notifyDetails->id)->id ?? null;
147 26 $mailTemplate = $emailTemplateHandler->getATemplate($mailTemplateID);
148 27 if (!is_wp_error($mailTemplate)) {
28 + // Honor template enable/disable: a disabled email template is never sent.
29 + if (isset($mailTemplate[0]->status) && empty($mailTemplate[0]->status)) {
30 + return;
31 + }
149 32 $mailTo = FieldValueHandler::validateMailArry($notifyDetails->to, $fieldValue);
33 + // Conditional email routing (Pro): add recipients resolved from value-based rules in the template config.
34 + $mailTo = apply_filters('bitform_email_conditional_routing', $mailTo, $mailTemplate[0], $fieldValue, $formID);
150 35 if (!empty($mailTo)) {
151 36 $from_name = '';
152 37 if (isset($notifyDetails->from_name) && !empty($notifyDetails->from_name)) {
153 38 $from_name = $notifyDetails->from_name;
@@ -158,14 +43,22 @@
158 43 ];
159 44 $from_mail = '';
160 45 if (!empty($notifyDetails->from)) {
161 46 $fromMail = FieldValueHandler::validateMailArry($notifyDetails->from, $fieldValue);
162 - $headerFromName = !empty($notifyDetails->fromName) ? $notifyDetails->fromName : explode('@', $fromMail[0])[0];
47 + $headerFromName = !empty($notifyDetails->from_name) ? $notifyDetails->from_name : explode('@', $fromMail[0])[0];
163 48 $mailHeaders[] = "FROM: $headerFromName " . '<' . sanitize_email($fromMail[0]) . '>';
164 49 $from_mail = $fromMail[0];
165 50 }
166 51 (new MailConfig())->sendMail(['from_name' => $from_name, 'from_email' => $from_mail]);
167 - $mailSubject = FieldValueHandler::replaceFieldWithValue($mailTemplate[0]->sub, $fieldValue, $formID);
52 + // Translate before smart-tag replacement. One language per submission,
53 + // shared by the admin and submitter emails.
54 + $mailSubjectTemplate = (string) apply_filters(
55 + 'bitform_translate_form_string',
56 + (string) $mailTemplate[0]->sub,
57 + 'mail-sub-' . $mailTemplateID,
58 + $formID
59 + );
60 + $mailSubject = FieldValueHandler::replaceFieldWithValue($mailSubjectTemplate, $fieldValue, $formID);
168 61
169 62 // allow developers to modify email subject
170 63 $mailSubject = apply_filters(
171 64 'bitform_filter_email_subject',
@@ -178,9 +71,14 @@
178 71 'is_double_optin' => (bool) $isDblOptin,
179 72 ]
180 73 );
181 74
182 - $mailBody = $mailTemplate[0]->body;
75 + $mailBody = (string) apply_filters(
76 + 'bitform_translate_form_string',
77 + (string) $mailTemplate[0]->body,
78 + 'mail-body-' . $mailTemplateID,
79 + $formID
80 + );
183 81 if (class_exists('\BitCode\BitFormPro\Admin\DownloadFile')) {
184 82 $downloadFile = new \BitCode\BitFormPro\Admin\DownloadFile();
185 83 $mailBody = $downloadFile->replacePdfShortCodeToLink($mailBody, $formID, $entryID);
186 84 $mailBody = $downloadFile->replaceShortCodeToPdfPassword($mailBody, $formID, $entryID);
@@ -245,8 +143,19 @@
245 143 } else {
246 144 $mailHeaders[] = 'Cc: ' . sanitize_email($mailCC);
247 145 }
248 146 }
147 + // PDF generation is a Pro feature; Pro hooks this filter and returns the generated
148 + // file path(s) — an array (one per configured template), or a single path string
149 + // from Pro versions that predate multi-PDF. Generated here (not before the
150 + // template/recipient checks) so disabled templates and empty recipients never
151 + // generate PDFs — and never leak temp files.
152 + $tempPdfLinks = apply_filters('bitform_generate_pdf_attachment', [], $notifyDetails, $entryDetails, $logId);
153 + $tempPdfLinks = is_array($tempPdfLinks) ? $tempPdfLinks : [$tempPdfLinks];
154 + $tempPdfLinks = array_values(array_unique(array_filter($tempPdfLinks, static function ($pdfPath) {
155 + return !empty($pdfPath) && is_string($pdfPath) && file_exists($pdfPath);
156 + })));
157 + $attachments = array_merge($attachments, $tempPdfLinks);
249 158 if (!empty($notifyDetails->attachment)) {
250 159 $fileFldKeys = $notifyDetails->attachment;
251 160 $fileBasePath = FileHandler::getEntriesFileUploadDir($formID, $entryID) . DIRECTORY_SEPARATOR;
252 161
@@ -275,8 +184,18 @@
275 184 );
276 185 }
277 186 }
278 187 }
188 + // WordPress Media Library attachments (stored as attachment IDs in the template config).
189 + if (!empty($notifyDetails->mediaAttachment)) {
190 + $mediaIds = is_array($notifyDetails->mediaAttachment) ? $notifyDetails->mediaAttachment : [$notifyDetails->mediaAttachment];
191 + foreach ($mediaIds as $mediaId) {
192 + $mediaPath = get_attached_file(absint($mediaId));
193 + if ($mediaPath && file_exists($mediaPath)) {
194 + $attachments[] = $mediaPath;
195 + }
196 + }
197 + }
279 198 $mailBody = stripcslashes($mailBody);
280 199 $mailSubject = stripcslashes($mailSubject);
281 200 $embedCb = static function ($phpmailer) use ($cidMap) {
282 201 foreach ($cidMap as $cid => $info) {
@@ -290,8 +209,15 @@
290 209 if (!empty($cidMap)) {
291 210 add_action('phpmailer_init', $embedCb);
292 211 }
293 212 add_filter('wp_mail_content_type', [self::class, 'filterMailContentType']);
213 + $fromNameCb = null;
214 + if (!empty($from_name)) {
215 + $fromNameCb = static function () use ($from_name) {
216 + return $from_name;
217 + };
218 + add_filter('wp_mail_from_name', $fromNameCb);
219 + }
294 220 $status = wp_mail($mailTo, $mailSubject, $mailBody, $mailHeaders, $attachments);
295 221
296 222 if (!$status) {
297 223 Log::debug_log([
@@ -346,8 +272,11 @@
346 272 ]
347 273 );
348 274 }
349 275 remove_filter('wp_mail_content_type', [self::class, 'filterMailContentType']);
276 + if (null !== $fromNameCb) {
277 + remove_filter('wp_mail_from_name', $fromNameCb);
278 + }
350 279 if (!empty($cidMap)) {
351 280 remove_action('phpmailer_init', $embedCb);
352 281 }
353 282 }
@@ -353,9 +282,9 @@
353 282 }
354 283 }
355 284 }
356 285
357 - if (!empty($tempPdfLink)) {
286 + foreach ($tempPdfLinks as $tempPdfLink) {
358 287 wp_delete_file($tempPdfLink);
359 288 }
360 289 }
361 290