PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.5.3
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.5.3
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
fluent-cart / app / Services / Email / EmailNotificationMailer.php

EmailNotificationMailer.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.5.3, at app/Services/Email/EmailNotificationMailer.php

373 lines 13.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\Email;
4
5 use FluentCart\App\App;
6 use FluentCart\App\Models\Model;
7 use FluentCart\App\Models\Order;
8 use FluentCart\App\Models\Subscription;
9 use FluentCart\App\Services\OrderService;
10 use FluentCart\App\Services\ShortCodeParser\ShortcodeTemplateBuilder;
11 use FluentCart\Framework\Support\Arr;
12
13 class EmailNotificationMailer
14 {
15 public function register()
16 {
17 // $this->registerAsyncMails();
18 add_action('fluent_cart/order_placed_offline', function ($data) {
19 $this->mailEmailsOfEvent(
20 'order_placed_offline',
21 $data
22 );
23
24 }, 999, 1);
25 // To Customer
26 add_action('fluent_cart/order_paid', function ($data) {
27 $this->mailEmailsOfEvent(
28 'order_paid',
29 $data
30 );
31
32 }, 999, 1);
33 // To Admin
34 add_action('fluent_cart/order_paid_done', function ($data) {
35 $this->mailEmailsOfEvent(
36 'order_paid_done',
37 $data
38 );
39 }, 10, 1);
40
41 // to customer and admin
42 add_action('fluent_cart/subscription_renewed', function ($data) {
43 $this->mailEmailsOfEvent(
44 'subscription_renewed',
45 $data
46 );
47 }, 999, 1);
48
49 // to customer and admin
50 add_action('fluent_cart/subscription_canceled', function ($data) {
51 $this->mailEmailsOfEvent(
52 'subscription_canceled',
53 $data
54 );
55 }, 999, 1);
56
57 add_action('fluent_cart/order_refunded', function ($data) {
58 $this->mailEmailsOfEvent('order_refunded', $data);
59 }, 999, 1);
60
61 add_action('fluent_cart/shipping_status_changed_to_shipped', function ($data) {
62 $this->mailEmailsOfEvent('shipping_status_changed_to_shipped', $data);
63 }, 999, 1);
64
65 add_action('fluent_cart/shipping_status_changed_to_delivered', function ($data) {
66 $this->mailEmailsOfEvent('shipping_status_changed_to_delivered', $data);
67 }, 999, 1);
68
69 // @todo uncomment when invoice feature is deployed
70 // add_action('fluent_cart/invoice_reminder_due', function ($data) {
71 // $this->mailEmailsOfEvent('invoice_reminder_due', $data);
72 // }, 999, 1);
73
74 add_action('fluent_cart/invoice_reminder_overdue', function ($data) {
75 $this->mailEmailsOfEvent('invoice_reminder_overdue', $data);
76 }, 999, 1);
77
78 add_action('fluent_cart/subscription_renewal_reminder', function ($data) {
79 $this->mailEmailsOfEvent('subscription_renewal_reminder', $data);
80 }, 999, 1);
81
82 add_action('fluent_cart/subscription_trial_end_reminder', function ($data) {
83 $this->mailEmailsOfEvent('subscription_trial_end_reminder', $data);
84 }, 999, 1);
85
86 }
87
88 public function registerAsyncMails()
89 {
90 //For Async Actions
91 add_action('fluent_cart/async_mail/order_created', function ($orderId, $mailName = '') {
92 (new static())->sendAsyncOrderMail($mailName, $orderId);
93 }, 10, 2);
94
95 add_action('fluent_cart/async_mail/order_placed_offline', function ($orderId, $mailName = '') {
96 (new static())->sendAsyncOrderMail($mailName, $orderId);
97 }, 10, 2);
98
99 add_action('fluent_cart/async_mail/order_paid', function ($orderId, $mailName = '') {
100 (new static())->sendAsyncOrderMail($mailName, $orderId);
101 }, 10, 2);
102
103 add_action('fluent_cart/async_mail/order_updated', function ($orderId, $mailName = '') {
104 (new static())->sendAsyncOrderMail($mailName, $orderId);
105 }, 10, 2);
106
107 add_action('fluent_cart/async_mail/order_refunded', function ($orderId, $mailName = '') {
108 (new static())->sendAsyncOrderMail($mailName, $orderId);
109 }, 10, 2);
110
111 add_action('fluent_cart/async_mail/subscription_activated', function ($subscriptionId, $mailName = '') {
112 (new static())->sendAsyncSubscriptionMail($mailName, $subscriptionId);
113 }, 10, 2);
114
115 add_action('fluent_cart/async_mail/subscription_reactivated', function ($subscriptionId, $mailName = '') {
116 (new static())->sendAsyncSubscriptionMail($mailName, $subscriptionId);
117 }, 10, 2);
118
119 add_action('fluent_cart/async_mail/subscription_renewed', function ($subscriptionId, $mailName = '') {
120 (new static())->sendAsyncSubscriptionMail($mailName, $subscriptionId);
121 }, 10, 2);
122
123 add_action('fluent_cart/async_mail/subscription_eot', function ($subscriptionId, $mailName = '') {
124 (new static())->sendAsyncSubscriptionMail($mailName, $subscriptionId);
125 }, 10, 2);
126
127 add_action('fluent_cart/async_mail/subscription_canceled', function ($subscriptionId, $mailName = '') {
128 (new static())->sendAsyncSubscriptionMail($mailName, $subscriptionId);
129 }, 10, 2);
130
131 add_action('fluent_cart/async_mail/subscription_expired', function ($subscriptionId, $mailName = '') {
132 (new static())->sendAsyncSubscriptionMail($mailName, $subscriptionId);
133 }, 10, 2);
134
135 }
136
137
138 public function formatParsable($parsable)
139 {
140 foreach ($parsable as &$item) {
141 if ($item instanceof Model) {
142 $item = $item->toArray();
143 }
144 }
145
146 if (!Arr::has($parsable, 'order.customer')) {
147 $parsable['order']['customer'] = Arr::get(
148 $parsable, 'customer', []
149 );
150 }
151
152 return $parsable;
153 }
154
155 public function mailEmailsOfEvent($event, $data, $asyncHook = '', $asyncData = [])
156 {
157 $parsedData = $this->formatParsable($data);
158
159 $order = Arr::get($data, 'order');
160
161 // Pass original Model data for template rendering (templates use $order->method())
162 $notifications = EmailNotifications::getNotificationsOfEvent($event, $data);
163
164 foreach ($notifications as $mailName => $notification) {
165 if ($order instanceof Order && !apply_filters('fluent_cart/should_send_email_notification', true, [
166 'event' => $event,
167 'mail_name' => $mailName,
168 'order' => $order,
169 ])) {
170 continue;
171 }
172
173 $isAsync = Arr::get($notification, 'is_async', false);
174 if ($isAsync && !empty($asyncHook)) {
175 $asyncData['mailName'] = $mailName;
176 as_enqueue_async_action($asyncHook, $asyncData);
177 } else {
178 list($body, $subject, $to) = $this->parseEmailContent($notification, $data);
179
180 $mailer = Mailer::make()->to($to)->subject($subject)->body($body);
181
182 $pdfPath = null;
183 $templateId = Arr::get($notification, 'settings.attach_pdf_template', '');
184 // Backward compat: old attach_pdf='yes' maps to order_receipt
185 if (empty($templateId) && Arr::get($notification, 'settings.attach_pdf', 'no') === 'yes') {
186 $templateId = 'order_receipt';
187 }
188 if (!empty($templateId) && defined('FLUENT_PDF')) {
189 $pdfPath = $this->generateOrderPdf($data, $templateId);
190 if ($pdfPath) {
191 $mailer->addAttachment($pdfPath);
192 }
193 }
194
195 $mailer->send(true);
196
197 // Clean up temp PDF file after sending
198 if ($pdfPath && file_exists($pdfPath)) {
199 @unlink($pdfPath);
200 }
201 }
202 }
203
204 }
205
206 public function mailByEmailName($emailName, $data)
207 {
208 // Extract Order model before formatParsable converts it to array
209 $orderModel = Arr::get($data, 'order');
210
211 $data = $this->formatParsable($data);
212 $notification = EmailNotifications::getNotification($emailName);
213 $notification = EmailNotifications::formatNotification($notification, $data);
214 list($body, $subject, $to) = $this->parseEmailContent($notification, $data);
215
216 $mailer = Mailer::make()->to($to)->subject($subject)->body($body);
217
218 $pdfPath = null;
219 $templateId = Arr::get($notification, 'settings.attach_pdf_template', '');
220 if (empty($templateId) && Arr::get($notification, 'settings.attach_pdf', 'no') === 'yes') {
221 $templateId = 'order_receipt';
222 }
223 if (!empty($templateId) && defined('FLUENT_PDF')) {
224 $pdfPath = $this->generateOrderPdf(['order' => $orderModel], $templateId);
225 if ($pdfPath) {
226 $mailer->addAttachment($pdfPath);
227 }
228 }
229
230 $mailer->send(true);
231
232 if ($pdfPath && file_exists($pdfPath)) {
233 @unlink($pdfPath);
234 }
235 }
236
237 public function getEmailFooter(): string
238 {
239
240 $footer = "";
241 $settings = EmailNotifications::getSettings();
242 $emailFooter = Arr::get($settings, 'email_footer', '');
243 if (!empty($emailFooter)) {
244 $footer .= ShortcodeTemplateBuilder::make($emailFooter, []);
245 }
246 $isEmailFooter = EmailNotifications::getSettings('show_email_footer');
247 if (!App::isProActive() || $isEmailFooter === 'yes') {
248 $cartFooter = "<div style='padding: 15px; text-align: center; font-size: 16px; color: #2F3448;'>Powered by <a href='https://fluentcart.com' style='color: #017EF3; text-decoration: none;'>FluentCart</a></div>";
249 $footer .= $cartFooter;
250 }
251 return $footer;
252
253 }
254
255 public function parseEmailContent($notification, $data, $templateData = null): array
256 {
257 $templateData = $templateData ?: $data;
258
259 $rawBody = Arr::get($notification, 'body', '');
260 $isCustom = (bool) Arr::get($notification, 'is_custom', false);
261
262 // Let pro parse block content; returns empty string if no pro
263 $parsedBody = apply_filters('fluent_cart/parse_email_block_content', '', $rawBody, $data);
264
265 $body = '';
266
267 // Custom block emails use block_editor_template (pro);
268 // default templates use general_template with legacy order_header.
269 if ($isCustom && $parsedBody) {
270 $body = apply_filters('fluent_cart/render_block_email_template', '', [
271 'emailBody' => $parsedBody,
272 'preheader' => Arr::get($notification, 'pre_header', ''),
273 'emailFooter' => $this->getEmailFooter(),
274 ]);
275 }
276
277 if (empty($body)) {
278 $header = App::make('view')->make('emails.parts.order_header', $data);
279 $body = (string)App::make('view')->make('emails.general_template', [
280 'emailBody' => $rawBody,
281 'preheader' => Arr::get($notification, 'pre_header', ''),
282 'header' => $header,
283 'emailFooter' => $this->getEmailFooter(),
284 ]);
285 }
286
287 $body = ShortcodeTemplateBuilder::make($body, $data);
288
289 $subject = ShortcodeTemplateBuilder::make(Arr::get($notification, 'subject', ''), $data);
290 $to = Arr::get($notification, 'to', '');
291 $to = ShortcodeTemplateBuilder::make($to, $data);
292
293 return [
294 0 => $body,
295 1 => $subject,
296 2 => $to
297 ];
298 }
299
300 public function sendAsyncOrderMail($emailName, $orderId)
301 {
302 $order = Order::query()->with(['customer', 'shipping_address', 'billing_address', 'transactions', 'orderTaxRates'])->find($orderId);
303
304 if ($order) {
305 $transaction = [];
306 if (!empty($order->transactions)) {
307 $transaction = $order->transactions->first();
308 }
309 $this->mailByEmailName($emailName, [
310 'order' => $order,
311 'customer' => $order->customer !== null ? $order->customer : [],
312 'transaction' => $transaction
313 ]);
314 }
315 }
316
317 public function sendAsyncSubscriptionMail($emailName, $subscriptionId)
318 {
319 $subscription = Subscription::query()->with([
320 'customer',
321 'transactions',
322 'order'
323 ])->find($subscriptionId);
324
325 if ($subscription) {
326 $order = $subscription->order;
327 if (!$order) {
328 return;
329 }
330 $this->mailByEmailName($emailName, [
331 'subscription' => $subscription,
332 'order' => $order,
333 'customer' => $subscription->customer !== null ? $subscription->customer : [],
334 'transactions' => $subscription->transactions
335 ]);
336 }
337
338 }
339
340 /**
341 * Generate a PDF receipt for the order in the given data array.
342 *
343 * @param array $data Event data containing 'order' key
344 * @return string|null Path to generated PDF or null
345 */
346 private function generateOrderPdf(array $data, string $templateId = 'order_receipt'): ?string
347 {
348 if (!OrderService::canGenerateReceiptPdf()) {
349 return null;
350 }
351
352 $order = Arr::get($data, 'order');
353 if (!$order || !($order instanceof Order)) {
354 return null;
355 }
356
357 try {
358 return apply_filters('fluent_cart/pdf/generate_receipt', null, [
359 'order' => $order,
360 'template_id' => $templateId,
361 ]);
362 } catch (\Throwable $e) {
363 fluent_cart_add_log(
364 'PDF Generation Failed',
365 $e->getMessage(),
366 'error'
367 );
368 return null;
369 }
370 }
371
372 }
373