PluginProbe
Mailgun for WordPress / trunk
Mailgun for WordPress vtrunk
2.2.3 2.2.2 2.2.1 trunk 1.9.9 2.0.0 2.0.1 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0
mailgun / includes / admin.php

admin.php in Mailgun for WordPress trunk, at includes/admin.php

533 lines 18.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * mailgun-wordpress-plugin - Sending mail from WordPress using Mailgun
4 * Copyright (C) 2016 Mailgun, et al.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * @package Mailgun
21 */
22 class MailgunAdmin extends Mailgun {
23
24 /**
25 * @var array Array of "safe" option defaults.
26 */
27 private array $defaults;
28
29 /**
30 * @var array
31 */
32 protected array $options = [];
33
34 /**
35 * @var string $hook_suffix
36 */
37 protected $hook_suffix;
38
39 /**
40 * Setup backend functionality in WordPress.
41 *
42 * @return void
43 */
44 public function __construct() {
45 parent::__construct();
46
47 $this->init();
48
49 // Load localizations if available. Deferred to `init` because WordPress 6.7+
50 // triggers a "translation loading was triggered too early" notice when a text
51 // domain is loaded before the `init` action.
52 add_action(
53 'init',
54 static function (): void {
55 load_plugin_textdomain('mailgun', false, 'mailgun/languages');
56 }
57 );
58
59 // Activation hook
60 register_activation_hook($this->plugin_file, array( &$this, 'activation' ));
61
62 // Hook into admin_init and register settings and potentially register an admin_notice
63 add_action('admin_init', array( &$this, 'admin_init' ));
64
65 // Activate the options page
66 add_action('admin_menu', array( &$this, 'admin_menu' ));
67
68 // Register an AJAX action for testing mail sending capabilities
69 add_action('wp_ajax_mailgun-test', array( &$this, 'ajax_send_test' ));
70 }
71
72 /**
73 * Adds the default options during plugin activation.
74 *
75 * @return void
76 */
77 public function activation(): void {
78 if ( ! $this->options) {
79 $this->options = $this->defaults;
80 add_option('mailgun', $this->options);
81 }
82 }
83
84
85 /**
86 * Initialize the default property.
87 *
88 * @return void
89 */
90 public function init(): void {
91 $sitename = sanitize_text_field(strtolower($_SERVER['SERVER_NAME'] ?? site_url()));
92 if (substr($sitename, 0, 4) === 'www.') {
93 $sitename = substr($sitename, 4);
94 }
95
96 $region = ( defined('MAILGUN_REGION') && MAILGUN_REGION ) ? MAILGUN_REGION : $this->get_option('region');
97 $regionDefault = $region ?: 'us';
98
99 $this->defaults = array(
100 'region' => $regionDefault,
101 'useAPI' => '1',
102 'apiKey' => '',
103 'domain' => '',
104 'username' => '',
105 'password' => '',
106 'secure' => '1',
107 'sectype' => 'tls',
108 'track-clicks' => '',
109 'track-opens' => '',
110 'campaign-id' => '',
111 'override-from' => '0',
112 'tag' => $sitename,
113 );
114 }
115
116 /**
117 * Add the options page.
118 *
119 * @return void
120 */
121 public function admin_menu(): void {
122 if (current_user_can('manage_options')) {
123 $this->hook_suffix = add_options_page(
124 __('Mailgun', 'mailgun'),
125 __('Mailgun', 'mailgun'),
126 'manage_options',
127 'mailgun',
128 array( &$this, 'options_page' )
129 );
130 add_options_page(
131 __('Mailgun Lists', 'mailgun'),
132 __('Mailgun Lists', 'mailgun'),
133 'manage_options',
134 'mailgun-lists',
135 array( &$this, 'lists_page' )
136 );
137 add_action("admin_print_scripts-{$this->hook_suffix}", array( &$this, 'admin_js' ));
138 add_filter("plugin_action_links_{$this->plugin_basename}", array( &$this, 'filter_plugin_actions' ));
139 add_action("admin_footer-{$this->hook_suffix}", array( &$this, 'admin_footer_js' ));
140 }
141 }
142
143 /**
144 * Enqueue javascript required for the admin settings page.
145 *
146 * @return void
147 */
148 public function admin_js(): void {
149 wp_enqueue_script('jquery');
150 }
151
152 /**
153 * Output JS to footer for enhanced admin page functionality.
154 */
155 public function admin_footer_js(): void {
156 ?>
157 <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css">
158 <script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
159 <script type="text/javascript">
160
161 /* <![CDATA[ */
162 var mailgunApiOrNot = function () {
163 if (jQuery('#mailgun-api').val() == 1) {
164 jQuery('.mailgun-smtp').hide()
165 jQuery('.mailgun-api').show()
166 } else {
167 jQuery('.mailgun-api').hide()
168 jQuery('.mailgun-smtp').show()
169 }
170
171 }
172 var formModified = false
173 jQuery(function () {
174 mailgunApiOrNot()
175 jQuery('#mailgun-api').change(function () {
176 mailgunApiOrNot()
177 })
178 jQuery('#mailgun-test').click(function (e) {
179 e.preventDefault()
180 if (formModified) {
181 var doTest = confirm('<?php _e('The Mailgun plugin configuration has changed since you last saved. Do you wish to test anyway?\n\nClick "Cancel" and then "Save Changes" if you wish to save your changes.',
182 'mailgun'); ?>')
183 if (!doTest) {
184 return false
185 }
186 }
187 jQuery(this).val('<?php _e('Testing...', 'mailgun'); ?>')
188 jQuery('#mailgun-test-result').text('')
189 jQuery.get(
190 ajaxurl,
191 {
192 action: 'mailgun-test',
193 _wpnonce: '<?php echo esc_attr(wp_create_nonce()); ?>'
194 }
195 )
196 .always(function () {
197 jQuery('#mailgun-test').val('<?php _e('Test Configuration', 'mailgun'); ?>')
198 })
199 .done(function (data) {
200 if (typeof data.message !== 'undefined' && data.message === 'Failure') {
201 toastr.error('Mailgun ' + data.method + ' Test ' + data.message
202 + '; status "' + data.error + '"');
203 } else {
204 toastr.success('Mailgun ' + data.method + ' Test ' + data.message
205 + '; status "' + data.error + '"');
206 }
207 })
208 .fail(function () {
209 toastr.error('Mailgun Test <?php _e('Failure', 'mailgun'); ?>')
210 })
211 })
212 jQuery('#mailgun-form').change(function () {
213 formModified = true
214 })
215 })
216 /* ]]> */
217 </script>
218 <?php
219 }
220
221 /**
222 * Output the options page.
223 *
224 * @return void
225 */
226 public function options_page(): void {
227 if ( ! @include 'options-page.php') {
228 printf(
229 __(
230 '<div id="message" class="updated fade"><p>The options page for the <strong>Mailgun</strong> plugin cannot be displayed. The file <strong>%s</strong> is missing. Please reinstall the plugin.</p></div>',
231 'mailgun'
232 ),
233 __DIR__ . '/options-page.php'
234 );
235 }
236 }
237
238 /**
239 * Output the lists page.
240 *
241 * @return void
242 */
243 public function lists_page(): void {
244 if ( ! @include 'lists-page.php') {
245 printf(
246 __(
247 '<div id="message" class="updated fade"><p>The lists page for the <strong>Mailgun</strong> plugin cannot be displayed. The file <strong>%s</strong> is missing. Please reinstall the plugin.</p></div>',
248 'mailgun'
249 ),
250 __DIR__ . '/lists-page.php'
251 );
252 }
253 }
254
255 /**
256 * Wrapper function hooked into admin_init to register settings
257 * and potentially register an admin notice if the plugin hasn't
258 * been configured yet.
259 *
260 * @return void
261 */
262 public function admin_init(): void {
263 $this->register_settings();
264
265 add_action('admin_notices', array( &$this, 'admin_notices' ));
266 }
267
268 /**
269 * Whitelist the mailgun options.
270 *
271 * @return void
272 */
273 public function register_settings(): void {
274 register_setting('mailgun', 'mailgun', array( &$this, 'validation' ));
275 }
276
277 /**
278 * Data validation callback function for options.
279 *
280 * @param array $options An array of options posted from the options page
281 *
282 * @return array
283 */
284 public function validation( array $options ): array {
285 $apiKey = trim($options['apiKey']);
286 $username = trim($options['username']);
287 if ( ! empty($apiKey)) {
288 $pos = strpos($apiKey, 'api:');
289 if ($pos !== false && $pos == 0) {
290 $apiKey = substr($apiKey, 4);
291 }
292
293 $options['apiKey'] = $apiKey;
294 }
295
296 if ( ! empty($username)) {
297 $username = preg_replace('/@.+$/', '', $username);
298 $options['username'] = $username;
299 }
300
301 foreach ($options as $key => $value) {
302 $options[ $key ] = trim($value);
303 }
304
305 if (empty($options['override-from'])) {
306 $options['override-from'] = $this->defaults['override-from'];
307 }
308
309 if (empty($options['sectype'])) {
310 $options['sectype'] = $this->defaults['sectype'];
311 }
312
313 $this->options = $options;
314
315 return $options;
316 }
317
318 /**
319 * Function to output an admin notice
320 * when plugin settings or constants need to be configured
321 *
322 * @return void
323 */
324 public function admin_notices(): void {
325 $screen = get_current_screen();
326 if ( ! isset($screen)) {
327 return;
328 }
329 if ( ! current_user_can('manage_options') || $screen->id === $this->hook_suffix) {
330 return;
331 }
332
333 $smtpPasswordUndefined = ( ! $this->get_option('password') && ( ! defined('MAILGUN_PASSWORD') || ! MAILGUN_PASSWORD ) );
334 $smtpActiveNotConfigured = ( $this->get_option('useAPI') === '0' && $smtpPasswordUndefined );
335 $apiRegionUndefined = ( ! $this->get_option('region') && ( ! defined('MAILGUN_REGION') || ! MAILGUN_REGION ) );
336 $apiKeyUndefined = ( ! $this->get_option('apiKey') && ( ! defined('MAILGUN_APIKEY') || ! MAILGUN_APIKEY ) );
337 $apiActiveNotConfigured = ( $this->get_option('useAPI') === '1' && ( $apiRegionUndefined || $apiKeyUndefined ) );
338
339 if (isset($_SESSION) && ( ! isset($_SESSION['settings_turned_of']) || $_SESSION['settings_turned_of'] === false ) && ( $apiActiveNotConfigured || $smtpActiveNotConfigured )) {
340 ?>
341 <div id='mailgun-warning' class='notice notice-warning is-dismissible'>
342 <p>
343 <?php
344 printf(
345 __(
346 'Use HTTP API is turned off or you do not have SMTP credentials. You can configure your Mailgun settings in your wp-config.php file or <a href="%1$s">here</a>',
347 'mailgun'
348 ),
349 menu_page_url('mailgun', false)
350 );
351 ?>
352 </p>
353 </div>
354 <?php $_SESSION['settings_turned_of'] = true; ?>
355 <?php } ?>
356
357 <?php
358 if ($this->get_option('override-from') === '1' &&
359 ( ! $this->get_option('from-name') || ! $this->get_option('from-address') )
360 ) {
361 ?>
362 <div id='mailgun-warning' class='notice notice-warning is-dismissible'>
363 <p>
364 <strong>
365 <?php _e('Mailgun is almost ready. ', 'mailgun'); ?>
366 </strong>
367 <?php
368 printf(
369 __(
370 '"Override From" option requires that "From Name" and "From Address" be set to work properly! <a href="%1$s">Configure Mailgun now</a>.',
371 'mailgun'
372 ),
373 menu_page_url('mailgun', false)
374 );
375 ?>
376 </p>
377 </div>
378 <?php
379 }
380 }
381
382 /**
383 * Add a settings link to the plugin actions.
384 *
385 * @param array $links Array of the plugin action links
386 *
387 * @return array
388 */
389 public function filter_plugin_actions( array $links ): array {
390 $settings_link = '<a href="' . menu_page_url('mailgun', false) . '">' . __('Settings', 'mailgun') . '</a>';
391 array_unshift($links, $settings_link);
392
393 return $links;
394 }
395
396 /**
397 * AJAX callback function to test mail sending functionality.
398 *
399 * @return void
400 * @throws JsonException
401 */
402 public function ajax_send_test(): void {
403 nocache_headers();
404 header('Content-Type: application/json');
405
406 if ( ! current_user_can('manage_options') || ! wp_verify_nonce(sanitize_text_field($_GET['_wpnonce']))) {
407 die(
408 json_encode(
409 array(
410 'message' => __('Unauthorized', 'mailgun'),
411 'method' => null,
412 'error' => __('Unauthorized', 'mailgun'),
413 ),
414 JSON_THROW_ON_ERROR
415 )
416 );
417 }
418
419 $getRegion = ( defined('MAILGUN_REGION') && MAILGUN_REGION ) ? MAILGUN_REGION : $this->get_option('region');
420 $useAPI = ( defined('MAILGUN_USEAPI') && MAILGUN_USEAPI ) ? MAILGUN_USEAPI : $this->get_option('useAPI');
421 $secure = ( defined('MAILGUN_SECURE') && MAILGUN_SECURE ) ? MAILGUN_SECURE : $this->get_option('secure');
422 $sectype = ( defined('MAILGUN_SECTYPE') && MAILGUN_SECTYPE ) ? MAILGUN_SECTYPE : $this->get_option('sectype');
423 $replyTo = ( defined('MAILGUN_REPLY_TO_ADDRESS') && MAILGUN_REPLY_TO_ADDRESS ) ? MAILGUN_REPLY_TO_ADDRESS : $this->get_option('reply_to');
424
425 if ( ! $getRegion) {
426 mg_api_last_error(__('Region has not been selected', 'mailgun'));
427 } else {
428 if ($getRegion === 'us') {
429 $region = __('U.S./North America', 'mailgun');
430 }
431 if ($getRegion === 'eu') {
432 $region = __('Europe', 'mailgun');
433 }
434 }
435
436 if ($useAPI) {
437 $method = __('HTTP API', 'mailgun');
438 } else {
439 $method = ( $secure ) ? __('Secure SMTP', 'mailgun') : __('Insecure SMTP', 'mailgun');
440 if ($secure) {
441 $method .= sprintf(__(' via %s', 'mailgun'), $sectype);
442 }
443 }
444
445 $admin_email = get_option('admin_email');
446 if ( ! $admin_email) {
447 die(
448 json_encode(
449 array(
450 'message' => __('Admin Email is empty', 'mailgun'),
451 'method' => $method,
452 'error' => __('Admin Email is empty', 'mailgun'),
453 ),
454 JSON_THROW_ON_ERROR
455 )
456 );
457 }
458
459 try {
460 $headers = array(
461 'Content-Type: text/plain',
462 'Reply-To: ' . $replyTo,
463 );
464
465 $result = wp_mail(
466 $admin_email,
467 __('Mailgun WordPress Plugin Test', 'mailgun'),
468 sprintf(
469 __(
470 "This is a test email generated by the Mailgun WordPress plugin.\n\nIf you have received this message, the requested test has succeeded.\n\nThe sending region is set to %s.\n\nThe method used to send this email was: %s.",
471 'mailgun'
472 ),
473 $region,
474 $method
475 ),
476 $headers
477 );
478 } catch (Throwable $throwable) {
479 // Log purpose
480 }
481
482 if ($useAPI) {
483 if ( ! function_exists('mg_api_last_error')) {
484 if ( ! include __DIR__ . '/wp-mail-api.php') {
485 $this->deactivate_and_die(__DIR__ . '/wp-mail-api.php');
486 }
487 }
488 $error_msg = mg_api_last_error();
489 } else {
490 if ( ! function_exists('mg_smtp_last_error')) {
491 if ( ! include __DIR__ . '/wp-mail-smtp.php') {
492 $this->deactivate_and_die(__DIR__ . '/wp-mail-smtp.php');
493 }
494 }
495 $error_msg = mg_smtp_last_error();
496 }
497
498 // Admin Email is used as 'to' parameter, but in case of 'Test Configuration' this message is not clear for the user, so replaced with more appropriate one
499 if (str_contains($error_msg, "'to'") && str_contains($error_msg, 'is not a valid')) {
500 $error_msg = sprintf(
501 "Administration Email Address (%s) is not valid and can't be used for test, you can change it at General Setting page",
502 $admin_email
503 );
504 }
505
506 if ($result) {
507 die(
508 json_encode(
509 array(
510 'message' => __('Success', 'mailgun'),
511 'method' => $method,
512 'error' => __('Success', 'mailgun'),
513 ),
514 JSON_THROW_ON_ERROR
515 )
516 );
517 }
518
519 // Error message will always be returned in case of failure, if not - connection wasn't successful
520 $error_msg = $error_msg ?: "Can't connect to Mailgun";
521 die(
522 json_encode(
523 array(
524 'message' => __('Failure', 'mailgun'),
525 'method' => $method,
526 'error' => $error_msg,
527 ),
528 JSON_THROW_ON_ERROR
529 )
530 );
531 }
532 }
533