FluentSMTP – WP Mail SMTP, Amazon SES, SendGrid, MailGun and Any SMTP Connector Plugin - Version 2.0.0

Version Description

(Date: July 27, 2021) = * Added Outlook / Office 365 API Connection * Improvements of Amazon SES Connection * Ability to disable force From Email for supported connections * Added Fallback Connection feature * Added One-Click migration from WP Mail SMTP Plugin * Added One-Click migration from WP Easy SMTP Plugin * UI Improvements * Added nonce and sanitization for connection inputs

Download this release

Release Info

Developer techjewel
Plugin Icon FluentSMTP – WP Mail SMTP, Amazon SES, SendGrid, MailGun and Any SMTP Connector Plugin
Version 2.0.0
Comparing to
See all releases

Code changes from version 1.2.0 to 2.0.0

app/Bindings.php CHANGED
@@ -11,6 +11,7 @@ $singletons = [
11
  'default' => 'FluentMail\App\Services\Mailer\Providers\DefaultMail\Handler',
12
  'sendinblue' => 'FluentMail\App\Services\Mailer\Providers\SendInBlue\Handler',
13
  'gmail' => 'FluentMail\App\Services\Mailer\Providers\Gmail\Handler',
 
14
  ];
15
 
16
  foreach ($singletons as $key => $className) {
11
  'default' => 'FluentMail\App\Services\Mailer\Providers\DefaultMail\Handler',
12
  'sendinblue' => 'FluentMail\App\Services\Mailer\Providers\SendInBlue\Handler',
13
  'gmail' => 'FluentMail\App\Services\Mailer\Providers\Gmail\Handler',
14
+ 'outlook' => 'FluentMail\App\Services\Mailer\Providers\Outlook\Handler',
15
  ];
16
 
17
  foreach ($singletons as $key => $className) {
app/Functions/helpers.php CHANGED
@@ -159,7 +159,7 @@ if (!function_exists('fluentMailGetProvider')) {
159
 
160
  if(!$connection) {
161
  $connection = fluentMailDefaultConnection();
162
- if($connection) {
163
  $connection['force_from_email_id'] = $connection['sender_email'];
164
  }
165
  }
159
 
160
  if(!$connection) {
161
  $connection = fluentMailDefaultConnection();
162
+ if($connection && \FluentMail\Includes\Support\Arr::get($connection, 'force_from_email') != 'no') {
163
  $connection['force_from_email_id'] = $connection['sender_email'];
164
  }
165
  }
app/Functions/pluggable.php CHANGED
@@ -40,6 +40,33 @@ if (! function_exists( 'wp_mail' ) ) :
40
  $atts = apply_filters(
41
  'wp_mail', compact('to', 'subject', 'message', 'headers', 'attachments')
42
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  if ( isset( $atts['to'] ) ) {
45
  $to = $atts['to'];
@@ -200,6 +227,7 @@ if (! function_exists( 'wp_mail' ) ) :
200
  $defaultConnection = false;
201
  if ( ! isset( $from_email ) ) {
202
  $defaultConnection = fluentMailDefaultConnection();
 
203
  if (!empty($defaultConnection['sender_email'])) {
204
  $from_email = $defaultConnection['sender_email'];
205
  } else {
40
  $atts = apply_filters(
41
  'wp_mail', compact('to', 'subject', 'message', 'headers', 'attachments')
42
  );
43
+
44
+ /**
45
+ * Filters whether to preempt sending an email.
46
+ *
47
+ * Returning a non-null value will short-circuit {@see wp_mail()}, returning
48
+ * that value instead. A boolean return value should be used to indicate whether
49
+ * the email was successfully sent.
50
+ *
51
+ * @since 5.7.0
52
+ *
53
+ * @param null|bool $return Short-circuit return value.
54
+ * @param array $atts {
55
+ * Array of the `wp_mail()` arguments.
56
+ *
57
+ * @type string|string[] $to Array or comma-separated list of email addresses to send message.
58
+ * @type string $subject Email subject.
59
+ * @type string $message Message contents.
60
+ * @type string|string[] $headers Additional headers.
61
+ * @type string|string[] $attachments Paths to files to attach.
62
+ * }
63
+ */
64
+ $pre_wp_mail = apply_filters( 'pre_wp_mail', null, $atts );
65
+
66
+ if ( null !== $pre_wp_mail ) {
67
+ return $pre_wp_mail;
68
+ }
69
+
70
 
71
  if ( isset( $atts['to'] ) ) {
72
  $to = $atts['to'];
227
  $defaultConnection = false;
228
  if ( ! isset( $from_email ) ) {
229
  $defaultConnection = fluentMailDefaultConnection();
230
+
231
  if (!empty($defaultConnection['sender_email'])) {
232
  $from_email = $defaultConnection['sender_email'];
233
  } else {
app/Hooks/Handlers/AdminMenuHandler.php CHANGED
@@ -2,9 +2,11 @@
2
 
3
  namespace FluentMail\App\Hooks\Handlers;
4
 
5
- use FluentMail\App\Models\Settings;
 
6
  use FluentMail\Includes\Core\Application;
7
  use FluentMail\App\Services\Mailer\Manager;
 
8
 
9
  class AdminMenuHandler
10
  {
@@ -25,6 +27,8 @@ class AdminMenuHandler
25
 
26
  add_action('admin_bar_menu', array($this, 'addSimulationBar'), 999);
27
 
 
 
28
  }
29
 
30
  public function addMenu()
@@ -40,6 +44,23 @@ class AdminMenuHandler
40
  [$this, 'renderApp'],
41
  16
42
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  }
44
 
45
  public function renderApp()
@@ -96,18 +117,30 @@ class AdminMenuHandler
96
 
97
  $user = get_user_by('ID', get_current_user_id());
98
 
 
 
 
 
 
 
 
 
 
99
  wp_localize_script('fluent_mail_admin_app_boot', 'FluentMailAdmin', [
100
  'slug' => FLUENTMAIL,
101
  'brand_logo' => esc_url(fluentMailMix('images/logo.svg')),
102
  'nonce' => wp_create_nonce(FLUENTMAIL),
103
- 'settings' => $this->getMailerSettings(),
104
  'has_fluentcrm' => defined('FLUENTCRM'),
105
  'has_fluentform' => defined('FLUENTFORM'),
106
  'user_email' => $user->user_email,
107
  'require_optin' => $this->isRequireOptin(),
108
  'has_ninja_tables' => defined('NINJA_TABLES_VERSION'),
109
  'disable_recommendation' => apply_filters('fluentmail_disable_recommendation', false),
110
- 'plugin_url' => 'https://fluentsmtp.com/?utm_source=wp&utm_medium=install&utm_campaign=dashboard'
 
 
 
111
  ]);
112
 
113
  do_action('fluent_mail_loading_app');
@@ -133,6 +166,16 @@ class AdminMenuHandler
133
  {
134
  $settings = $this->app->make(Manager::class)->getMailerConfigAndSettings(true);
135
 
 
 
 
 
 
 
 
 
 
 
136
  $settings = array_merge(
137
  $settings,
138
  [
@@ -186,7 +229,7 @@ class AdminMenuHandler
186
 
187
  $misc = $this->app->make(Manager::class)->getConfig('misc');
188
 
189
- if(!empty($misc['simulate_emails']) && $misc['simulate_emails'] =='yes' ) {
190
  $args = [
191
  'parent' => 'top-secondary',
192
  'id' => 'fluentsmtp_simulated',
@@ -204,15 +247,199 @@ class AdminMenuHandler
204
  public function isRequireOptin()
205
  {
206
  $opted = get_option('_fluentsmtp_sub_update');
207
- if($opted) {
208
  return 'no';
209
  }
210
  // check if dismissed
211
- $dismissedStamp = get_option('_fluentsmtp_dismissed_timestamp');
212
- if($dismissedStamp && (time() - $dismissedStamp) < 30 * 24 * 60 * 60) {
213
  return 'no';
214
  }
215
 
216
  return 'yes';
217
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  }
2
 
3
  namespace FluentMail\App\Hooks\Handlers;
4
 
5
+ use FluentMail\App\Models\Logger;
6
+ use FluentMail\App\Services\Converter;
7
  use FluentMail\Includes\Core\Application;
8
  use FluentMail\App\Services\Mailer\Manager;
9
+ use FluentMail\Includes\Support\Arr;
10
 
11
  class AdminMenuHandler
12
  {
27
 
28
  add_action('admin_bar_menu', array($this, 'addSimulationBar'), 999);
29
 
30
+ add_action('admin_init', array($this, 'initAdminWidget'));
31
+
32
  }
33
 
34
  public function addMenu()
44
  [$this, 'renderApp'],
45
  16
46
  );
47
+
48
+
49
+ if (defined('WPFORMS_VERSION')) {
50
+ // As user is using FluentSMTP we want to remove other SMTP suggestions which will create conflicts
51
+ // and FluentSMTP will not work in that case, So we are removing from that menu
52
+ global $submenu;
53
+ if (Arr::get($submenu, 'wpforms-overview.7.2') == 'wpforms-smtp') {
54
+ unset($submenu['wpforms-overview'][7]);
55
+ } else {
56
+ foreach ($submenu['wpforms-overview'] as $itemIndex => $item) {
57
+ if (isset($item[2]) && $item[2] == 'wpforms-smtp') {
58
+ unset($submenu['wpforms-overview'][$itemIndex]);
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
  }
65
 
66
  public function renderApp()
117
 
118
  $user = get_user_by('ID', get_current_user_id());
119
 
120
+ $disable_recommendation = defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS;
121
+
122
+ $settings = $this->getMailerSettings();
123
+
124
+ $recommendedSettings = false;
125
+ if (empty($settings['connections'])) {
126
+ $recommendedSettings = (new Converter())->getSuggestedConnection();
127
+ }
128
+
129
  wp_localize_script('fluent_mail_admin_app_boot', 'FluentMailAdmin', [
130
  'slug' => FLUENTMAIL,
131
  'brand_logo' => esc_url(fluentMailMix('images/logo.svg')),
132
  'nonce' => wp_create_nonce(FLUENTMAIL),
133
+ 'settings' => $settings,
134
  'has_fluentcrm' => defined('FLUENTCRM'),
135
  'has_fluentform' => defined('FLUENTFORM'),
136
  'user_email' => $user->user_email,
137
  'require_optin' => $this->isRequireOptin(),
138
  'has_ninja_tables' => defined('NINJA_TABLES_VERSION'),
139
  'disable_recommendation' => apply_filters('fluentmail_disable_recommendation', false),
140
+ 'disable_installation' => $disable_recommendation,
141
+ 'plugin_url' => 'https://fluentsmtp.com/?utm_source=wp&utm_medium=install&utm_campaign=dashboard',
142
+ 'trans' => $this->getTrans(),
143
+ 'recommended' => $recommendedSettings
144
  ]);
145
 
146
  do_action('fluent_mail_loading_app');
166
  {
167
  $settings = $this->app->make(Manager::class)->getMailerConfigAndSettings(true);
168
 
169
+ if ($settings['mappings'] && $settings['connections']) {
170
+ $validMappings = array_keys(Arr::get($settings, 'connections', []));
171
+
172
+ $settings['mappings'] = array_filter($settings['mappings'], function ($key) use ($validMappings) {
173
+ return in_array($key, $validMappings);
174
+ });
175
+ }
176
+
177
+ $settings['providers']['outlook']['callback_url'] = rest_url('fluent-smtp/outlook_callback');
178
+
179
  $settings = array_merge(
180
  $settings,
181
  [
229
 
230
  $misc = $this->app->make(Manager::class)->getConfig('misc');
231
 
232
+ if (!empty($misc['simulate_emails']) && $misc['simulate_emails'] == 'yes') {
233
  $args = [
234
  'parent' => 'top-secondary',
235
  'id' => 'fluentsmtp_simulated',
247
  public function isRequireOptin()
248
  {
249
  $opted = get_option('_fluentsmtp_sub_update');
250
+ if ($opted) {
251
  return 'no';
252
  }
253
  // check if dismissed
254
+ $dismissedStamp = get_option('_fluentsmtp_dismissed_timestamp');
255
+ if ($dismissedStamp && (time() - $dismissedStamp) < 30 * 24 * 60 * 60) {
256
  return 'no';
257
  }
258
 
259
  return 'yes';
260
  }
261
+
262
+
263
+ public function initAdminWidget()
264
+ {
265
+ // This widget should be displayed for certain high-level users only.
266
+ if (!current_user_can('manage_options') || apply_filters('fluent_mail_disable_dashboard_widget', false)) {
267
+ return;
268
+ }
269
+
270
+ add_action('wp_dashboard_setup', function () {
271
+ $widget_key = 'fluentsmtp_reports_widget';
272
+
273
+ wp_add_dashboard_widget(
274
+ $widget_key,
275
+ esc_html__('Fluent SMTP', 'fluent-smtp'),
276
+ [$this, 'dashWidgetContent']
277
+ );
278
+ });
279
+
280
+ }
281
+
282
+ public function dashWidgetContent()
283
+ {
284
+ $stats = [];
285
+ $logModel = new Logger();
286
+ $currentTimeStamp = current_time('timestamp');
287
+ $startToday = date('Y-m-d 00:00:01', $currentTimeStamp);
288
+
289
+ $allTime = $logModel->getStats();
290
+
291
+ $stats['today'] = [
292
+ 'title' => __('Today', 'fluent-smtp'),
293
+ 'sent' => ($allTime['sent']) ? $logModel->getTotalCountStat('sent', $startToday) : 0,
294
+ 'failed' => ($allTime['failed']) ? $logModel->getTotalCountStat('failed', $startToday) : 0
295
+ ];
296
+
297
+ $lastWeek = date('Y-m-d 00:00:01', strtotime('-7 days'));
298
+ $stats['week'] = [
299
+ 'title' => __('Last 7 days', 'fluent-smtp'),
300
+ 'sent' => ($allTime['sent']) ? $logModel->getTotalCountStat('sent', $lastWeek) : 0,
301
+ 'failed' => ($allTime['failed']) ? $logModel->getTotalCountStat('sent', $lastWeek) : 0,
302
+ ];
303
+
304
+ $stats['all_time'] = [
305
+ 'title' => __('All', 'fluent-smtp'),
306
+ 'sent' => $allTime['sent'],
307
+ 'failed' => $allTime['failed'],
308
+ ];
309
+
310
+ ?>
311
+ <style type="text/css">
312
+ td.fstmp_failed {
313
+ color: red;
314
+ font-weight: bold;
315
+ }
316
+ </style>
317
+ <div class="fsmtp_dash_wrapper">
318
+ <table class="fsmtp_dash_table wp-list-table widefat fixed striped">
319
+ <thead>
320
+ <tr>
321
+ <th><?php _e('Date', 'fluent-smtp'); ?></th>
322
+ <th><?php _e('Sent', 'fluent-smtp'); ?></th>
323
+ <th><?php _e('Failed', 'fluent-smtp'); ?></th>
324
+ </tr>
325
+ </thead>
326
+ <tbody>
327
+ <?php foreach ($stats as $stat): ?>
328
+ <tr>
329
+ <td><?php echo $stat['title']; ?></td>
330
+ <td><?php echo $stat['sent']; ?></td>
331
+ <td class="<?php echo ($stat['failed']) ? 'fstmp_failed' : ''; ?>"><?php echo $stat['failed']; ?></td>
332
+ </tr>
333
+ <?php endforeach; ?>
334
+ </tbody>
335
+ </table>
336
+ <a style="text-decoration: none; padding-top: 10px; display: block"
337
+ href="<?php echo admin_url('options-general.php?page=fluent-mail#/'); ?>"
338
+ class=""><?php _e('View All', 'fluent-smtp'); ?></a>
339
+ </div>
340
+ <?php
341
+ }
342
+
343
+ public function getTrans()
344
+ {
345
+ return [
346
+ 'Settings' => __('Settings', 'fluent-smtp'),
347
+ 'Email Test' => __('Email Test', 'fluent-smtp'),
348
+ 'Email Logs' => __('Email Logs', 'fluent-smtp'),
349
+ 'Support' => __('Support', 'fluent-smtp'),
350
+ 'Docs' => __('Docs', 'fluent-smtp'),
351
+ 'cancel' => __('cancel', 'fluent-smtp'),
352
+ 'confirm' => __('confirm', 'fluent-smtp'),
353
+ 'confirm_msg' => __('Are you sure to delete this?', 'fluent-smtp'),
354
+ 'wizard_title' => __('Welcome to FluentSMTP', 'fluent-smtp'),
355
+ 'wizard_sub' => __('Thank you for installing FluentSMTP - The ultimate SMTP & Email Service Connection Plugin for WordPress', 'fluent-smtp'),
356
+ 'wizard_instruction' => __('Please configure your first email service provider connection', 'fluent-smtp'),
357
+ 'Sending Stats' => __('Sending Stats', 'fluent-smtp'),
358
+ 'Quick Overview' => __('Quick Overview', 'fluent-smtp'),
359
+ 'Total Email Sent (Logged):' => __('Total Email Sent (Logged):', 'fluent-smtp'),
360
+ 'Email Failed:' => __('Email Failed:', 'fluent-smtp'),
361
+ 'Active Connections:' => __('Active Connections:', 'fluent-smtp'),
362
+ 'Active Senders:' => __('Active Senders:', 'fluent-smtp'),
363
+ 'Save Email Logs:' => __('Save Email Logs:', 'fluent-smtp'),
364
+ 'Delete Logs:' => __('Delete Logs:', 'fluent-smtp'),
365
+ 'Days' => __('Days', 'fluent-smtp'),
366
+ 'Subscribe To Updates' => __('Subscribe To Updates', 'fluent-smtp'),
367
+ 'Last week' => __('Last week', 'fluent-smtp'),
368
+ 'Last month' => __('Last month', 'fluent-smtp'),
369
+ 'Last 3 months' => __('Last 3 months', 'fluent-smtp'),
370
+ 'By Date' => __('By Date', 'fluent-smtp'),
371
+ 'Apply' => __('Apply', 'fluent-smtp'),
372
+ 'Resend Selected Emails' => __('Resend Selected Emails', 'fluent-smtp'),
373
+ 'Bulk Action' => __('Bulk Action', 'fluent-smtp'),
374
+ 'Delete All' => __('Delete All', 'fluent-smtp'),
375
+ 'Enter Full Screen' => __('Enter Full Screen', 'fluent-smtp'),
376
+ 'Filter By' => __('Filter By', 'fluent-smtp'),
377
+ 'Status' => __('Status', 'fluent-smtp'),
378
+ 'Date' => __('Date', 'fluent-smtp'),
379
+ 'Date Range' => __('Date Range', 'fluent-smtp'),
380
+ 'Select' => __('Select', 'fluent-smtp'),
381
+ 'Successful' => __('Successful', 'fluent-smtp'),
382
+ 'Failed' => __('Failed', 'fluent-smtp'),
383
+ 'Select date' => __('Select date', 'fluent-smtp'),
384
+ 'Select date and time' => __('Select date and time', 'fluent-smtp'),
385
+ 'Start date' => __('Start date', 'fluent-smtp'),
386
+ 'End date' => __('End date', 'fluent-smtp'),
387
+ 'Filter' => __('Filter', 'fluent-smtp'),
388
+ 'Type & press enter...' => __('Type & press enter...', 'fluent-smtp'),
389
+ 'Subject' => __('Subject', 'fluent-smtp'),
390
+ 'To' => __('To', 'fluent-smtp'),
391
+ 'Date-Time' => __('Date-Time', 'fluent-smtp'),
392
+ 'Actions' => __('Actions', 'fluent-smtp'),
393
+ 'Retry' => __('Retry', 'fluent-smtp'),
394
+ 'Resend' => __('Resend', 'fluent-smtp'),
395
+ 'Turn On' => __('Turn On', 'fluent-smtp'),
396
+ 'Resent Count' => __('Resent Count', 'fluent-smtp'),
397
+ 'Email Body' => __('Email Body', 'fluent-smtp'),
398
+ 'Attachments' => __('Attachments', 'fluent-smtp'),
399
+ 'Next' => __('Next', 'fluent-smtp'),
400
+ 'Prev' => __('Prev', 'fluent-smtp'),
401
+ 'Search Results for' => __('Search Results for', 'fluent-smtp'),
402
+ 'Sender Settings' => __('Sender Settings', 'fluent-smtp'),
403
+ 'From Email' => __('From Email', 'fluent-smtp'),
404
+ 'Force From Email (Recommended Settings: Enable)' => __('Force From Email (Recommended Settings: Enable)', 'fluent-smtp'),
405
+ 'from_email_tooltip' => __('If checked, the From Email setting above will be used for all emails (It will check if the from email is listed to available connections).', 'fluent-smtp'),
406
+ 'Set the return-path to match the From Email' => __('Set the return-path to match the From Email', 'fluent-smtp'),
407
+ 'From Name' => __('From Name', 'fluent-smtp'),
408
+ 'Force Sender Name' => __('Force Sender Name', 'fluent-smtp'),
409
+ 'Save Connection Settings' => __('Save Connection Settings', 'fluent-smtp'),
410
+ 'save_connection_error_1' => __('Please select your email service provider', 'fluent-smtp'),
411
+ 'save_connection_error_2' => __('Credential Verification Failed. Please check your inputs', 'fluent-smtp'),
412
+ 'force_sender_tooltip' => __('When checked, the From Name setting above will be used for all emails, ignoring values set by other plugins.', 'fluent-smtp'),
413
+ 'Validating Data. Please wait' => __('Validating Data. Please wait', 'fluent-smtp'),
414
+ 'Active Email Connections' => __('Active Email Connections', 'fluent-smtp'),
415
+ 'Add Another Connection' => __('Add Another Connection', 'fluent-smtp'),
416
+ 'Provider' => __('Provider', 'fluent-smtp'),
417
+ 'Connection Details' => __('Connection Details', 'fluent-smtp'),
418
+ 'Close' => __('Close', 'fluent-smtp'),
419
+ 'General Settings' => __('General Settings', 'fluent-smtp'),
420
+ 'Notification Settings' => __('Notification Settings', 'fluent-smtp'),
421
+ 'Add Connection' => __('Add Connection', 'fluent-smtp'),
422
+ 'Edit Connection' => __('Edit Connection', 'fluent-smtp'),
423
+ 'routing_info' => __('Your emails will be routed automatically based on From email address. No additional configuration is required.', 'fluent-smtp'),
424
+ 'Enable Email Summary' => __('Enable Email Summary', 'fluent-smtp'),
425
+ 'Enable Email Summary Notification' => __('Enable Email Summary Notification', 'fluent-smtp'),
426
+ 'Notification Email Addresses' => __('Notification Email Addresses', 'fluent-smtp'),
427
+ 'Email Address' => __('Email Address', 'fluent-smtp'),
428
+ 'Notification Days' => __('Notification Days', 'fluent-smtp'),
429
+ 'Save Settings' => __('Save Settings', 'fluent-smtp'),
430
+ 'Log All Emails for Reporting' => __('Log All Emails for Reporting', 'fluent-smtp'),
431
+ 'Disable Logging for FluentCRM Emails' => __('Disable Logging for FluentCRM Emails', 'fluent-smtp'),
432
+ 'FluentCRM Email Logging' => __('FluentCRM Email Logging', 'fluent-smtp'),
433
+ 'Delete Logs' => __('Delete Logs', 'fluent-smtp'),
434
+ 'delete_logs_info' => __('Select how many days, the logs will be saved. If you select 7 days, then logs older than 7 days will be deleted automatically.', 'fluent-smtp'),
435
+ 'Default Connection' => __('Default Connection', 'fluent-smtp'),
436
+ 'Fallback Connection' => __('Fallback Connection', 'fluent-smtp'),
437
+ 'default_connection_popover' => __('Select which connection will be used for sending transactional emails from your WordPress. If you use multiple connection then email will be routed based on source from email address', 'fluent-smtp'),
438
+ 'fallback_connection_popover' => __('Fallback Connection will be used if an email is failed to send in one connection. Please select a different connection than the default connection', 'fluent-smtp'),
439
+ 'Please add another connection to use fallback feature' => __('Please add another connection to use fallback feature', 'fluent-smtp'),
440
+ 'Email Simulation' => __('Email Simulation', 'fluent-smtp'),
441
+ 'Email_Simulation_Label' => __('Disable sending all emails. If you enable this, no email will be sent.', 'fluent-smtp'),
442
+ 'Email_Simulation_Yes' => __('No Emails will be sent from your WordPress.', 'fluent-smtp'),
443
+ ];
444
+ }
445
  }
app/Hooks/Handlers/SchedulerHandler.php ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FluentMail\App\Hooks\Handlers;
4
+
5
+ use FluentMail\App\Models\Logger;
6
+ use FluentMail\App\Models\Settings;
7
+ use FluentMail\Includes\Support\Arr;
8
+
9
+ class SchedulerHandler
10
+ {
11
+ protected $actionName = 'fluentmail_do_daily_scheduled_tasks';
12
+
13
+ public function register()
14
+ {
15
+ add_action('init', array($this, 'initScheduler'));
16
+ add_action($this->actionName, array($this, 'handleScheduledJobs'));
17
+ add_action('fluentmail_email_sending_failed', array($this, 'maybeHandleFallbackConnection'), 10, 2);
18
+ }
19
+
20
+ public function initScheduler()
21
+ {
22
+ if (!wp_next_scheduled($this->actionName)) {
23
+ wp_schedule_event(time(), 'daily', $this->actionName);
24
+ }
25
+ }
26
+
27
+ public function handleScheduledJobs()
28
+ {
29
+ $this->deleteOldEmails();
30
+ $this->sendDailyDigest();
31
+ }
32
+
33
+ private function deleteOldEmails()
34
+ {
35
+ $settings = get_option('fluentmail-settings', []);
36
+ $logSaveDays = intval(Arr::get($settings, 'misc.log_saved_interval_days'));
37
+ if ($logSaveDays) {
38
+ (new \FluentMail\App\Models\Logger())->deleteLogsOlderThan($logSaveDays);
39
+ }
40
+ }
41
+
42
+ public function sendDailyDigest()
43
+ {
44
+ $settings = (new Settings())->notificationSettings();
45
+
46
+ if($settings['enabled'] != 'yes' || empty($settings['notify_days']) || empty($settings['notify_email'])) {
47
+ return;
48
+ }
49
+
50
+ $currentDay = date('D');
51
+ if(!in_array($currentDay, $settings['notify_days'])) {
52
+ return;
53
+ }
54
+
55
+ $sendTo = $settings['notify_email'];
56
+ $sendTo = str_replace('{admin_email}', get_option('admin_email'), $sendTo);
57
+
58
+ $sendToArray = explode(',', $sendTo);
59
+
60
+ $sendToArray = array_filter($sendToArray, function ($email) {
61
+ return is_email($email);
62
+ });
63
+
64
+ if(!$sendToArray) {
65
+ return false;
66
+ }
67
+
68
+ // we can send a summary email
69
+ $lastDigestSent = get_option('_fluentmail_last_email_digest');
70
+ if($lastDigestSent) {
71
+ if((time() - strtotime($lastDigestSent)) < 72000 ) {
72
+ return false; // we don't want to send another email if sent time within 20 hours
73
+ }
74
+ } else {
75
+ $lastDigestSent = date('Y-m-d', strtotime('-7 days'));
76
+ }
77
+
78
+ // Let's create the stats
79
+ $startDate = date('Y-m-d 00:00:01', (strtotime($lastDigestSent) - 86400));
80
+ $endDate = date('Y-m-d 23:59:59', strtotime('-1 days'));
81
+
82
+ $reportingDays = floor((strtotime($endDate) - strtotime($startDate)) / 86400);
83
+
84
+ $loggerModel = new Logger();
85
+ $sentCount = $loggerModel->getTotalCountStat('sent', $startDate, $endDate);
86
+
87
+ $sentStats = [
88
+ 'total' => $sentCount,
89
+ 'subjects' => [],
90
+ 'unique_subjects' => 0
91
+ ];
92
+ if($sentCount) {
93
+ $sentStats['unique_subjects'] = $loggerModel->getSubjectCountStat('sent', $startDate, $endDate);
94
+ $sentStats['subjects'] = $loggerModel->getSubjectStat('sent', $startDate, $endDate, 10);
95
+ }
96
+
97
+ $failedCount = $loggerModel->getTotalCountStat('failed', $startDate, $endDate);
98
+ $failedStats = [
99
+ 'total' => $sentCount,
100
+ 'subjects' => [],
101
+ 'unique_subjects' => 0
102
+ ];
103
+ if($failedCount) {
104
+ $failedStats['unique_subjects'] = $loggerModel->getSubjectCountStat('failed', $startDate, $endDate);
105
+ $failedStats['subjects'] = $loggerModel->getSubjectStat('failed', $startDate, $endDate);
106
+ }
107
+
108
+ $sentSubTitle = sprintf(
109
+ __('Showing %1$s of %2$s different subject lines sent in the past %3$s'),
110
+ number_format_i18n(count($sentStats['subjects'])),
111
+ number_format_i18n($sentStats['unique_subjects']),
112
+ ($reportingDays < 2) ? 'day' : $reportingDays.' days'
113
+ );
114
+
115
+ $failedSubTitle = sprintf(
116
+ __('Showing %1$s of %2$s different subject lines failed in the past %3$s'),
117
+ number_format_i18n(count($failedStats['subjects'])),
118
+ number_format_i18n($failedStats['unique_subjects']),
119
+ ($reportingDays < 2) ? 'day' : $reportingDays.' days'
120
+ );
121
+
122
+ $sentTitle = __('Emails Sent', 'fluent-smtp');
123
+ if($sentCount) {
124
+ $sentTitle .= ' <span style="font-size: 12px; vertical-align: middle;">('.number_format_i18n($sentCount).')</span>';
125
+ }
126
+ $failedTitle = __('Email Failures', 'fluent-smtp');
127
+ if($failedCount) {
128
+ $failedTitle .= ' <span style="font-size: 12px; vertical-align: middle;">('.number_format_i18n($failedCount).')</span>';
129
+ }
130
+
131
+ $reportingDate = date(get_option('date_format'), strtotime($startDate));
132
+
133
+ $data = [
134
+ 'sent' => [
135
+ 'total' => $sentCount,
136
+ 'title' => $sentTitle,
137
+ 'subtitle' => $sentSubTitle,
138
+ 'subject_items' => $sentStats['subjects']
139
+ ],
140
+ 'fail' => [
141
+ 'total' => $failedCount,
142
+ 'title' => $failedTitle,
143
+ 'subtitle' => $failedSubTitle,
144
+ 'subject_items' => $failedStats['subjects']
145
+ ],
146
+ 'date_range' => $reportingDate,
147
+ 'domain_name' => $this->getDomainName()
148
+ ];
149
+
150
+ $emailBody = (string) fluentMail('view')->make('admin.digest_email', $data);
151
+ $emailSubject = $reportingDate. ' email sending stats for '.$this->getDomainName();
152
+
153
+ $headers = array('Content-Type: text/html; charset=UTF-8');
154
+
155
+ update_option('_fluentmail_last_email_digest', date('Y-m-d H:i:s'));
156
+
157
+ return wp_mail($sendToArray, $emailSubject, $emailBody, $headers);
158
+
159
+ }
160
+
161
+ private function getDomainName()
162
+ {
163
+ $parts = parse_url( site_url() );
164
+ $url = $parts['host'] . ( isset( $parts['path'] ) ? $parts['path'] : '' );
165
+ return untrailingslashit( $url );
166
+ }
167
+
168
+ public function maybeHandleFallbackConnection($logId, $handler)
169
+ {
170
+ if(defined('FLUENTMAIL_EMAIL_TESTING')) {
171
+ return false;
172
+ }
173
+
174
+ $settings = (new \FluentMail\App\Models\Settings())->getSettings();
175
+
176
+ $fallbackConnectionId = \FluentMail\Includes\Support\Arr::get($settings, 'misc.fallback_connection');
177
+
178
+ if (!$fallbackConnectionId) {
179
+ return false;
180
+ }
181
+
182
+ $fallbackConnection = \FluentMail\Includes\Support\Arr::get($settings, 'connections.' . $fallbackConnectionId);
183
+
184
+ if(!$fallbackConnection) {
185
+ return false;
186
+ }
187
+
188
+ $phpMailer = $handler->getPhpMailer();
189
+
190
+ $fallbackSettings = $fallbackConnection['provider_settings'];
191
+ $phpMailer->setFrom($fallbackSettings['sender_email'], $phpMailer->FromName);
192
+
193
+ // Trap the fluentSMTPMail mailer here
194
+ $phpMailer = new \FluentMail\App\Services\Mailer\FluentPHPMailer($phpMailer);
195
+ return $phpMailer->sendViaFallback($logId);
196
+ }
197
+ }
app/Hooks/actions.php CHANGED
@@ -2,20 +2,30 @@
2
 
3
  (new \FluentMail\App\Hooks\Handlers\AdminMenuHandler($app))->addFluentMailMenu();
4
 
 
 
5
  $app->addCustomAction('handle_exception', 'ExceptionHandler@handle');
6
 
7
  $app->addAction('admin_notices', 'AdminMenuHandler@maybeAdminNotice');
8
 
9
- $app->addAction('fluentmail_do_daily_scheduled_tasks', function () {
10
- $manager = fluentMail(\FluentMail\App\Services\Mailer\Manager::class);
11
- $logSaveDays = $manager->getSettings('misc.log_saved_interval_days');
12
- $logSaveDays = intval($logSaveDays);
13
- if($logSaveDays) {
14
- (new \FluentMail\App\Models\Logger())->deleteLogsOlderThan($logSaveDays);
15
- }
16
-
17
- $day = date('d');
18
- if($day == '01') {
19
- // this is a monthly cron
20
- }
21
- });
 
 
 
 
 
 
 
 
2
 
3
  (new \FluentMail\App\Hooks\Handlers\AdminMenuHandler($app))->addFluentMailMenu();
4
 
5
+ (new \FluentMail\App\Hooks\Handlers\SchedulerHandler())->register();
6
+
7
  $app->addCustomAction('handle_exception', 'ExceptionHandler@handle');
8
 
9
  $app->addAction('admin_notices', 'AdminMenuHandler@maybeAdminNotice');
10
 
11
+ add_action('rest_api_init', function () use ($app) {
12
+ register_rest_route('fluent-smtp', '/outlook_callback/', array(
13
+ 'methods' => 'GET',
14
+ 'callback' => function (\WP_REST_Request $request) use ($app) {
15
+ $code = $request->get_param('code');
16
+ header("Content-Type: text/html");
17
+ $app->view->render('admin.html_code', [
18
+ 'title' => 'Your Access Code',
19
+ 'body' => '<p>Copy the following code and paste in the fluentSMTP settings</p><textarea readonly>' . sanitize_textarea_field($code) . '</textarea>'
20
+ ]);
21
+ die();
22
+ },
23
+ 'permission_callback' => function () {
24
+ $state = $_REQUEST['state'];
25
+ if($state != get_option('_fluentmail_last_generated_state')) {
26
+ return false;
27
+ }
28
+ return true;
29
+ }
30
+ ));
31
+ });
app/Http/Controllers/Controller.php CHANGED
@@ -53,6 +53,14 @@ abstract class Controller
53
  die();
54
  }
55
 
 
 
 
 
 
 
 
 
56
  return true;
57
  }
58
  }
53
  die();
54
  }
55
 
56
+ $nonce = $this->request->get('nonce');
57
+ if(!wp_verify_nonce($nonce, FLUENTMAIL)) {
58
+ wp_send_json_error([
59
+ 'message' => __('Security Failed. Please reload the page', 'fluent-smtp')
60
+ ]);
61
+ die();
62
+ }
63
+
64
  return true;
65
  }
66
  }
app/Http/Controllers/LoggerController.php CHANGED
@@ -53,7 +53,6 @@ class LoggerController extends Controller
53
 
54
  try {
55
  $this->app->addAction('wp_mail_failed', function($response) use ($logger, $request) {
56
-
57
  $log = $logger->find($id = $request->get('id'));
58
  $log['retries'] = $log['retries'] + 1;
59
  $logger->updateLog($log, ['id' => $id]);
53
 
54
  try {
55
  $this->app->addAction('wp_mail_failed', function($response) use ($logger, $request) {
 
56
  $log = $logger->find($id = $request->get('id'));
57
  $log['retries'] = $log['retries'] + 1;
58
  $logger->updateLog($log, ['id' => $id]);
app/Http/Controllers/SettingsController.php CHANGED
@@ -59,6 +59,15 @@ class SettingsController extends Controller
59
  $provider = $factory->make($data['connection']['provider']);
60
 
61
  $connection = $data['connection'];
 
 
 
 
 
 
 
 
 
62
  $this->validateConnection($provider, $connection);
63
  $provider->checkConnection($connection);
64
 
@@ -100,7 +109,7 @@ class SettingsController extends Controller
100
  $this->verify();
101
 
102
  $settings = $settings->delete($request->get('key'));
103
-
104
  return $this->sendSuccess($settings);
105
  }
106
 
@@ -133,7 +142,7 @@ class SettingsController extends Controller
133
  'email_error' => __('The email field is required.', 'fluent-smtp')
134
  ], 422);
135
  }
136
-
137
  if (!defined('FLUENTMAIL_EMAIL_TESTING')) {
138
  define('FLUENTMAIL_EMAIL_TESTING', true);
139
  }
@@ -227,7 +236,7 @@ class SettingsController extends Controller
227
  ]
228
  ];
229
 
230
- if(!isset($UrlMaps[$pluginSlug])) {
231
  $this->sendError([
232
  'message' => __('Sorry, You can not install this plugin', 'fluent-smtp')
233
  ]);
@@ -246,7 +255,6 @@ class SettingsController extends Controller
246
  }
247
  }
248
 
249
-
250
  private function backgroundInstaller($plugin_to_install)
251
  {
252
  if (!empty($plugin_to_install['repo-slug'])) {
@@ -363,6 +371,7 @@ class SettingsController extends Controller
363
 
364
  public function subscribe()
365
  {
 
366
  $email = sanitize_text_field($_REQUEST['email']);
367
  if(!is_email($email)) {
368
  return $this->sendError([
@@ -388,6 +397,7 @@ class SettingsController extends Controller
388
 
389
  public function subscribeDismiss()
390
  {
 
391
  update_option('_fluentsmtp_dismissed_timestamp', time(), 'no');
392
 
393
  return $this->sendSuccess([
@@ -430,6 +440,7 @@ class SettingsController extends Controller
430
 
431
  public function getGmailAuthUrl(Request $request)
432
  {
 
433
  $connection = wp_unslash($request->get('connection'));
434
 
435
  $clientId = Arr::get($connection, 'client_id');
@@ -479,4 +490,97 @@ class SettingsController extends Controller
479
  ]);
480
  }
481
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
482
  }
 
 
59
  $provider = $factory->make($data['connection']['provider']);
60
 
61
  $connection = $data['connection'];
62
+
63
+ $connection['sender_name'] = sanitize_text_field($connection['sender_name']);
64
+ $connection['sender_email'] = sanitize_email($connection['sender_email']);
65
+ if(isset($connection['force_from_email'])) {
66
+ $connection['force_from_email'] = sanitize_text_field($connection['force_from_email']);
67
+ }
68
+ $connection['return_path'] = sanitize_text_field($connection['return_path']);
69
+ $connection['provider'] = sanitize_text_field($connection['provider']);
70
+
71
  $this->validateConnection($provider, $connection);
72
  $provider->checkConnection($connection);
73
 
109
  $this->verify();
110
 
111
  $settings = $settings->delete($request->get('key'));
112
+
113
  return $this->sendSuccess($settings);
114
  }
115
 
142
  'email_error' => __('The email field is required.', 'fluent-smtp')
143
  ], 422);
144
  }
145
+
146
  if (!defined('FLUENTMAIL_EMAIL_TESTING')) {
147
  define('FLUENTMAIL_EMAIL_TESTING', true);
148
  }
236
  ]
237
  ];
238
 
239
+ if(!isset($UrlMaps[$pluginSlug]) || (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS)) {
240
  $this->sendError([
241
  'message' => __('Sorry, You can not install this plugin', 'fluent-smtp')
242
  ]);
255
  }
256
  }
257
 
 
258
  private function backgroundInstaller($plugin_to_install)
259
  {
260
  if (!empty($plugin_to_install['repo-slug'])) {
371
 
372
  public function subscribe()
373
  {
374
+ $this->verify();
375
  $email = sanitize_text_field($_REQUEST['email']);
376
  if(!is_email($email)) {
377
  return $this->sendError([
397
 
398
  public function subscribeDismiss()
399
  {
400
+ $this->verify();
401
  update_option('_fluentsmtp_dismissed_timestamp', time(), 'no');
402
 
403
  return $this->sendSuccess([
440
 
441
  public function getGmailAuthUrl(Request $request)
442
  {
443
+ $this->verify();
444
  $connection = wp_unslash($request->get('connection'));
445
 
446
  $clientId = Arr::get($connection, 'client_id');
490
  ]);
491
  }
492
 
493
+ public function getOutlookAuthUrl(Request $request)
494
+ {
495
+ $this->verify();
496
+ $connection = wp_unslash($request->get('connection'));
497
+
498
+ $clientId = Arr::get($connection, 'client_id');
499
+ $clientSecret = Arr::get($connection, 'client_secret');
500
+
501
+ delete_option('_fluentsmtp_intended_outlook_info');
502
+
503
+ if(Arr::get($connection, 'key_store') == 'wp_config') {
504
+ if(defined('FLUENTMAIL_OUTLOOK_CLIENT_ID')) {
505
+ $clientId = FLUENTMAIL_OUTLOOK_CLIENT_ID;
506
+ } else {
507
+ return $this->sendError([
508
+ 'client_id' => [
509
+ 'required' => 'Please define FLUENTMAIL_OUTLOOK_CLIENT_ID in your wp-config.php file'
510
+ ]
511
+ ]);
512
+ }
513
+ if(defined('FLUENTMAIL_OUTLOOK_CLIENT_SECRET')) {
514
+ $clientSecret = FLUENTMAIL_OUTLOOK_CLIENT_SECRET;
515
+ } else {
516
+ return $this->sendError([
517
+ 'client_secret' => [
518
+ 'required' => 'Please define FLUENTMAIL_OUTLOOK_CLIENT_SECRET in your wp-config.php file'
519
+ ]
520
+ ]);
521
+ }
522
+ } else {
523
+ update_option('_fluentsmtp_intended_outlook_info', [
524
+ 'client_id' => $clientId,
525
+ 'client_secret' => $clientSecret
526
+ ]);
527
+ }
528
+
529
+ if(!$clientId) {
530
+ return $this->sendError([
531
+ 'client_id' => [
532
+ 'required' => 'Please provide application client id'
533
+ ]
534
+ ]);
535
+ }
536
+
537
+ if(!$clientSecret) {
538
+ return $this->sendError([
539
+ 'client_secret' => [
540
+ 'required' => 'Please provide application client secret'
541
+ ]
542
+ ]);
543
+ }
544
+
545
+ return $this->sendSuccess([
546
+ 'auth_url' => (new \FluentMail\App\Services\Mailer\Providers\Outlook\API($clientId, $clientSecret))->getAuthUrl()
547
+ ]);
548
+ }
549
+
550
+ public function getNotificationSettings()
551
+ {
552
+ $this->verify();
553
+ return $this->sendSuccess([
554
+ 'settings' => (new Settings())->notificationSettings()
555
+ ]);
556
+ }
557
+
558
+ public function saveNotificationSettings(Request $request)
559
+ {
560
+ $this->verify();
561
+
562
+ $settings = $request->get('settings', []);
563
+
564
+ $settings = Arr::only($settings, ['enabled', 'notify_email', 'notify_days']);
565
+
566
+ $settings['notify_email'] = sanitize_text_field( $settings['notify_email']);
567
+ $settings['enabled'] = sanitize_text_field( $settings['enabled']);
568
+
569
+ $defaults = [
570
+ 'enabled' => 'no',
571
+ 'notify_email' => '{site_admin}',
572
+ 'notify_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
573
+ ];
574
+
575
+ $settings = wp_parse_args($settings, $defaults);
576
+
577
+ update_option('_fluent_smtp_notify_settings', $settings, false);
578
+
579
+ return $this->sendSuccess([
580
+ 'message' => 'Settings has been updated successfully'
581
+ ]);
582
+ }
583
+
584
  }
585
+
586
+
app/Http/routes.php CHANGED
@@ -13,7 +13,10 @@ $app->post('/settings/test', 'SettingsController@sendTestEmil');
13
  $app->post('/settings/subscribe', 'SettingsController@subscribe');
14
  $app->post('/settings/subscribe-dismiss', 'SettingsController@subscribeDismiss');
15
  $app->get('settings/connection_info', 'SettingsController@getConnectionInfo');
 
 
16
  $app->post('settings/gmail_auth_url', 'SettingsController@getGmailAuthUrl');
 
17
 
18
  $app->get('/logs', 'LoggerController@get');
19
  $app->get('/logs/show', 'LoggerController@show');
13
  $app->post('/settings/subscribe', 'SettingsController@subscribe');
14
  $app->post('/settings/subscribe-dismiss', 'SettingsController@subscribeDismiss');
15
  $app->get('settings/connection_info', 'SettingsController@getConnectionInfo');
16
+ $app->get('settings/notification-settings', 'SettingsController@getNotificationSettings');
17
+ $app->post('settings/notification-settings', 'SettingsController@saveNotificationSettings');
18
  $app->post('settings/gmail_auth_url', 'SettingsController@getGmailAuthUrl');
19
+ $app->post('settings/outlook_auth_url', 'SettingsController@getOutlookAuthUrl');
20
 
21
  $app->get('/logs', 'LoggerController@get');
22
  $app->get('/logs/show', 'LoggerController@show');
app/Models/Logger.php CHANGED
@@ -24,10 +24,7 @@ class Logger extends Model
24
  protected $searchables = [
25
  'to',
26
  'from',
27
- 'subject',
28
- 'body',
29
- 'response',
30
- 'extra'
31
  ];
32
 
33
  protected $table = null;
@@ -54,34 +51,34 @@ class Logger extends Model
54
  $filterColumn = isset($data['filter_by']) ? sanitize_text_field($data['filter_by']) : false;
55
  $filterValue = $data['filter_by_value'];
56
 
57
- if($filterColumn && $filterValue) {
58
- if($filterColumn == 'daterange' && is_array($filterValue)) {
59
  $query->whereBetween('created_at', $filterValue[0], $filterValue[1]);
60
- } else if($filterValue) {
61
  $query->where($filterColumn, $filterValue);
62
  }
63
  }
64
 
65
- if(!empty($data['query'])) {
66
  $search = trim(sanitize_text_field($data['query']));
67
  $query->where(function ($q) use ($search) {
68
  $searchColumns = $this->searchables;
69
 
70
  $columnSearch = false;
71
- if(strpos($search, ':')) {
72
  $searchArray = explode(':', $search);
73
  $column = array_shift($searchArray);
74
- if(in_array($column, $this->fillables)) {
75
  $columnSearch = true;
76
- $q->where($column, 'LIKE', '%'.trim(implode(':', $searchArray)).'%');
77
  }
78
  }
79
 
80
- if(!$columnSearch) {
81
  $firstColumn = array_shift($searchColumns);
82
- $q->where($firstColumn, 'LIKE', '%'.$search.'%');
83
  foreach ($searchColumns as $column) {
84
- $q->orWhere($column, 'LIKE', '%'.$search.'%');
85
  }
86
  }
87
 
@@ -98,7 +95,7 @@ class Logger extends Model
98
  protected function buildWhere($data)
99
  {
100
  $where = [];
101
-
102
  if (isset($data['filter_by_value'])) {
103
  $where[$data['filter_by']] = $data['filter_by_value'];
104
  }
@@ -137,12 +134,12 @@ class Logger extends Model
137
  $nestedOr = '';
138
  $values = explode('|', $value);
139
  foreach ($values as $itemValue) {
140
- $args[] = '%'.$this->db->esc_like($itemValue).'%';
141
  $nestedOr .= " OR `{$key}` LIKE '%s'";
142
  }
143
  $orWhere .= ' OR (' . trim($nestedOr, 'OR ') . ')';
144
  } else {
145
- $args[] = '%'.$this->db->esc_like($value).'%';
146
  $orWhere .= " OR `{$key}` LIKE '%s'";
147
  }
148
  }
@@ -209,7 +206,7 @@ class Logger extends Model
209
 
210
  $ids = array_filter($id, 'intval');
211
 
212
- if($ids) {
213
  return $this->getDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs')
214
  ->whereIn('id', $ids)
215
  ->delete();
@@ -287,7 +284,7 @@ class Logger extends Model
287
 
288
  $row->response = maybe_unserialize($row->response);
289
 
290
- return (array) $row;
291
  }
292
 
293
  public function resendEmailFromLog($id, $type = 'retry')
@@ -348,11 +345,11 @@ class Logger extends Model
348
  );
349
 
350
  $updateData = [
351
- 'status' => 'sent',
352
- 'updated_at' => current_time('mysql'),
353
  ];
354
 
355
- if(!$result && $type == 'check_realtime' && $email['status'] == 'failed') {
356
  $updateData['status'] = 'failed';
357
  }
358
 
@@ -408,44 +405,67 @@ class Logger extends Model
408
  }
409
  }
410
 
411
- public function hasPendingEmails()
412
  {
413
- $status = 'pending';
414
-
415
- $query = $this->db->prepare(
416
- "SELECT count(*) as total FROM `{$this->table}` WHERE status = '%s'",
417
- $status
418
- );
419
-
420
- $col = $this->db->get_col($query);
 
 
 
 
 
 
 
 
 
 
 
 
 
421
 
422
- return reset($col);
423
  }
424
 
425
- public function getPendingEmails($limit = 10, $order = 'ASC', $exclude = [])
426
  {
427
- return $this->getEmails($limit, $order, 'pending', $exclude);
 
 
 
 
 
 
 
 
 
 
 
428
  }
429
 
430
- public function getEmails($limit = 10, $order = 'ASC', $status = null, $exclude = [])
431
  {
432
- $where = '';
433
-
434
- if (!$status) {
435
- $where = "WHERE `status` != '{$status}'";
436
- } else {
437
- $where = "WHERE `status` = '{$status}'";
438
- }
439
-
440
- if (is_array($exclude) && $exclude) {
441
- $ids = implode(',', $exclude);
442
- $where .= " AND id NOT IN ({$ids})";
443
- }
444
-
445
- $orderBy = "ORDER BY `created_at` {$order}";
446
 
447
- $query = "SELECT * FROM `{$this->table}` {$where} {$orderBy} LIMIT %d";
448
- $query = $this->db->prepare($query, $limit);
449
- return $this->db->get_results($query);
450
  }
 
451
  }
24
  protected $searchables = [
25
  'to',
26
  'from',
27
+ 'subject'
 
 
 
28
  ];
29
 
30
  protected $table = null;
51
  $filterColumn = isset($data['filter_by']) ? sanitize_text_field($data['filter_by']) : false;
52
  $filterValue = $data['filter_by_value'];
53
 
54
+ if ($filterColumn && $filterValue) {
55
+ if ($filterColumn == 'daterange' && is_array($filterValue)) {
56
  $query->whereBetween('created_at', $filterValue[0], $filterValue[1]);
57
+ } else if ($filterValue) {
58
  $query->where($filterColumn, $filterValue);
59
  }
60
  }
61
 
62
+ if (!empty($data['query'])) {
63
  $search = trim(sanitize_text_field($data['query']));
64
  $query->where(function ($q) use ($search) {
65
  $searchColumns = $this->searchables;
66
 
67
  $columnSearch = false;
68
+ if (strpos($search, ':')) {
69
  $searchArray = explode(':', $search);
70
  $column = array_shift($searchArray);
71
+ if (in_array($column, $this->fillables)) {
72
  $columnSearch = true;
73
+ $q->where($column, 'LIKE', '%' . trim(implode(':', $searchArray)) . '%');
74
  }
75
  }
76
 
77
+ if (!$columnSearch) {
78
  $firstColumn = array_shift($searchColumns);
79
+ $q->where($firstColumn, 'LIKE', '%' . $search . '%');
80
  foreach ($searchColumns as $column) {
81
+ $q->orWhere($column, 'LIKE', '%' . $search . '%');
82
  }
83
  }
84
 
95
  protected function buildWhere($data)
96
  {
97
  $where = [];
98
+
99
  if (isset($data['filter_by_value'])) {
100
  $where[$data['filter_by']] = $data['filter_by_value'];
101
  }
134
  $nestedOr = '';
135
  $values = explode('|', $value);
136
  foreach ($values as $itemValue) {
137
+ $args[] = '%' . $this->db->esc_like($itemValue) . '%';
138
  $nestedOr .= " OR `{$key}` LIKE '%s'";
139
  }
140
  $orWhere .= ' OR (' . trim($nestedOr, 'OR ') . ')';
141
  } else {
142
+ $args[] = '%' . $this->db->esc_like($value) . '%';
143
  $orWhere .= " OR `{$key}` LIKE '%s'";
144
  }
145
  }
206
 
207
  $ids = array_filter($id, 'intval');
208
 
209
+ if ($ids) {
210
  return $this->getDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs')
211
  ->whereIn('id', $ids)
212
  ->delete();
284
 
285
  $row->response = maybe_unserialize($row->response);
286
 
287
+ return (array)$row;
288
  }
289
 
290
  public function resendEmailFromLog($id, $type = 'retry')
345
  );
346
 
347
  $updateData = [
348
+ 'status' => 'sent',
349
+ 'updated_at' => current_time('mysql'),
350
  ];
351
 
352
+ if (!$result && $type == 'check_realtime' && $email['status'] == 'failed') {
353
  $updateData['status'] = 'failed';
354
  }
355
 
405
  }
406
  }
407
 
408
+ public function getTotalCountStat($status, $startDate, $endDate = false)
409
  {
410
+ if ($endDate) {
411
+ $query = $this->db->prepare(
412
+ "SELECT COUNT(*)
413
+ FROM {$this->table}
414
+ WHERE status = %s
415
+ AND created_at >= %s
416
+ AND created_at <= %s",
417
+ $status,
418
+ $startDate,
419
+ $endDate
420
+ );
421
+ } else {
422
+ $query = $this->db->prepare(
423
+ "SELECT COUNT(*)
424
+ FROM {$this->table}
425
+ WHERE status = %s
426
+ AND created_at >= %s",
427
+ $status,
428
+ $startDate
429
+ );
430
+ }
431
 
432
+ return (int)$this->db->get_var($query);
433
  }
434
 
435
+ public function getSubjectCountStat($status, $startDate, $endDate)
436
  {
437
+ $query = $this->db->prepare(
438
+ "SELECT COUNT(DISTINCT(subject))
439
+ FROM {$this->table}
440
+ WHERE status = %s
441
+ AND created_at >= %s
442
+ AND created_at <= %s",
443
+ $status,
444
+ $startDate,
445
+ $endDate
446
+ );
447
+
448
+ return (int)$this->db->get_var($query);
449
  }
450
 
451
+ public function getSubjectStat($status, $statDate, $endDate, $limit = 5)
452
  {
453
+ $query = $this->db->prepare(
454
+ "SELECT subject,
455
+ COUNT(DISTINCT id) AS emails_sent
456
+ FROM {$this->table}
457
+ WHERE created_at >= %s
458
+ AND created_at <= %s
459
+ AND status = %s
460
+ GROUP BY subject
461
+ ORDER BY emails_sent DESC
462
+ LIMIT {$limit}",
463
+ $statDate,
464
+ $endDate,
465
+ $status
466
+ );
467
 
468
+ return $this->db->get_results($query, ARRAY_A);
 
 
469
  }
470
+
471
  }
app/Models/Settings.php CHANGED
@@ -74,6 +74,15 @@ class Settings
74
 
75
  $settings['connections'] = $connections;
76
 
 
 
 
 
 
 
 
 
 
77
  $misc = $this->getMisc();
78
 
79
  if(!$misc) {
@@ -109,24 +118,31 @@ class Settings
109
  {
110
  $settings = $this->getSettings();
111
 
112
- $item = array_filter($settings['mappings'], function($k) use ($key) {
113
- return $k == $key;
114
- });
115
 
116
- $values = array_keys($item);
117
- $email = reset($values);
118
 
119
- if ($email) {
120
- unset($settings['mappings'][$email]);
121
- unset($settings['connections'][$key]);
 
 
 
122
  }
123
 
 
 
 
124
  if (Arr::get($settings, 'misc.default_connection') == $key) {
125
  $default = Arr::get($settings, 'mappings', []);
126
  $default = reset($default);
127
  Arr::set($settings, 'misc.default_connection', $default ?: '');
128
  }
129
 
 
 
 
 
130
  update_option($this->optionName, $settings);
131
 
132
  return $settings;
@@ -212,4 +228,17 @@ class Settings
212
  $settings['connections'][$key]['provider_settings'] = $connection;
213
  $this->saveGlobalSettings($settings);
214
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  }
74
 
75
  $settings['connections'] = $connections;
76
 
77
+
78
+ if($settings['mappings'] && $settings['connections']) {
79
+ $validMappings = array_keys(Arr::get($settings, 'connections', []));
80
+
81
+ $settings['mappings'] = array_filter($settings['mappings'], function ($key) use ($validMappings) {
82
+ return in_array($key, $validMappings);
83
+ });
84
+ }
85
+
86
  $misc = $this->getMisc();
87
 
88
  if(!$misc) {
118
  {
119
  $settings = $this->getSettings();
120
 
 
 
 
121
 
122
+ $mappings = $settings['mappings'];
123
+ $connections = $settings['connections'];
124
 
125
+ unset($connections[$key]);
126
+
127
+ foreach ($mappings as $mapKey => $mapValue) {
128
+ if($mapValue == $key) {
129
+ unset($mappings[$mapKey]);
130
+ }
131
  }
132
 
133
+ $settings['mappings'] = $mappings;
134
+ $settings['connections'] = $connections;
135
+
136
  if (Arr::get($settings, 'misc.default_connection') == $key) {
137
  $default = Arr::get($settings, 'mappings', []);
138
  $default = reset($default);
139
  Arr::set($settings, 'misc.default_connection', $default ?: '');
140
  }
141
 
142
+ if (Arr::get($settings, 'misc.fallback_connection') == $key) {
143
+ Arr::set($settings, 'misc.fallback_connection', '');
144
+ }
145
+
146
  update_option($this->optionName, $settings);
147
 
148
  return $settings;
228
  $settings['connections'][$key]['provider_settings'] = $connection;
229
  $this->saveGlobalSettings($settings);
230
  }
231
+
232
+ public function notificationSettings()
233
+ {
234
+ $defaults = [
235
+ 'enabled' => 'no',
236
+ 'notify_email' => '{site_admin}',
237
+ 'notify_days' => ['Mon']
238
+ ];
239
+
240
+ $settings = get_option('_fluent_smtp_notify_settings', []);
241
+
242
+ return wp_parse_args($settings, $defaults);
243
+ }
244
  }
app/Services/Converter.php ADDED
@@ -0,0 +1,475 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace FluentMail\App\Services;
4
+
5
+ use FluentMail\Includes\Support\Arr;
6
+
7
+ class Converter
8
+ {
9
+ public function getSuggestedConnection()
10
+ {
11
+ $wpMailSmtp = $this->maybeWPMailSmtp();
12
+ if($wpMailSmtp) {
13
+ return $wpMailSmtp;
14
+ }
15
+
16
+ $easySMTP = $this->maybeEasySmtp();
17
+ if($easySMTP) {
18
+ return $easySMTP;
19
+ }
20
+
21
+ return false;
22
+ }
23
+
24
+ private function maybeWPMailSmtp()
25
+ {
26
+ $wpMailSettings = get_option('wp_mail_smtp');
27
+ if (!$wpMailSettings) {
28
+ return false;
29
+ }
30
+
31
+ $mailSettings = Arr::get($wpMailSettings, 'mail', []);
32
+
33
+ $commonSettings = [
34
+ 'sender_name' => $this->maybeFromWPMailDefined('mail', 'from_name', Arr::get($mailSettings, 'from_name')),
35
+ 'sender_email' => $this->maybeFromWPMailDefined('mail', 'from_email', Arr::get($mailSettings, 'from_email')),
36
+ 'force_from_name' => Arr::get($mailSettings, 'from_name_force') == 1 ? 'yes' : 'no',
37
+ 'force_from_email' => Arr::get($mailSettings, 'from_email_force') == 1 ? 'yes' : 'no',
38
+ 'return_path' => Arr::get($mailSettings, 'return_path') == 1 ? 'yes' : 'no'
39
+ ];
40
+
41
+ // Let's try the SMTP First
42
+ $mailer = Arr::get($mailSettings, 'mailer');
43
+
44
+ if ($mailer == 'smtp') {
45
+ $smtp = Arr::get($wpMailSettings, 'smtp', []);
46
+ $auth = $this->maybeFromWPMailDefined('smtp', 'auth', Arr::get($smtp, 'auth')) == 1 ? 'yes' : 'no';
47
+
48
+ $userName = $this->maybeFromWPMailDefined('smtp', 'user', Arr::get($smtp, 'user'));
49
+ $password = $this->maybeFromWPMailDefined('smtp', 'pass', '');
50
+
51
+ if ($auth == 'yes') {
52
+ if (!$password) {
53
+ $password = $this->wpMailPassDecode(Arr::get($smtp, 'pass'));
54
+ }
55
+ }
56
+
57
+ $localSettings = [
58
+ 'host' => $this->maybeFromWPMailDefined('smtp', 'host', Arr::get($smtp, 'host')),
59
+ 'port' => $this->maybeFromWPMailDefined('smtp', 'port', Arr::get($smtp, 'port')),
60
+ 'auth' => $auth,
61
+ 'username' => $userName,
62
+ 'password' => $password,
63
+ 'auto_tls' => $this->maybeFromWPMailDefined('smtp', 'auto_tls', Arr::get($smtp, 'auto_tls')) == 1 ? 'yes' : 'no',
64
+ 'encryption' => $this->maybeFromWPMailDefined('smtp', 'encryption', Arr::get($smtp, 'encryption', 'none')),
65
+ 'key_store' => 'db',
66
+ 'provider' => 'smtp'
67
+ ];
68
+
69
+ $commonSettings = wp_parse_args($commonSettings, $localSettings);
70
+ } else if ($mailer == 'mailgun') {
71
+ $mailgun = Arr::get($wpMailSettings, 'mailgun', []);
72
+ $localSettings = [
73
+ 'api_key' => $this->maybeFromWPMailDefined('mailgun', 'api_key', Arr::get($mailgun, 'api_key')),
74
+ 'domain_name' => $this->maybeFromWPMailDefined('mailgun', 'domain', Arr::get($mailgun, 'domain')),
75
+ 'key_store' => 'db',
76
+ 'region' => strtolower($this->maybeFromWPMailDefined('mailgun', 'region', Arr::get($mailgun, 'region'))),
77
+ 'provider' => 'mailgun'
78
+ ];
79
+ $commonSettings = wp_parse_args($commonSettings, $localSettings);
80
+ unset($commonSettings['force_from_email']);
81
+ } else if ($mailer == 'sendinblue' || $mailer == 'sendgrid' || $mailer == 'pepipostapi') {
82
+ $local = Arr::get($wpMailSettings, $mailer, []);
83
+ $localSettings = [
84
+ 'api_key' => $this->maybeFromWPMailDefined($mailer, 'api_key', Arr::get($local, 'api_key')),
85
+ 'key_store' => 'db',
86
+ 'provider' => ($mailer == 'pepipostapi') ? 'pepipost' : $mailer
87
+ ];
88
+ $commonSettings = wp_parse_args($commonSettings, $localSettings);
89
+ unset($commonSettings['force_from_email']);
90
+ } else if ($mailer == 'amazonses') {
91
+ $local = Arr::get($wpMailSettings, $mailer, []);
92
+ $localSettings = [
93
+ 'access_key' => $this->maybeFromWPMailDefined($mailer, 'client_id', Arr::get($local, 'client_id')),
94
+ 'secret_key' => $this->maybeFromWPMailDefined($mailer, 'client_secret', Arr::get($local, 'client_secret')),
95
+ 'region' => $this->maybeFromWPMailDefined($mailer, 'region', Arr::get($local, 'region')),
96
+ 'key_store' => 'db',
97
+ 'provider' => 'ses'
98
+ ];
99
+
100
+ $commonSettings = wp_parse_args($commonSettings, $localSettings);
101
+ } else if ($mailer == 'mail') {
102
+ $commonSettings['provider'] = 'default';
103
+ } else {
104
+ return false;
105
+ }
106
+
107
+ return [
108
+ 'title' => 'Import data from your current plugin (WP Mail SMTP)',
109
+ 'subtitle' => 'We have detected other SMTP plugin\'s settings available on your site. Click bellow to pre-populate the values',
110
+ 'settings' => $commonSettings,
111
+ 'button_text' => 'Import From WP Mail SMTP'
112
+ ];
113
+ }
114
+
115
+ private function wpMailPassDecode($encrypted)
116
+ {
117
+ if (apply_filters('wp_mail_smtp_helpers_crypto_stop', false)) {
118
+ return $encrypted;
119
+ }
120
+
121
+ if (!function_exists('\mb_strlen') || !function_exists('\mb_substr') || !function_exists('\sodium_crypto_secretbox_open')) {
122
+ return $encrypted;
123
+ }
124
+
125
+ // Unpack base64 message.
126
+ $decoded = base64_decode($encrypted); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
127
+
128
+ if (false === $decoded) {
129
+ return $encrypted;
130
+ }
131
+
132
+ if (mb_strlen($decoded, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) { // phpcs:ignore
133
+ return $encrypted;
134
+ }
135
+
136
+ // Pull nonce and ciphertext out of unpacked message.
137
+ $nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit'); // phpcs:ignore
138
+ $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit'); // phpcs:ignore
139
+
140
+ $secret_key = $this->getWPMailSecretKey();
141
+
142
+ if (empty($secret_key)) {
143
+ return $encrypted;
144
+ }
145
+
146
+ // Decrypt it.
147
+ $message = sodium_crypto_secretbox_open( // phpcs:ignore
148
+ $ciphertext,
149
+ $nonce,
150
+ $secret_key
151
+ );
152
+
153
+ // Check for decryption failures.
154
+ if (false === $message) {
155
+ return $encrypted;
156
+ }
157
+
158
+ return $message;
159
+ }
160
+
161
+ private function getWPMailSecretKey()
162
+ {
163
+ if (defined('WPMS_CRYPTO_KEY')) {
164
+ return WPMS_CRYPTO_KEY;
165
+ }
166
+
167
+ $secret_key = get_option('wp_mail_smtp_mail_key');
168
+ $secret_key = apply_filters('wp_mail_smtp_helpers_crypto_get_secret_key', $secret_key);
169
+
170
+ // If we already have the secret, send it back.
171
+ if (false !== $secret_key) {
172
+ $secret_key = base64_decode($secret_key); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
173
+ }
174
+
175
+ return $secret_key;
176
+ }
177
+
178
+ private function maybeFromWPMailDefined($group, $key, $value)
179
+ {
180
+
181
+ if (!defined('WPMS_ON') || !WPMS_ON) {
182
+ return $value;
183
+ }
184
+
185
+ // Just to feel safe.
186
+ $group = sanitize_key($group);
187
+ $key = sanitize_key($key);
188
+ $return = false;
189
+
190
+ switch ($group) {
191
+ case 'mail':
192
+ switch ($key) {
193
+ case 'from_name':
194
+ if (defined('WPMS_MAIL_FROM_NAME') && WPMS_MAIL_FROM_NAME) {
195
+ $value = WPMS_MAIL_FROM_NAME;
196
+ }
197
+ break;
198
+ case 'from_email':
199
+