PluginProbe ʕ •ᴥ•ʔ
WP Job Manager / 2.4.7
WP Job Manager v2.4.7
2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.1.0 1.1.1 1.1.2 1.1.3 1.10.0 1.11.0 1.11.1 1.12.0 1.12.1 1.13.0 1.14.0 1.15.0 1.16.0 1.16.1 1.17.0 1.18.0 1.19.0 1.2.0 1.20.0 1.20.1 1.21.0 1.21.1 1.21.2 1.21.3 1.21.4 1.22.0 1.22.1 1.22.2 1.22.3 1.23.0 1.23.1 1.23.10 1.23.11 1.23.12 1.23.13 1.23.2 1.23.3 1.23.4 1.23.5 1.23.6 1.23.7 1.23.8 1.23.9 1.24.0 1.24.0.1 1.25.0 1.25.0.1 1.25.1 1.25.1.1 1.25.2 1.25.2.1 1.25.3 1.25.3.1 1.26.0 1.26.0.1 1.26.1 1.26.1.1 1.26.2 1.26.2.1 1.27.0 1.27.0.1 1.28.0 1.28.0.1 1.29.0 1.29.0.1 1.29.1 1.29.1.1 1.29.2 1.29.2.1 1.29.3 1.29.3.1 1.3.0 1.3.1 1.30.0 1.30.0.1 1.30.1 1.30.1.1 1.30.2 1.30.2.1 1.31.0 1.31.0.1 1.31.1 1.31.1.1 1.31.2 1.31.3 1.32.0 1.32.1 1.32.2 1.32.3 1.33.0 1.33.1 1.33.2 1.33.3 1.33.4 1.33.5 1.34.0 1.34.1 1.34.2 1.34.3 1.34.4 1.34.5 1.35.0 1.35.1 1.35.2 1.35.3 1.36.0 1.36.1 1.36.2 1.37.0 1.38.0 1.38.1 1.39.0 1.4.0 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.5.0 1.5.1 1.5.2 1.6.0 1.7.0 1.7.1 1.7.3 1.8.0 1.8.1 1.8.2 1.9.0 1.9.1 1.9.2 1.9.3 2.0.0 2.1.0 2.1.1 2.2.0 2.2.1 2.2.2 2.3.0 2.4.0 2.4.1
wp-job-manager / includes / class-wp-job-manager-email-notifications.php
wp-job-manager / includes Last commit date
3rd-party 4 days ago abstracts 3 months ago admin 2 weeks ago emails 2 weeks ago forms 4 days ago helper 3 months ago promoted-jobs 4 days ago ui 2 weeks ago widgets 2 weeks ago class-access-token.php 2 years ago class-dev-tools.php 2 years ago class-guest-session.php 2 years ago class-guest-user.php 2 years ago class-job-dashboard-shortcode.php 6 months ago class-job-listing-stats.php 2 years ago class-job-overlay.php 2 years ago class-stats-dashboard.php 2 years ago class-stats-script.php 3 months ago class-stats.php 3 months ago class-wp-job-manager-ajax.php 2 months ago class-wp-job-manager-api.php 6 years ago class-wp-job-manager-blocks.php 2 weeks ago class-wp-job-manager-cache-helper.php 3 months ago class-wp-job-manager-category-walker.php 6 years ago class-wp-job-manager-com-api.php 2 years ago class-wp-job-manager-data-cleaner.php 2 years ago class-wp-job-manager-data-exporter.php 3 months ago class-wp-job-manager-dependency-checker.php 2 years ago class-wp-job-manager-email-notifications.php 2 years ago class-wp-job-manager-forms.php 5 years ago class-wp-job-manager-geocode.php 2 weeks ago class-wp-job-manager-install.php 2 years ago class-wp-job-manager-post-types.php 4 days ago class-wp-job-manager-recaptcha.php 6 months ago class-wp-job-manager-rest-api.php 2 months ago class-wp-job-manager-shortcodes.php 2 weeks ago class-wp-job-manager-usage-tracking-data.php 2 years ago class-wp-job-manager-usage-tracking.php 2 years ago class-wp-job-manager-widget.php 2 weeks ago class-wp-job-manager.php 3 months ago trait-singleton.php 2 years ago
class-wp-job-manager-email-notifications.php
965 lines
1 <?php
2 /**
3 * File containing the class WP_Job_Manager_Email_Notifications.
4 *
5 * @package wp-job-manager
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 use Pelago\Emogrifier\CssInliner;
13 use Pelago\Emogrifier\HtmlProcessor\CssToAttributeConverter;
14 /**
15 * Base class for WP Job Manager's email notification system.
16 *
17 * @since 1.31.0
18 */
19 final class WP_Job_Manager_Email_Notifications {
20 const EMAIL_SETTING_PREFIX = 'job_manager_email_';
21 const EMAIL_SETTING_ENABLED = 'enabled';
22 const EMAIL_SETTING_PLAIN_TEXT = 'plain_text';
23
24 /**
25 * Notifications to be scheduled.
26 *
27 * @var array
28 */
29 private static $deferred_notifications = [];
30
31 /**
32 * Sets up initial hooks.
33 *
34 * @static
35 */
36 public static function init() {
37 add_action( 'job_manager_send_notification', [ __CLASS__, 'schedule_notification' ], 10, 2 );
38 add_action( 'job_manager_email_init', [ __CLASS__, 'lazy_init' ] );
39 add_action( 'job_manager_email_job_details', [ __CLASS__, 'output_job_details' ], 10, 4 );
40 add_action( 'job_manager_email_header', [ __CLASS__, 'output_header' ], 10, 3 );
41 add_action( 'job_manager_email_footer', [ __CLASS__, 'output_footer' ], 10, 3 );
42 add_action( 'job_manager_email_daily_notices', [ __CLASS__, 'send_employer_expiring_notice' ] );
43 add_action( 'job_manager_email_daily_notices', [ __CLASS__, 'send_admin_expiring_notice' ] );
44 add_filter( 'job_manager_settings', [ __CLASS__, 'add_job_manager_email_settings' ], 1 );
45 add_action( 'job_manager_job_submitted', [ __CLASS__, 'send_new_job_notification' ] );
46 add_action( 'job_manager_user_edit_job_listing', [ __CLASS__, 'send_updated_job_notification' ] );
47 }
48
49 /**
50 * Gets list of email notifications handled by WP Job Manager core.
51 *
52 * @return array
53 */
54 public static function core_email_notifications() {
55 return [
56 'WP_Job_Manager_Email_Admin_New_Job',
57 'WP_Job_Manager_Email_Admin_Updated_Job',
58 'WP_Job_Manager_Email_Admin_Expiring_Job',
59 'WP_Job_Manager_Email_Employer_Expiring_Job',
60 ];
61 }
62
63 /**
64 * Sets up an email notification to be sent at the end of the script's execution.
65 *
66 * Do not call manually.
67 *
68 * @access private
69 *
70 * @param string $notification
71 * @param array $args
72 */
73 public static function schedule_notification( $notification, $args = [] ) {
74 self::maybe_init();
75
76 self::$deferred_notifications[] = [ $notification, $args ];
77 }
78
79 /**
80 * Sends all notifications collected during execution.
81 *
82 * Do not call manually.
83 *
84 * @access private
85 */
86 public static function send_deferred_notifications() {
87 $email_notifications = self::get_email_notifications( true );
88
89 foreach ( self::$deferred_notifications as $email ) {
90 if (
91 ! is_string( $email[0] )
92 || ! isset( $email_notifications[ $email[0] ] )
93 ) {
94 continue;
95 }
96
97 $email_class = $email_notifications[ $email[0] ];
98 $email_notification_key = $email[0];
99 $email_args = is_array( $email[1] ) ? $email[1] : [];
100
101 self::send_email( $email[0], new $email_class( $email_args, self::get_email_settings( $email_notification_key ) ) );
102 }
103
104 self::$deferred_notifications = [];
105 }
106
107 /**
108 * Send email notifications for a specific email now and return the status. Not usually necessary but useful if you
109 * need to check to make sure a notification was sent.
110 *
111 * @param string $email_key Email key to send notifications for.
112 * @return bool True if an email notification was sent.
113 */
114 public static function send_notifications( $email_key ) {
115 $email_sent = false;
116 $email_notifications = self::get_email_notifications( true );
117 foreach ( self::$deferred_notifications as $index => $email ) {
118 if (
119 ! is_string( $email[0] )
120 || ! isset( $email_notifications[ $email[0] ] )
121 || $email[0] !== $email_key
122 ) {
123 continue;
124 }
125
126 $email_class = $email_notifications[ $email[0] ];
127 $email_args = is_array( $email[1] ) ? $email[1] : [];
128
129 if ( self::send_email( $email[0], new $email_class( $email_args, self::get_email_settings( $email_key ) ) ) ) {
130 $email_sent = true;
131 }
132
133 unset( self::$deferred_notifications[ $index ] );
134 }
135
136 return $email_sent;
137 }
138
139 /**
140 * Initialize if necessary.
141 */
142 public static function maybe_init() {
143 if ( 0 === did_action( 'job_manager_email_init' ) ) {
144 /**
145 * Lazily load remaining files needed for email notifications. Do this here instead of in
146 * `shutdown` for proper logging in case of syntax errors.
147 *
148 * @since 1.31.0
149 */
150 do_action( 'job_manager_email_init' );
151 }
152 }
153
154 /**
155 * Include email files.
156 *
157 * Do not call manually.
158 *
159 * @access private
160 */
161 public static function lazy_init() {
162 add_action( 'shutdown', [ __CLASS__, 'send_deferred_notifications' ] );
163
164 include_once JOB_MANAGER_PLUGIN_DIR . '/includes/emails/class-wp-job-manager-email-admin-new-job.php';
165 include_once JOB_MANAGER_PLUGIN_DIR . '/includes/emails/class-wp-job-manager-email-admin-updated-job.php';
166 include_once JOB_MANAGER_PLUGIN_DIR . '/includes/emails/class-wp-job-manager-email-employer-expiring-job.php';
167 include_once JOB_MANAGER_PLUGIN_DIR . '/includes/emails/class-wp-job-manager-email-admin-expiring-job.php';
168
169 // Load Vendor Autoload.
170 require_once JOB_MANAGER_PLUGIN_DIR . '/vendor/autoload.php';
171 }
172
173 /**
174 * Clear the deferred notifications email array.
175 *
176 * Do not call manually. Only for help with tests.
177 *
178 * @access private
179 */
180 public static function clear_deferred_notifications() {
181 if ( ! defined( 'PHPUNIT_WPJM_TESTSUITE' ) || ! PHPUNIT_WPJM_TESTSUITE ) {
182 die( 'This is just for use while testing' );
183 }
184 self::$deferred_notifications = [];
185 }
186
187 /**
188 * Gets a list of all email notifications that WP Job Manager handles.
189 *
190 * @param bool $enabled_notifications_only
191 * @return array
192 */
193 public static function get_email_notifications( $enabled_notifications_only = false ) {
194 self::maybe_init();
195
196 /**
197 * Retrieves all email notifications to be sent.
198 *
199 * @since 1.31.0
200 *
201 * @param array $email_notifications All the email notifications to be registered.
202 */
203 $email_notification_classes = array_unique( apply_filters( 'job_manager_email_notifications', self::core_email_notifications() ) );
204 $email_notifications = [];
205
206 /**
207 * Email class in loop.
208 *
209 * @var WP_Job_Manager_Email $email_class
210 */
211 foreach ( $email_notification_classes as $email_class ) {
212 // Check to make sure email notification is valid.
213 if ( ! self::is_email_notification_valid( $email_class ) ) {
214 continue;
215 }
216
217 // PHP 5.2: Using `call_user_func()` but `$email_class::get_key()` preferred.
218 $email_notification_key = call_user_func( [ $email_class, 'get_key' ] );
219 if (
220 isset( $email_notifications[ $email_notification_key ] )
221 || ( $enabled_notifications_only && ! self::is_email_notification_enabled( $email_notification_key ) )
222 ) {
223 continue;
224 }
225
226 $email_notifications[ $email_notification_key ] = $email_class;
227 }
228
229 return $email_notifications;
230 }
231
232 /**
233 * Show details about the job listing.
234 *
235 * @param WP_Post $job The job listing to show details for.
236 * @param WP_Job_Manager_Email $email Email object for the notification.
237 * @param bool $sent_to_admin True if this is being sent to an administrator.
238 * @param bool $plain_text True if the email is being sent as plain text.
239 */
240 public static function output_job_details( $job, $email, $sent_to_admin, $plain_text = false ) {
241 $template_segment = self::locate_template_file( 'email-job-details', $plain_text );
242 if ( ! file_exists( $template_segment ) ) {
243 return;
244 }
245
246 $fields = self::get_job_detail_fields( $job, $sent_to_admin, $plain_text );
247
248 include $template_segment;
249 }
250
251 /**
252 * Get the job fields to show in email templates.
253 *
254 * @param WP_Post $job
255 * @param bool $sent_to_admin
256 * @param bool $plain_text
257 * @return array
258 */
259 private static function get_job_detail_fields( WP_Post $job, $sent_to_admin, $plain_text = false ) {
260 $fields = [];
261
262 $fields['job_title'] = [
263 'label' => __( 'Job title', 'wp-job-manager' ),
264 'value' => $job->post_title,
265 ];
266
267 if ( $sent_to_admin || 'publish' === $job->post_status ) {
268 $fields['job_title']['url'] = get_permalink( $job );
269 }
270
271 $job_location = get_the_job_location( $job );
272 if ( ! empty( $job_location ) ) {
273 $fields['job_location'] = [
274 'label' => __( 'Location', 'wp-job-manager' ),
275 'value' => $job_location,
276 ];
277 }
278
279 if ( get_option( 'job_manager_enable_types' ) && wp_count_terms( \WP_Job_Manager_Post_Types::TAX_LISTING_TYPE ) > 0 ) {
280 $job_types = wpjm_get_the_job_types( $job );
281 if ( ! empty( $job_types ) ) {
282 $fields['job_type'] = [
283 'label' => __( 'Job type', 'wp-job-manager' ),
284 'value' => implode( ', ', wp_list_pluck( $job_types, 'name' ) ),
285 ];
286 }
287 }
288
289 if ( get_option( 'job_manager_enable_categories' ) && wp_count_terms( \WP_Job_Manager_Post_Types::TAX_LISTING_CATEGORY ) > 0 ) {
290 $job_categories = wpjm_get_the_job_categories( $job );
291 if ( ! empty( $job_categories ) ) {
292 $fields['job_category'] = [
293 'label' => __( 'Job category', 'wp-job-manager' ),
294 'value' => implode( ', ', wp_list_pluck( $job_categories, 'name' ) ),
295 ];
296 }
297 }
298
299 $company_name = get_the_company_name( $job );
300 if ( ! empty( $company_name ) ) {
301 $fields['company_name'] = [
302 'label' => __( 'Company name', 'wp-job-manager' ),
303 'value' => $company_name,
304 ];
305 }
306
307 $company_website = get_the_company_website( $job );
308 if ( ! empty( $company_website ) ) {
309 $fields['company_website'] = [
310 'label' => __( 'Company website', 'wp-job-manager' ),
311 'value' => $plain_text ? $company_website : sprintf( '<a href="%1$s">%1$s</a>', esc_url( $company_website, [ 'http', 'https' ] ) ),
312 ];
313 }
314
315 $job_expires = WP_Job_Manager_Post_Types::instance()->get_job_expiration( $job );
316 $wp_date_format = get_option( 'date_format' ) ?: JOB_MANAGER_DATE_FORMAT_FALLBACK;
317 if ( ! empty( $job_expires ) ) {
318 $job_expires_str = wp_date( $wp_date_format, $job_expires->getTimestamp() );
319 $fields['job_expires'] = [
320 'label' => __( 'Listing expires', 'wp-job-manager' ),
321 'value' => $job_expires_str,
322 ];
323 }
324
325 if ( $sent_to_admin ) {
326 $author = get_user_by( 'ID', $job->post_author );
327 if ( $author instanceof WP_User ) {
328 $fields['author'] = [
329 'label' => __( 'Posted by', 'wp-job-manager' ),
330 'value' => $author->user_nicename,
331 'url' => 'mailto:' . $author->user_email,
332 ];
333 }
334 }
335
336 /**
337 * Modify the fields shown in email notifications in the details summary a job listing.
338 *
339 * @since 1.31.0
340 *
341 * @param array $fields {
342 * Array of fields. Each field is keyed with a unique identifier.
343 * {
344 * @type string $label Label to show next to field.
345 * @type string $value Value for field.
346 * @type string $url URL to provide with the value (optional).
347 * }
348 * }
349 * @param WP_Post $job Job listing.
350 * @param bool $sent_to_admin True if being sent in an admin notification.
351 * @param bool $plain_text True if being sent as plain text.
352 */
353 return apply_filters( 'job_manager_emails_job_detail_fields', $fields, $job, $sent_to_admin, $plain_text );
354 }
355
356 /**
357 * Output email header.
358 *
359 * @param string $email_notification_key Email notification key for email being sent.
360 * @param bool $sent_to_admin True if this is being sent to an administrator.
361 * @param bool $plain_text True if the email is being sent as plain text.
362 */
363 public static function output_header( $email_notification_key, $sent_to_admin, $plain_text = false ) {
364 $template_segment = self::email_template_path_alternative( $email_notification_key, 'email-header', $plain_text );
365 if ( false === $template_segment ) {
366 $template_segment = self::locate_template_file( 'email-header', $plain_text );
367 }
368 if ( ! $template_segment || ! file_exists( $template_segment ) ) {
369 return;
370 }
371 include $template_segment;
372 }
373
374 /**
375 * Output email footer.
376 *
377 * @param string $email_notification_key Email notification key for email being sent.
378 * @param bool $sent_to_admin True if this is being sent to an administrator.
379 * @param bool $plain_text True if the email is being sent as plain text.
380 */
381 public static function output_footer( $email_notification_key, $sent_to_admin, $plain_text = false ) {
382 $template_segment = self::email_template_path_alternative( $email_notification_key, 'email-footer', $plain_text );
383 if ( false === $template_segment ) {
384 $template_segment = self::locate_template_file( 'email-footer', $plain_text );
385 }
386 if ( ! $template_segment || ! file_exists( $template_segment ) ) {
387 return;
388 }
389 include $template_segment;
390 }
391
392 /**
393 * Checks for an alternative email template segment in the template path specified by the current email.
394 * Useful to provide alternative email headers and footers for a specific WPJM extension plugin.
395 *
396 * @param string $email_notification_key Email notification key for email being sent.
397 * @param string $template_name Name of the template to check.
398 * @param bool $plain_text True if the email is being sent as plain text.
399 * @return bool|string Returns path to template path alternative or false if none exists.
400 */
401 private static function email_template_path_alternative( $email_notification_key, $template_name, $plain_text ) {
402 $email_class = self::get_email_class( $email_notification_key );
403 if ( ! $email_class || ! is_subclass_of( $email_class, 'WP_Job_Manager_Email_Template' ) ) {
404 return false;
405 }
406
407 $template_default_path = call_user_func( [ $email_class, 'get_template_default_path' ] );
408 if ( '' === $template_default_path ) {
409 return false;
410 }
411
412 $template_path = call_user_func( [ $email_class, 'get_template_path' ] );
413 $template = self::locate_template_file( $template_name, $plain_text, $template_path, $template_default_path );
414 if ( '' === $template ) {
415 return false;
416 }
417
418 return $template;
419 }
420
421 /**
422 * Locate template file.
423 *
424 * @param string $template_name
425 * @param bool $plain_text
426 * @param string $template_path
427 * @param string $default_path
428 * @return string
429 */
430 public static function locate_template_file( $template_name, $plain_text = false, $template_path = 'job_manager', $default_path = '' ) {
431 return locate_job_manager_template( WP_Job_Manager_Email_Template::generate_template_file_name( $template_name, $plain_text ), $template_path, $default_path );
432 }
433
434 /**
435 * Add email notification settings for the job manager context.
436 *
437 * @param array $settings
438 * @return array
439 */
440 public static function add_job_manager_email_settings( $settings ) {
441 return self::add_email_settings( $settings, WP_Job_Manager_Email::get_context() );
442 }
443
444 /**
445 * Add email notification settings for a context.
446 *
447 * @param array $settings
448 * @param string $context
449 * @return array
450 */
451 public static function add_email_settings( $settings, $context ) {
452 $email_notifications = self::get_email_notifications( false );
453 $email_settings = [];
454
455 foreach ( $email_notifications as $email_notification_key => $email_class ) {
456 $email_notification_context = $email_class::get_context();
457 if ( $context !== $email_notification_context ) {
458 continue;
459 }
460
461 $email_settings[] = [
462 'type' => 'multi_enable_expand',
463 'class' => 'email-setting-row no-separator',
464 'name' => self::EMAIL_SETTING_PREFIX . $email_class::get_key(),
465 'enable_field' => [
466 'name' => self::EMAIL_SETTING_ENABLED,
467 'cb_label' => $email_class::get_name(),
468 'desc' => $email_class::get_description(),
469 'force_value' => $email_class::get_enabled_force_value(),
470 ],
471 'label' => false,
472 'std' => self::get_email_setting_defaults( $email_notification_key ),
473 'settings' => self::get_email_setting_fields( $email_notification_key ),
474 'track' => 'bool',
475 ];
476 }
477
478 if ( ! empty( $email_settings ) ) {
479 $settings['email_notifications'] = [
480 __( 'Email Notifications', 'wp-job-manager' ),
481 $email_settings,
482 [
483 'before' => __( 'Select the email notifications to enable.', 'wp-job-manager' ),
484 ],
485 ];
486 }
487
488 return $settings;
489 }
490
491 /**
492 * Checks if a particular notification is enabled or not.
493 *
494 * @param string $email_notification_key
495 * @return bool
496 */
497 public static function is_email_notification_enabled( $email_notification_key ) {
498 $settings = self::get_email_settings( $email_notification_key );
499 $email_class = self::get_email_class( $email_notification_key );
500
501 $enabled_force_value = $email_class ? $email_class::get_enabled_force_value() : null;
502 $is_email_notification_enabled = ! empty( $settings[ self::EMAIL_SETTING_ENABLED ] );
503
504 if ( is_bool( $enabled_force_value ) ) {
505 $is_email_notification_enabled = $enabled_force_value;
506 }
507
508 /**
509 * Filter whether an notification email is enabled.
510 *
511 * @since 1.31.0
512 *
513 * @param bool $is_email_notification_enabled
514 * @param string $email_notification_key
515 */
516 return apply_filters( 'job_manager_email_is_email_notification_enabled', $is_email_notification_enabled, $email_notification_key );
517 }
518
519 /**
520 * Checks if we should send emails using plain text.
521 *
522 * @since 1.31.0
523 * @since 1.34.1 Added arguments to allow for per-recipient customization.
524 *
525 * @param string $email_notification_key Unique ID for this email notification.
526 * @param array $args Email arguments.
527 * @return bool
528 */
529 public static function send_as_plain_text( $email_notification_key, $args = [] ) {
530 $settings = self::get_email_settings( $email_notification_key );
531
532 $send_as_plain_text = ! empty( $settings[ self::EMAIL_SETTING_PLAIN_TEXT ] );
533
534 /**
535 * Filter whether to send emails as plain text.
536 *
537 * @since 1.31.0
538 *
539 * @param bool $send_as_plain_text Whether to send this email as plain text.
540 * @param string $email_notification_key Unique ID for this email notification.
541 * @param array $args Email arguments.
542 */
543 return apply_filters( 'job_manager_email_send_as_plain_text', $send_as_plain_text, $email_notification_key, $args );
544 }
545
546 /**
547 * Sending notices to employers for expiring job listings.
548 */
549 public static function send_employer_expiring_notice() {
550 self::maybe_init();
551
552 $email_key = WP_Job_Manager_Email_Employer_Expiring_Job::get_key();
553 if ( ! self::is_email_notification_enabled( $email_key ) ) {
554 return;
555 }
556 $settings = self::get_email_settings( $email_key );
557 $days_notice = WP_Job_Manager_Email_Employer_Expiring_Job::get_notice_period( $settings );
558 self::send_expiring_notice( $email_key, $days_notice );
559 }
560
561 /**
562 * Sending notices to the site administrator for expiring job listings.
563 */
564 public static function send_admin_expiring_notice() {
565 self::maybe_init();
566
567 $email_key = WP_Job_Manager_Email_Admin_Expiring_Job::get_key();
568 if ( ! self::is_email_notification_enabled( $email_key ) ) {
569 return;
570 }
571 $settings = self::get_email_settings( $email_key );
572 $days_notice = WP_Job_Manager_Email_Admin_Expiring_Job::get_notice_period( $settings );
573 self::send_expiring_notice( $email_key, $days_notice );
574 }
575
576 /**
577 * Fire the action to send a new job notification to the admin.
578 *
579 * @param int $job_id
580 */
581 public static function send_new_job_notification( $job_id ) {
582 do_action( 'job_manager_send_notification', 'admin_new_job', [ 'job_id' => $job_id ] );
583 }
584
585 /**
586 * Fire the action to send a updated job notification to the admin.
587 *
588 * @param int $job_id
589 */
590 public static function send_updated_job_notification( $job_id ) {
591 do_action( 'job_manager_send_notification', 'admin_updated_job', [ 'job_id' => $job_id ] );
592 }
593
594 /**
595 * Send notice based on job expiration date.
596 *
597 * @param string $email_notification_key
598 * @param int $days_notice
599 */
600 private static function send_expiring_notice( $email_notification_key, $days_notice ) {
601 $notice_before_datetime = current_datetime()->add( new DateInterval( 'P' . $days_notice . 'D' ) );
602
603 $job_ids = get_posts(
604 [
605 'post_type' => \WP_Job_Manager_Post_Types::PT_LISTING,
606 'post_status' => 'publish',
607 'fields' => 'ids',
608 'posts_per_page' => -1,
609 'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Used in production with no issues.
610 [
611 'key' => '_job_expires',
612 'value' => $notice_before_datetime->format( 'Y-m-d' ),
613 ],
614 ],
615 ]
616 );
617
618 if ( $job_ids ) {
619 foreach ( $job_ids as $job_id ) {
620 do_action( 'job_manager_send_notification', $email_notification_key, [ 'job_id' => $job_id ] );
621 }
622 }
623 }
624
625 /**
626 * Get the setting fields for an email.
627 *
628 * @param string $email_notification_key
629 * @return array
630 */
631 private static function get_email_setting_fields( $email_notification_key ) {
632 $email_class = self::get_email_class( $email_notification_key );
633 $core_settings = [
634 [
635 'name' => 'plain_text',
636 'std' => '0',
637 'label' => __( 'Format', 'wp-job-manager' ),
638 'type' => 'radio',
639 'options' => [
640 '1' => __( 'Send plain text email', 'wp-job-manager' ),
641 '0' => __( 'Send rich text email', 'wp-job-manager' ),
642 ],
643 ],
644 ];
645
646 if ( 'employer_expiring_job' !== $email_notification_key ) {
647 array_unshift(
648 $core_settings,
649 [
650 'name' => 'email_to',
651 'placeholder' => 'Defaults to Admin email',
652 'std' => '',
653 'label' => __( 'Email', 'wp-job-manager' ),
654 'type' => 'text',
655 ]
656 );
657 }
658 $email_settings = call_user_func( [ $email_class, 'get_setting_fields' ] );
659 return array_merge( $core_settings, $email_settings );
660 }
661
662 /**
663 * Get the settings for the email.
664 *
665 * @param string $email_notification_key
666 * @return array
667 */
668 private static function get_email_settings( $email_notification_key ) {
669 $option_name = self::EMAIL_SETTING_PREFIX . $email_notification_key;
670 $option_value = get_option( $option_name );
671 if ( empty( $option_value ) || ! is_array( $option_value ) ) {
672 $option_value = [];
673 }
674 $default_settings = self::get_email_setting_defaults( $email_notification_key );
675
676 return array_merge( $default_settings, $option_value );
677 }
678
679 /**
680 * Gets the default values for the email notification.
681 *
682 * @param string $email_notification_key
683 * @return array
684 */
685 private static function get_email_setting_defaults( $email_notification_key ) {
686 $settings = self::get_email_setting_fields( $email_notification_key );
687 $email_class = self::get_email_class( $email_notification_key );
688
689 $defaults = [];
690 $defaults[ self::EMAIL_SETTING_ENABLED ] = call_user_func( [ $email_class, 'is_default_enabled' ] ) ? '1' : '0';
691
692 foreach ( $settings as $setting ) {
693 $defaults[ $setting['name'] ] = null;
694 if ( isset( $setting['std'] ) ) {
695 $defaults[ $setting['name'] ] = $setting['std'];
696 }
697 }
698
699 return $defaults;
700 }
701
702 /**
703 * Get the email class from the unique key.
704 *
705 * @param string $email_notification_key
706 * @return bool|string
707 */
708 private static function get_email_class( $email_notification_key ) {
709 $email_notifications = self::get_email_notifications( false );
710
711 return isset( $email_notifications[ $email_notification_key ] ) ? $email_notifications[ $email_notification_key ] : false;
712 }
713
714 /**
715 * Returns the total number of deferred notifications to be sent.
716 *
717 * Do not use. Used just in unit tests.
718 *
719 * @access private
720 *
721 * @return int
722 */
723 public static function get_deferred_notification_count() {
724 return count( self::$deferred_notifications );
725 }
726
727 /**
728 * Returns the hash representation of the notifications to be sent.
729 *
730 * Do not use. Used just in unit tests.
731 *
732 * @access private
733 *
734 * @return string[]
735 */
736 public static function get_deferred_notification_hashes() {
737 return array_map(
738 function( $value ) {
739 return sha1( wp_json_encode( $value ) );
740 },
741 self::$deferred_notifications
742 );
743 }
744
745 /**
746 * Confirms an email notification is valid.
747 *
748 * @access private
749 *
750 * @param string $email_class
751 * @return bool
752 */
753 private static function is_email_notification_valid( $email_class ) {
754 // PHP 5.2: Using `call_user_func()` but `$email_class::get_key()` preferred.
755 return is_string( $email_class )
756 && class_exists( $email_class )
757 && is_subclass_of( $email_class, 'WP_Job_Manager_Email' )
758 && false !== call_user_func( [ $email_class, 'get_key' ] )
759 && false !== call_user_func( [ $email_class, 'get_name' ] );
760 }
761
762 /**
763 * Sends an email notification.
764 *
765 * @access private
766 *
767 * @param string $email_notification_key
768 * @param WP_Job_Manager_Email $email
769 * @return bool
770 */
771 private static function send_email( $email_notification_key, WP_Job_Manager_Email $email ) {
772
773 global $job_manager_doing_email;
774 $job_manager_doing_email = true;
775
776 if ( ! $email->is_valid() ) {
777 return false;
778 }
779
780 $fields = [ 'to', 'from', 'subject', 'rich_content', 'plain_content', 'attachments', 'cc', 'headers' ];
781 $args = [];
782 foreach ( $fields as $field ) {
783 $method = 'get_' . $field;
784
785 /**
786 * Filter email values for job manager notifications.
787 *
788 * @since 1.31.0
789 *
790 * @param mixed $email_field_value Value to be filtered.
791 * @param WP_Job_Manager_Email $email Email notification object.
792 */
793 $args[ $field ] = apply_filters( "job_manager_email_{$email_notification_key}_{$field}", $email->$method(), $email );
794 }
795
796 $args['headers'] = is_array( $args['headers'] ) ? $args['headers'] : [];
797 $send_to = $args['to'];
798 if ( ! is_array( $send_to ) ) {
799 $send_to = array_filter( array_map( 'sanitize_email', preg_split( '/[,|;]\s?/', $send_to ) ) );
800 }
801
802 $sent_count = 0;
803 foreach ( $send_to as $to_email ) {
804 $args['to'] = $to_email;
805
806 $is_plain_text_only = self::send_as_plain_text( $email_notification_key, $args );
807
808 $content_plain = self::get_email_content( $email_notification_key, $args, true );
809
810 if ( $is_plain_text_only ) {
811 $body = $content_plain;
812 } else {
813 $body = self::get_email_content( $email_notification_key, $args, false );
814 }
815
816 /**
817 * Filter all email arguments for job manager notifications.
818 *
819 * @since 1.34.1
820 *
821 * @param mixed $email_field_value Value to be filtered.
822 * @param WP_Job_Manager_Email $email Email notification object.
823 */
824 $args = apply_filters( "job_manager_email_{$email_notification_key}_args", $args, $email );
825
826 $headers = $args['headers'];
827 if ( ! empty( $args['from'] ) ) {
828 $headers[] = 'From: ' . $args['from'];
829 }
830
831 if ( ! empty( $args['cc'] ) ) {
832 $headers[] = 'CC: ' . $args['cc'];
833 }
834
835 /**
836 * Allows for short-circuiting the actual sending of email notifications.
837 *
838 * @since 1.31.0
839 *
840 * @param bool $do_send_notification True if we should send the notification.
841 * @param WP_Job_Manager_Email $email Email notification object.
842 * @param array $args Email arguments for generating email.
843 * @param string $content Email content.
844 * @param array $headers Email headers.
845 */
846 if ( ! apply_filters( 'job_manager_email_do_send_notification', true, $email, $args, $body, $headers ) ) {
847 continue;
848 }
849
850 $set_content_type = fn() => 'text/html';
851
852 if ( ! $is_plain_text_only ) {
853 add_filter( 'wp_mail_content_type', $set_content_type );
854 }
855
856 if ( wp_mail( $to_email, $args['subject'], $body, $headers, $args['attachments'] ) ) {
857 $sent_count++;
858 }
859
860 remove_filter( 'wp_mail_content_type', $set_content_type );
861 }
862
863 $job_manager_doing_email = false;
864
865 return $sent_count > 0;
866 }
867
868 /**
869 * Generates the content for an email.
870 *
871 * @access private
872 *
873 * @param string $email_notification_key Unique email notification key.
874 * @param array $args Arguments passed for generating email.
875 * @param bool $is_plain_text Whether to generate plain text or rich text content.
876 *
877 * @return string
878 */
879 private static function get_email_content( $email_notification_key, $args, $is_plain_text ) {
880
881 ob_start();
882
883 /**
884 * Output the header for all job manager emails.
885 *
886 * @since 1.31.0
887 *
888 * @param string $email_notification_key Unique email notification key.
889 * @param array $args Arguments passed for generating email.
890 * @param bool $is_plain_text True if sending plain text email.
891 */
892 do_action( 'job_manager_email_header', $email_notification_key, $args, $is_plain_text );
893
894 if ( $is_plain_text ) {
895 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Plain text e-mail.
896 echo wp_specialchars_decode( wp_strip_all_tags( $args['plain_content'] ) );
897 } else {
898 echo wp_kses_post( ( $args['rich_content'] ) );
899 }
900
901 /**
902 * Output the footer for all job manager emails.
903 *
904 * @since 1.31.0
905 *
906 * @param string $email_notification_key Unique email notification key.
907 * @param array $args Arguments passed for generating email.
908 * @param bool $is_plain_text True if sending plain text email.
909 */
910 do_action( 'job_manager_email_footer', $email_notification_key, $args, $is_plain_text );
911
912 $content = ob_get_clean();
913 if ( ! $is_plain_text ) {
914 $content = self::inject_styles( $content );
915 }
916
917 /**
918 * Filter the content of the email.
919 *
920 * @since 1.31.0
921 *
922 * @param string $content Email content.
923 * @param string $email_notification_key Unique email notification key.
924 * @param array $args Arguments passed for generating email.
925 * @param bool $is_plain_text True if sending plain text email.
926 */
927 return apply_filters( 'job_manager_email_content', $content, $email_notification_key, $args, $is_plain_text );
928 }
929
930 /**
931 * Inject inline styles into email content.
932 *
933 * @param string $content
934 * @return string
935 */
936 private static function inject_styles( $content ) {
937 if ( class_exists( CssInliner::class ) ) {
938 try {
939 $dom_document = CssInliner::fromHtml( $content )->inlineCss( self::get_styles() )->getDomDocument();
940 $content = CssToAttributeConverter::fromDomDocument( $dom_document )->convertCssToVisualAttributes()->render();
941
942 } catch ( Exception $e ) {
943 trigger_error( 'Unable to inject styles into email notification: ' . $e->getMessage() ); // @codingStandardsIgnoreLine
944 }
945 }
946 return $content;
947 }
948
949 /**
950 * Gets the CSS styles to be used in email notifications.
951 *
952 * @return string
953 */
954 private static function get_styles() {
955 $email_styles_template = self::locate_template_file( 'email-styles' );
956 if ( ! file_exists( $email_styles_template ) ) {
957 return '';
958 }
959 ob_start();
960 include $email_styles_template;
961 return ob_get_clean();
962 }
963
964 }
965