PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
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 1.2.0 All 47 releases
fluent-cart / app / Http / Controllers / EmailNotificationController.php

EmailNotificationController.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.4, at app/Http/Controllers/EmailNotificationController.php

342 lines 12.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\Http\Controllers;
4
5 use FluentCart\App\App;
6 use FluentCart\Api\StoreSettings;
7 use FluentCart\App\Helpers\EditorShortCodeHelper;
8 use FluentCart\App\Http\Requests\EmailNotificationRequest;
9 use FluentCart\App\Http\Requests\EmailSettingsRequest;
10 use FluentCart\App\Http\Requests\SchedulingSettingsRequest;
11 use FluentCart\App\Services\Email\EmailNotificationMailer;
12 use FluentCart\App\Services\Email\EmailNotifications;
13 use FluentCart\App\Services\Email\StoreDigestService;
14 use FluentCart\App\Services\PDF\ReceiptPdfTemplateService;
15 use FluentCart\App\Services\Reminders\ReminderService;
16 use FluentCart\App\Services\ShortCodeParser\ShortcodeTemplateBuilder;
17 use FluentCart\App\Services\Email\EmailPreviewService;
18 use FluentCart\App\Services\TemplateService;
19 use FluentCart\Framework\Http\Request\Request;
20 use FluentCart\Framework\Support\Arr;
21 use FluentCart\Framework\Support\Str;
22
23 class EmailNotificationController extends Controller
24 {
25 public function index(Request $request): \WP_REST_Response
26 {
27
28 $getNotifications = EmailNotifications::getNotifications();
29
30 if ($getNotifications) {
31 return $this->sendSuccess([
32 'data' => $getNotifications
33 ]);
34 }
35 return $this->sendError([
36 'data' => []
37 ]);
38 }
39
40 public function find($notification): \WP_REST_Response
41 {
42 $name = sanitize_text_field($notification);
43 $notification = EmailNotifications::getNotification($name);
44
45
46 if ($notification) {
47 $hasFluentPdf = defined('FLUENT_PDF');
48 $fontsReady = false;
49 $setupUrl = '';
50
51 if ($hasFluentPdf && class_exists('FluentPdf\Classes\Controller\FontDownloader')) {
52 $fontDownloader = new \FluentPdf\Classes\Controller\FontDownloader();
53 $fontsReady = empty($fontDownloader->getDownloadableFonts());
54 $setupUrl = admin_url('admin.php?page=fluent_pdf.php');
55 }
56
57 $pdfTemplates = $hasFluentPdf ? (new ReceiptPdfTemplateService())->getTemplateList() : [];
58
59 // Let add-ons inject their custom field data (settings.extra / extra_fields)
60 // into the editor payload from their own storage.
61 $notification = apply_filters('fluent_cart/email_notification_data', $notification, $name);
62
63 return $this->sendSuccess([
64 'data' => $notification,
65 'shortcodes' => EditorShortCodeHelper::getEmailNotificationShortcodes(),
66 'has_fluent_pdf' => $hasFluentPdf,
67 'fonts_ready' => $fontsReady,
68 'setup_url' => $setupUrl,
69 'pdf_templates' => $pdfTemplates,
70 ]);
71 }
72
73 return $this->sendError([
74 'message' => __('Notification Details not found', 'fluent-cart')
75 ]);
76 }
77
78 public function update(EmailNotificationRequest $request, $notification): \WP_REST_Response
79 {
80 $data = $request->getSafe($request->sanitize());
81
82 $settings = Arr::get($data, 'settings', []);
83
84 // Strip custom template data in free; pro filter restores it
85 $settingsWithoutTemplate = $settings;
86 unset($settingsWithoutTemplate['email_body']);
87 $settingsWithoutTemplate['is_default_body'] = 'yes';
88
89 $settings = apply_filters('fluent_cart/prepare_email_template_data', $settingsWithoutTemplate, $settings);
90
91 $updated = EmailNotifications::updateNotification($notification, $settings);
92 if ($updated) {
93 // Core does not persist custom fields (settings.extra). It fires this after a
94 // successful update so add-ons can store their own data in their own storage.
95 do_action('fluent_cart/email_notification_updated', $notification, $settings);
96
97 return $this->sendSuccess([
98 'message' => __('Notification updated successfully', 'fluent-cart')
99 ]);
100 } else {
101 return $this->sendError([
102 'message' => __('Failed to update notification', 'fluent-cart')
103 ]);
104 }
105
106 }
107
108 public function enableNotification(Request $request, $name): \WP_REST_Response
109 {
110 $enabledValue = sanitize_text_field(Arr::get($request->all(), 'active'));
111
112 $notification = EmailNotifications::updateNotification(
113 $name,
114 ['active' => $enabledValue]
115 );
116
117 if ($notification) {
118 return $this->sendSuccess([
119 'message' => __('Notification updated successfully', 'fluent-cart')
120 ]);
121 }
122 return $this->sendError([
123 'message' => __('Failed to update notification', 'fluent-cart')
124 ]);
125
126 }
127
128 public function getShortCodes(Request $request): \WP_REST_Response
129 {
130 return $this->sendSuccess([
131 'data' => [
132 'email_templates' => $this->getTemplateFiles(),
133 'shortcodes' => EditorShortCodeHelper::getEmailNotificationShortcodes(),
134 'buttons' => EditorShortCodeHelper::getButtons()
135 ],
136 ]);
137 }
138
139 public function previewDefaultTemplate(Request $request)
140 {
141 $template = sanitize_text_field($request->get('template'));
142
143 // Validate the template against the known notification template paths so an
144 // invalid or missing value returns a clean REST error instead of an uncaught
145 // "view not found" fatal from the template renderer.
146 $validTemplates = array_filter(array_column(
147 EmailNotifications::getNotifications(), 'template_path'
148 ));
149
150 if (!$template || !in_array($template, $validTemplates, true)) {
151 return $this->sendError([
152 'message' => __('Invalid or missing "template" parameter.', 'fluent-cart')
153 ], 422);
154 }
155
156 $previewService = new EmailPreviewService();
157 $data = $previewService->getPreviewData($template);
158
159 // Let add-ons adjust the preview data for their own templates — e.g. unset
160 // `order` so an orderless notification (wishlist, withdrawal, …) previews
161 // without the order header (emails.parts.order_header renders only when
162 // $order is non-empty), or add the sample context their smartcodes need.
163 // Core templates are untouched: no core listener changes $data. The context
164 // is an array so more keys can be added later without changing the signature.
165 $data = apply_filters('fluent_cart/email/preview_data', $data, ['template' => $template]);
166
167 $body = TemplateService::getTemplateByPathName($template, $data);
168
169 // Wrap in the same outer email template used by actual emails
170 $header = (string) App::make('view')->make('emails.parts.order_header', $data);
171 $mailer = new EmailNotificationMailer();
172 $view = (string) App::make('view')->make('emails.general_template', [
173 'emailBody' => $body,
174 'preheader' => '',
175 'header' => $header,
176 'emailFooter' => $mailer->getEmailFooter(),
177 ]);
178
179 // Resolve shortcodes (e.g., {{ settings.store_brand }}, {{order.created_at}})
180 $view = ShortcodeTemplateBuilder::make($view, $data);
181
182 // Disable all links/buttons in the preview so they are not clickable
183 $view .= '<style>a, button, [role="button"] { pointer-events: none !important; cursor: default !important; }</style>';
184
185 return $this->sendSuccess([
186 'data' => [
187 'content' => $view
188 ],
189 ]);
190 }
191
192 public function getTemplateFiles()
193 {
194 $defaultFilePath = FLUENTCART_PLUGIN_PATH . '/app/Views/emails';
195 $filesArray = [];
196 $files = scandir($defaultFilePath);
197 foreach ($files as $file) {
198 $filePath = $defaultFilePath . '/' . $file;
199 if (is_file($filePath)) {
200 $file = Str::of($file)->replace('.php', '');
201 $filesArray[] = [
202 'path' => $file,
203 'label' => Str::of($file)->replace('fluent_cart', '')->headline()
204 ];
205 }
206 }
207 return $filesArray;
208 }
209
210 public function getSettings(): \WP_REST_Response
211 {
212 return $this->sendSuccess([
213 'data' => EmailNotifications::getSettings(),
214 'shortcodes' => EditorShortCodeHelper::getEmailSettingsShortcodes(),
215 ]);
216 }
217
218 public function saveSettings(EmailSettingsRequest $request): \WP_REST_Response
219 {
220 $data = $request->getSafe($request->sanitize());
221
222 if (!App::isProActive()) {
223 $data['show_email_footer'] = 'yes';
224 }
225
226 $updated = EmailNotifications::updateSettings($data);
227
228 if ($updated) {
229 return $this->sendSuccess([
230 'message' => __('Email settings saved successfully', 'fluent-cart')
231 ]);
232 } else {
233 return $this->sendError([
234 'message' => __('Failed to save email settings', 'fluent-cart')
235 ]);
236 }
237 }
238
239 public function getDigestSettings(): \WP_REST_Response
240 {
241 return $this->sendSuccess([
242 'data' => StoreDigestService::getSettings(),
243 ]);
244 }
245
246 public function saveDigestSettings(Request $request): \WP_REST_Response
247 {
248 $saved = StoreDigestService::saveSettings($request->all());
249
250 return $this->sendSuccess([
251 'data' => $saved,
252 'message' => __('Store digest settings saved successfully', 'fluent-cart'),
253 ]);
254 }
255
256 public function sendDigestTest(Request $request): \WP_REST_Response
257 {
258 $frequency = sanitize_text_field(Arr::get($request->all(), 'frequency', 'daily'));
259 if (!in_array($frequency, StoreDigestService::CADENCES, true)) {
260 $frequency = 'daily';
261 }
262
263 // Send the test to the configured recipients, falling back to the WP admin email.
264 $recipients = StoreDigestService::getSettings('recipients');
265 if (empty($recipients)) {
266 $recipients = get_bloginfo('admin_email');
267 }
268
269 $sent = StoreDigestService::sendDigest($frequency, (string) $recipients);
270
271 if ($sent) {
272 return $this->sendSuccess([
273 'message' => __('Test digest email sent.', 'fluent-cart'),
274 ]);
275 }
276
277 return $this->sendError([
278 'message' => __('Could not send the test email. Please check your mailing settings and recipients.', 'fluent-cart'),
279 ]);
280 }
281
282 public function getSchedulingSettings(StoreSettings $storeSettings): array
283 {
284 $tab = 'reminders';
285 $currentTabFields = Arr::get($storeSettings->fields(), 'setting_tabs.schema.' . $tab);
286
287 return [
288 'settings' => $storeSettings->get(),
289 'fields' => [
290 $tab => $currentTabFields
291 ]
292 ];
293 }
294
295 public function saveSchedulingSettings(SchedulingSettingsRequest $request, StoreSettings $storeSettings): \WP_REST_Response
296 {
297 try {
298 $sanitizedData = $request->getSafe($request->sanitize());
299
300 $data = array_merge(
301 $storeSettings->get(),
302 $sanitizedData
303 );
304
305 $updated = $storeSettings->save($data);
306
307 return $this->sendSuccess([
308 'data' => $updated,
309 'message' => __('Scheduling settings saved successfully', 'fluent-cart')
310 ]);
311 } catch (\Throwable $e) {
312 return $this->sendError([
313 'message' => __('Failed to save scheduling settings', 'fluent-cart')
314 ], 423);
315 }
316 }
317
318 public function sendManualReminder(Request $request): \WP_REST_Response
319 {
320 $event = sanitize_text_field(Arr::get($request->all(), 'event', ''));
321 $entityId = absint(Arr::get($request->all(), 'entity_id', 0));
322
323 if (empty($event) || empty($entityId)) {
324 return $this->sendError([
325 'message' => __('Event type and entity ID are required', 'fluent-cart')
326 ]);
327 }
328
329 $result = (new ReminderService())->sendManualReminder($event, $entityId);
330
331 if ($result['success']) {
332 return $this->sendSuccess([
333 'message' => $result['message']
334 ]);
335 }
336
337 return $this->sendError([
338 'message' => $result['message']
339 ]);
340 }
341 }
342