PluginProbe
Property Hive / 2.3.0
Property Hive v2.3.0
2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 1.4.61 1.4.62 All 260 releases
propertyhive / includes / admin / settings / class-ph-settings-emails.php

class-ph-settings-emails.php in Property Hive 2.3.0, at includes/admin/settings/class-ph-settings-emails.php

772 lines 34.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // phpcs:set WordPress.Security.ValidatedSanitizedInput customSanitizingFunctions[] ph_clean
3 // ph_clean() recursively sanitizes text; presence, shape and unslashing checks remain separate.
4
5 /**
6 * PropertyHive Email Settings
7 *
8 * @author PropertyHive
9 * @category Admin
10 * @package PropertyHive/Admin
11 * @version 1.0.0
12 */
13
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit; // Exit if accessed directly
16 }
17
18 if ( ! class_exists( 'PH_Settings_Emails' ) ) :
19
20 /**
21 * PH_Settings_Emails.
22 */
23 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy public global class PH_Settings_Emails; preserving the existing PH_* class name is required for plugin and extension compatibility.
24 class PH_Settings_Emails extends PH_Settings_Page {
25
26 /**
27 * Constructor.
28 */
29 public function __construct() {
30 $this->id = 'email';
31 $this->label = __( 'Emails', 'propertyhive' );
32
33 add_filter( 'propertyhive_settings_tabs_array', array( $this, 'add_settings_page' ), 20 );
34 add_action( 'propertyhive_sections_' . $this->id, array( $this, 'output_sections' ) );
35 add_action( 'propertyhive_settings_' . $this->id, array( $this, 'output' ) );
36 add_action( 'propertyhive_settings_save_' . $this->id, array( $this, 'save' ) );
37
38 add_action( 'propertyhive_admin_field_email_queue', array( $this, 'email_queue_setting' ) );
39 }
40
41 /**
42 * Get sections.
43 *
44 * @return array
45 */
46 public function get_sections() {
47 $sections = array(
48 '' => __( 'Email Options', 'propertyhive' ),
49 'enquiry-auto-responder' => __( 'Enquiry Auto-Responder', 'propertyhive' ),
50 );
51
52 if ( get_option('propertyhive_module_disabled_contacts', '') != 'yes' )
53 {
54 $sections['match'] = __( 'Property Match', 'propertyhive' );
55 }
56
57 if (
58 get_option('propertyhive_module_disabled_viewings', '') != 'yes' ||
59 get_option('propertyhive_module_disabled_appraisals', '') != 'yes'
60 )
61 {
62 $sections['booking-confirmation'] = __( 'Booking Confirmations & Cancellations', 'propertyhive' );
63 }
64
65 if ( get_option('propertyhive_module_disabled_contacts', '') != 'yes' )
66 {
67 $sections['log'] = __( 'Email Queue', 'propertyhive' );
68 }
69
70 return apply_filters( 'propertyhive_get_sections_' . $this->id, $sections );
71 }
72
73 /**
74 * Get settings array.
75 *
76 * @return array
77 */
78 public function get_settings() {
79 $settings = array(
80
81 array( 'title' => __( 'Email Sender Settings', 'propertyhive' ), 'type' => 'title', 'id' => 'email_sender_options' ),
82
83 array(
84 'title' => __( '"From" Email Address', 'propertyhive' ),
85 'id' => 'propertyhive_email_from_address',
86 'type' => 'text',
87 'css' => 'min-width:300px;',
88 'default' => get_option('admin_email'),
89 'autoload' => false,
90 'desc_tip' => false,
91 ),
92
93 array( 'type' => 'sectionend', 'id' => 'email_sender_options' ),
94
95 array( 'title' => __( 'Email Template Settings', 'propertyhive' ), 'type' => 'title', 'id' => 'email_template_options' ),
96
97 array(
98 'title' => __( 'Header Image URL', 'propertyhive' ),
99 'desc' => __( 'URL to an image you want to show in the email header. Upload images using the media uploader (Admin > Media).', 'propertyhive' ),
100 'id' => 'propertyhive_email_header_image',
101 'type' => 'text',
102 'css' => 'min-width:300px;',
103 'placeholder' => 'http://',
104 'default' => '',
105 'autoload' => false,
106 'desc_tip' => true,
107 ),
108
109 array(
110 'title' => __( 'Email Background Colour', 'propertyhive' ),
111 'desc' => __( 'The background colour for Property Hive email templates.', 'propertyhive' ),
112 'id' => 'propertyhive_email_background_color',
113 'type' => 'color',
114 'css' => 'width:6em;',
115 'default' => '#f7f7f7',
116 'autoload' => false,
117 'desc_tip' => true,
118 ),
119
120 array(
121 'title' => __( 'Body Background Colour', 'propertyhive' ),
122 'desc' => __( 'The main body background colour.', 'propertyhive' ),
123 'id' => 'propertyhive_email_body_background_color',
124 'type' => 'color',
125 'css' => 'width:6em;',
126 'default' => '#ffffff',
127 'autoload' => false,
128 'desc_tip' => true,
129 ),
130
131 array(
132 'title' => __( 'Body Text Colour', 'propertyhive' ),
133 'desc' => __( 'The main body text colour.', 'propertyhive' ),
134 'id' => 'propertyhive_email_text_color',
135 'type' => 'color',
136 'css' => 'width:6em;',
137 'default' => '#333333',
138 'autoload' => false,
139 'desc_tip' => true,
140 ),
141
142 array( 'type' => 'sectionend', 'id' => 'email_template_options' ),
143
144 );
145
146 $settings = apply_filters( 'propertyhive_email_settings', $settings );
147
148 return apply_filters( 'propertyhive_get_settings_' . $this->id, $settings );
149 }
150
151 /**
152 * Get enquiry auto-responder settings array
153 *
154 * @return array
155 */
156 public function get_enquiry_autoresponder_settings() {
157
158 return apply_filters( 'propertyhive_enquiry_autoresponder_settings', array(
159
160 array( 'title' => __( 'Enquiry Auto-Responder Settings', 'propertyhive' ), 'type' => 'title', 'id' => 'enquiry_auto_responder_email_options' ),
161
162 array(
163 'title' => __( 'Auto-Responder Enabled', 'propertyhive' ),
164 'id' => 'propertyhive_enquiry_auto_responder',
165 'type' => 'checkbox',
166 'default' => '',
167 ),
168
169 array(
170 'title' => __( 'Auto-Responder Email Subject', 'propertyhive' ),
171 'id' => 'propertyhive_enquiry_auto_responder_email_subject',
172 'type' => 'text',
173 'css' => 'min-width:300px;',
174 ),
175
176 array(
177 'title' => __( 'Auto-Responder Email Body', 'propertyhive' ),
178 'id' => 'propertyhive_enquiry_auto_responder_email_body',
179 'type' => 'textarea',
180 'css' => 'min-width:300px; height:110px;',
181 ),
182
183 array( 'type' => 'sectionend', 'id' => 'enquiry_auto_responder_email_options' )
184
185 ) ); // End settings
186 }
187
188 /**
189 * Get property match settings settings array
190 *
191 * @return array
192 */
193 public function get_property_match_settings() {
194
195 $settings = array();
196
197 $settings[] = array( 'title' => __( 'Property Match Email Settings', 'propertyhive' ), 'type' => 'title', 'id' => 'applicant_match_email_options' );
198
199 $settings[] = array(
200 'title' => __( 'Default Email Subject', 'propertyhive' ),
201 'id' => 'propertyhive_property_match_default_email_subject',
202 'type' => 'text',
203 'css' => 'min-width:300px;',
204 );
205
206 $settings[] = array(
207 'title' => __( 'Default Email Body', 'propertyhive' ),
208 'id' => 'propertyhive_property_match_default_email_body',
209 'type' => 'textarea',
210 'css' => 'min-width:300px; height:110px;',
211 );
212
213 $settings[] = array(
214 'title' => __( 'Default From Email Address', 'propertyhive' ),
215 'id' => 'propertyhive_property_match_default_from',
216 'type' => 'select',
217 'default' => '',
218 'css' => 'min-width:300px;',
219 'options' => array(
220 '' => __( 'User Email Address', 'propertyhive' ),
221 'default_from_email' => __( 'Default "From" Email Address', 'propertyhive' ),
222 ),
223 'desc' => '<p>' . __( 'This sets the email address that manual matches will be sent from by default. This can still be edited when you go to send the match.<br>Automatic matches, if enabled, will still be sent from the email address of the office that most of the properties in the match belong to.', 'propertyhive' ) . '</p>',
224 );
225
226 $options = array();
227 $args = array(
228 'hide_empty' => false,
229 'parent' => 0
230 );
231 $terms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'availability' ) ) );
232
233 if ( !empty( $terms ) && !is_wp_error( $terms ) )
234 {
235 foreach ($terms as $term)
236 {
237 $options[$term->term_id] = $term->name;
238 }
239 }
240 $settings[] = array(
241 'title' => __( 'Only Include Properties With Statuses', 'propertyhive' ),
242 'id' => 'propertyhive_property_match_statuses',
243 'type' => 'multiselect',
244 'css' => 'min-width:300px; height:110px;',
245 'options' => $options,
246 'desc' => '<p>' . __( 'By default, all on market properties will come back in matches when sending properties to applicants. If you wish to only send properties with a certain status you can choose this here. For example, maybe you don\'t want Sold STC properties to be sent. Hold ctrl/cmd whilst clicking to select multiple.<br>This will also affect the Similar Properties included in the Auto Responder above, if applicable.', 'propertyhive' ) . '</p>',
247 );
248
249 $time_offset = (int) get_option('gmt_offset') * 60 * 60;
250
251 $settings[] = array(
252 'title' => __( 'Automatically Send Matching Properties To Applicants', 'propertyhive' ),
253 'desc' => __( 'Enabling this setting will mean applicants will automatically get sent properties.<br><br>
254 - This will only apply to properties added from the moment this option is activated.<br>
255 - When enabled, this can be disabled on a per-applicant basis by going into their record.<br>
256 - When sending out lots of emails we recommend using <a href="https://en-gb.wordpress.org/plugins/tags/smtp" target="_blank">a plugin</a> to send them out using SMTP. Your web developer or hosting company should be able to advise on this.', 'propertyhive' ) . ( ( get_option( 'propertyhive_auto_property_match', '' ) == 'yes' && get_option( 'propertyhive_auto_property_match_enabled_date', '' ) != '' ) ? '<br><br>Enabled on ' . gmdate("jS F Y H:i", strtotime(get_option( 'propertyhive_auto_property_match_enabled_date', '' )) + $time_offset) : '' ),
257 'id' => 'propertyhive_auto_property_match',
258 'type' => 'checkbox',
259 'default' => '',
260 );
261
262 $settings[] = array( 'type' => 'sectionend', 'id' => 'applicant_match_email_options' );
263
264 return apply_filters( 'propertyhive_property_match_settings', $settings ); // End settings
265 }
266
267 /**
268 * Get booking confirmation email settings array
269 *
270 * @return array
271 */
272 public function get_booking_confirmation_settings() {
273
274 $settings = array();
275
276 if ( get_option('propertyhive_module_disabled_viewings', '') != 'yes' )
277 {
278 //Applicant
279 $settings[] = array( 'title' => __( 'Applicant Viewing Booking Confirmations', 'propertyhive' ), 'type' => 'title', 'id' => 'applicant_viewing_booking_confirmation_email_options' );
280
281 $settings[] = array(
282 'title' => __( 'Default Email Subject', 'propertyhive' ),
283 'id' => 'propertyhive_viewing_applicant_booking_confirmation_email_subject',
284 'type' => 'text',
285 'css' => 'min-width:300px;',
286 );
287
288 $settings[] = array(
289 'title' => __( 'Default Email Body', 'propertyhive' ),
290 'id' => 'propertyhive_viewing_applicant_booking_confirmation_email_body',
291 'type' => 'textarea',
292 'css' => 'min-width:300px; height:110px;',
293 );
294
295 $settings[] = array( 'type' => 'sectionend', 'id' => 'applicant_viewing_booking_confirmation_email_options' );
296
297 $settings[] = array( 'title' => __( 'Applicant Viewing Cancellation Notifications', 'propertyhive' ), 'type' => 'title', 'id' => 'applicant_viewing_cancellation_notification_email_options' );
298
299 $settings[] = array(
300 'title' => __( 'Default Email Subject', 'propertyhive' ),
301 'id' => 'propertyhive_viewing_applicant_cancellation_notification_email_subject',
302 'type' => 'text',
303 'css' => 'min-width:300px;',
304 );
305
306 $settings[] = array(
307 'title' => __( 'Default Email Body', 'propertyhive' ),
308 'id' => 'propertyhive_viewing_applicant_cancellation_notification_email_body',
309 'type' => 'textarea',
310 'css' => 'min-width:300px; height:110px;',
311 );
312
313 $settings[] = array( 'type' => 'sectionend', 'id' => 'applicant_viewing_cancellation_notification_email_options' );
314
315 // Owner
316 $settings[] = array( 'title' => __( 'Owner/Landlord Viewing Booking Confirmations', 'propertyhive' ), 'type' => 'title', 'id' => 'owner_viewing_booking_confirmation_email_options' );
317
318 $settings[] = array(
319 'title' => __( 'Default Email Subject', 'propertyhive' ),
320 'id' => 'propertyhive_viewing_owner_booking_confirmation_email_subject',
321 'type' => 'text',
322 'css' => 'min-width:300px;',
323 );
324
325 $settings[] = array(
326 'title' => __( 'Default Email Body', 'propertyhive' ),
327 'id' => 'propertyhive_viewing_owner_booking_confirmation_email_body',
328 'type' => 'textarea',
329 'css' => 'min-width:300px; height:110px;',
330 );
331
332 $settings[] = array( 'type' => 'sectionend', 'id' => 'owner_viewing_booking_confirmation_email_options' );
333
334 $settings[] = array( 'title' => __( 'Owner/Landlord Viewing Cancellation Notifications', 'propertyhive' ), 'type' => 'title', 'id' => 'owner_viewing_cancellation_notification_email_options' );
335
336 $settings[] = array(
337 'title' => __( 'Default Email Subject', 'propertyhive' ),
338 'id' => 'propertyhive_viewing_owner_cancellation_notification_email_subject',
339 'type' => 'text',
340 'css' => 'min-width:300px;',
341 );
342
343 $settings[] = array(
344 'title' => __( 'Default Email Body', 'propertyhive' ),
345 'id' => 'propertyhive_viewing_owner_cancellation_notification_email_body',
346 'type' => 'textarea',
347 'css' => 'min-width:300px; height:110px;',
348 );
349
350 $settings[] = array( 'type' => 'sectionend', 'id' => 'owner_viewing_cancellation_notification_email_options' );
351
352 // Attending Negotiator
353 $settings[] = array( 'title' => __( 'Attending Negotiator Viewing Booking Confirmations', 'propertyhive' ), 'type' => 'title', 'id' => 'attending_negotiator_viewing_booking_confirmation_email_options' );
354
355 $settings[] = array(
356 'title' => __( 'Default Email Subject', 'propertyhive' ),
357 'id' => 'propertyhive_viewing_attending_negotiator_booking_confirmation_email_subject',
358 'type' => 'text',
359 'css' => 'min-width:300px;',
360 );
361
362 $settings[] = array(
363 'title' => __( 'Default Email Body', 'propertyhive' ),
364 'id' => 'propertyhive_viewing_attending_negotiator_booking_confirmation_email_body',
365 'type' => 'textarea',
366 'css' => 'min-width:300px; height:110px;',
367 );
368
369 $settings[] = array( 'type' => 'sectionend', 'id' => 'attending_negotiator_viewing_booking_confirmation_email_options' );
370
371 $settings[] = array( 'title' => __( 'Attending Negotiator Viewing Cancellation Notifications', 'propertyhive' ), 'type' => 'title', 'id' => 'attending_negotiator_viewing_cancellation_notifications_email_options' );
372
373 $settings[] = array(
374 'title' => __( 'Default Email Subject', 'propertyhive' ),
375 'id' => 'propertyhive_viewing_attending_negotiator_cancellation_notification_email_subject',
376 'type' => 'text',
377 'css' => 'min-width:300px;',
378 );
379
380 $settings[] = array(
381 'title' => __( 'Default Email Body', 'propertyhive' ),
382 'id' => 'propertyhive_viewing_attending_negotiator_cancellation_notification_email_body',
383 'type' => 'textarea',
384 'css' => 'min-width:300px; height:110px;',
385 );
386
387 $settings[] = array( 'type' => 'sectionend', 'id' => 'attending_negotiator_viewing_cancellation_notifications_email_options' );
388 }
389
390 if ( get_option('propertyhive_module_disabled_appraisals', '') != 'yes' )
391 {
392 // Owner
393 $settings[] = array( 'title' => __( 'Owner/Landlord Appraisal Booking Confirmations', 'propertyhive' ), 'type' => 'title', 'id' => 'owner_appraisal_booking_confirmation_email_options' );
394
395 $settings[] = array(
396 'title' => __( 'Default Email Subject', 'propertyhive' ),
397 'id' => 'propertyhive_appraisal_owner_booking_confirmation_email_subject',
398 'type' => 'text',
399 'css' => 'min-width:300px;',
400 );
401
402 $settings[] = array(
403 'title' => __( 'Default Email Body', 'propertyhive' ),
404 'id' => 'propertyhive_appraisal_owner_booking_confirmation_email_body',
405 'type' => 'textarea',
406 'css' => 'min-width:300px; height:110px;',
407 );
408
409 $settings[] = array( 'type' => 'sectionend', 'id' => 'owner_appraisal_booking_confirmation_email_options' );
410 }
411
412 if ( get_option('propertyhive_module_disabled_appraisals', '') != 'yes' || get_option('propertyhive_module_disabled_viewings', '') != 'yes' )
413 {
414 // Owner
415 $settings[] = array( 'title' => __( 'Booking Confirmations & Cancellations', 'propertyhive' ), 'type' => 'title', 'id' => 'booking_confirmation_email_options' );
416
417 $settings[] = array(
418 'title' => __( 'Sent From Email Address', 'propertyhive' ),
419 'id' => 'propertyhive_confirmations_default_from',
420 'type' => 'select',
421 'default' => '',
422 'css' => 'min-width:300px;',
423 'options' => array(
424 '' => __( 'Default "From" Email Address', 'propertyhive' ),
425 'office' => __( 'Office Email Address', 'propertyhive' ),
426 'user' => __( 'User Email Address', 'propertyhive' ),
427 ),
428 'desc' => '<p>' . __( 'This sets the email address that booking confirmations and cancellation notifications come from and that will receive the response should someone reply.', 'propertyhive' ) . '</p>',
429 );
430
431 $settings[] = array(
432 'title' => __( 'Customise Emails Before Sending', 'propertyhive' ),
433 'id' => 'propertyhive_customise_confirmation_emails',
434 'type' => 'checkbox',
435 'desc' => 'With this ticked you\'ll be able to customise the subject and body of any emails before they get sent. This can be useful for adding any appraisal/viewing-specific details to the email. If left unticked, the default subject and body set above will be used.'
436 );
437
438 $settings[] = array( 'type' => 'sectionend', 'id' => 'booking_confirmation_email_options' );
439 }
440
441 return apply_filters( 'propertyhive_booking_confirmation_settings', $settings ); // End general map settings
442 }
443
444 /**
445 * Get email queue settings array
446 *
447 * @return array
448 */
449 public function get_email_queue_settings() {
450
451 return apply_filters( 'propertyhive_email_queue_settings', array(
452
453 array( 'title' => __( 'Email Queue', 'propertyhive' ), 'type' => 'title', 'desc' => '', 'id' => 'email_queue_options' ),
454
455 array(
456 'type' => 'email_queue'
457 ),
458
459 array( 'type' => 'sectionend', 'id' => 'email_queue_options'),
460
461 ) ); // End general map settings
462 }
463
464 /**
465 * Output email queue
466 *
467 * @access public
468 * @return void
469 */
470 public function email_queue_setting()
471 {
472 global $wpdb, $post;
473
474 $date_from = '';
475 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The email queue date filter is a read-only GET parameter; the separate Run Now action has its own capability and nonce gate.
476 if ( isset( $_GET['date_from'] ) && is_string( $_GET['date_from'] ) ) {
477 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The email queue date filter is a read-only GET parameter; the separate Run Now action has its own capability and nonce gate.
478 $date_from = sanitize_text_field( wp_unslash( $_GET['date_from'] ) );
479 }
480 $date = DateTime::createFromFormat( 'Y-m-d', $date_from );
481 if ( 'all' !== $date_from && ( ! $date || $date->format( 'Y-m-d' ) !== $date_from ) ) {
482 $date_from = gmdate( 'Y-m-d', strtotime( '-30 days' ) );
483 }
484 $status = '';
485 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The email queue status filter is a read-only GET parameter; the separate Run Now action has its own capability and nonce gate.
486 if ( isset( $_GET['status'] ) && is_string( $_GET['status'] ) ) {
487 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The email queue status filter is a read-only GET parameter; the separate Run Now action has its own capability and nonce gate.
488 $status = sanitize_key( wp_unslash( $_GET['status'] ) );
489 }
490 $additional_query_string = '&date_from=' . rawurlencode( $date_from );
491 ?>
492 <tr valign="top">
493 <td style="padding:0">
494
495 <p>Some processes, such as property matching, can result in a lot of emails being sent at once. To cater for this we queue emails and send them out in batches at regular intervals.</p>
496
497 <p>Using this screen you can see which emails have been sent, which have failed, and how many are queued to be sent.</p>
498
499 <br>
500
501 <p><strong><?php
502 $next_due = wp_next_scheduled( 'propertyhive_process_email_log' );
503
504 if ( $next_due == FALSE )
505 {
506 echo 'Whoops. WordPress doesn\'t have the emails automated task scheduled. A quick fix for this is to deactivate, then re-activate the plugin.';
507 }
508 else
509 {
510 echo esc_html(__( 'Next scheduled to run at', 'propertyhive' ) . ' ' . wp_date("H:i jS F Y", $next_due));
511 }
512 ?></strong> <a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=ph-settings&tab=email&section=log&custom_email_log_cron=propertyhive_process_email_log' ), 'propertyhive-run-email-job' ) ); ?>" class="button">Run Now</a></p>
513
514 <br>
515
516 <ul class="subsubsub">
517 <?php
518 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The email queue is stored in Property Hive's custom table, which has no WP_Query API; this live admin count must reflect asynchronous queue changes immediately.
519 $emails = $wpdb->get_var("
520 SELECT COUNT(*)
521 FROM " . $wpdb->prefix . "ph_email_log
522 ");
523 ?>
524 <li class="all"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log' . $additional_query_string)); ?>"<?php if ( '' === $status ) { echo ' class="current"'; } ?>>All <span class="count">(<?php echo number_format($emails); ?>)</span></a> |</li>
525 <?php
526 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The email queue is stored in Property Hive's custom table, which has no WP_Query API; this live admin count must reflect asynchronous queue changes immediately.
527 $emails = $wpdb->get_var("
528 SELECT COUNT(*)
529 FROM " . $wpdb->prefix . "ph_email_log
530 WHERE
531 status = ''
532 ");
533 ?>
534 <li class="queued"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log&status=queued' . $additional_query_string)); ?>"<?php if ( 'queued' === $status ) { echo ' class="current"'; } ?>>Queued <span class="count">(<?php echo number_format($emails); ?>)</span></a> |</li>
535 <?php
536 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The email queue is stored in Property Hive's custom table, which has no WP_Query API; this live admin count must reflect asynchronous queue changes immediately.
537 $emails = $wpdb->get_var("
538 SELECT COUNT(*)
539 FROM " . $wpdb->prefix . "ph_email_log
540 WHERE
541 status IN ('fail1', 'fail2')
542 ");
543 ?>
544 <li class="failed"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log&status=failed' . $additional_query_string)); ?>"<?php if ( 'failed' === $status ) { echo ' class="current"'; } ?>>Failed <span class="count">(<?php echo number_format($emails); ?>)</span></a> |</li>
545 <?php
546 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The email queue is stored in Property Hive's custom table, which has no WP_Query API; this live admin count must reflect asynchronous queue changes immediately.
547 $emails = $wpdb->get_var("
548 SELECT COUNT(*)
549 FROM " . $wpdb->prefix . "ph_email_log
550 WHERE
551 status = 'sent'
552 ");
553 ?>
554 <li class="sent"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log&status=sent' . $additional_query_string)); ?>"<?php if ( 'sent' === $status ) { echo ' class="current"'; } ?>>Sent <span class="count">(<?php echo number_format($emails); ?>)</span></a></li>
555 </ul>
556
557 <div class="tablenav top">
558 <div class="alignleft actions bulkactions">
559 <div class="alignleft actions">
560 <input type="hidden" name="page" value="ph-settings">
561 <input type="hidden" name="tab" value="email">
562 <input type="hidden" name="section" value="log">
563 <input type="hidden" name="status" value="<?php echo esc_attr( $status ); ?>">
564 <select name="date_from" id="dropdown_date_from">
565 <option value="<?php echo esc_attr(gmdate("Y-m-d", strtotime("-7 days"))); ?>"<?php if ( $date_from === gmdate("Y-m-d", strtotime("-7 days")) ) { echo ' selected'; } ?>>Last 7 Days</option>
566 <option value="<?php echo esc_attr(gmdate("Y-m-d", strtotime("-14 days"))); ?>"<?php if ( $date_from === gmdate("Y-m-d", strtotime("-14 days")) ) { echo ' selected'; } ?>>Last 14 Days</option>
567 <option value="<?php echo esc_attr(gmdate("Y-m-d", strtotime("-30 days"))); ?>"<?php if ( $date_from === gmdate("Y-m-d", strtotime("-30 days")) ) { echo ' selected'; } ?>>Last 30 Days</option>
568 <option value="all"<?php if ( 'all' === $date_from ) { echo ' selected'; } ?>>All Time</option>
569 </select>
570 <input type="submit" name="filter_action" id="post-query-submit" class="button" value="Filter">
571
572 <script>
573 jQuery(document).ready(function()
574 {
575 jQuery('#_wpnonce').remove();
576 jQuery('input[name=\'_wp_http_referer\']').remove();
577 jQuery('#mainform').attr('method', 'get');
578 jQuery('#mainform').attr('action', '<?php echo esc_url(admin_url('admin.php')); ?>');
579 });
580 </script>
581 </div>
582 </div>
583 </div>
584
585 <table class="ph_email_queue widefat" cellspacing="0">
586 <thead>
587 <tr>
588 <th class="date-time"><?php echo esc_html(__( 'Date/Time', 'propertyhive' )); ?></th>
589 <th class="recipient"><?php echo esc_html(__( 'Recipient', 'propertyhive' )); ?></th>
590 <th class="subject"><?php echo esc_html(__( 'Subject', 'propertyhive' )); ?></th>
591 <th class="status"><?php echo esc_html(__( 'Status', 'propertyhive' )); ?></th>
592 <th class="actions">&nbsp;</th>
593 </tr>
594 </thead>
595 <tbody>
596 <?php
597 $query = "SELECT email_id, contact_id, to_email_address, subject, status, send_at
598 FROM {$wpdb->prefix}ph_email_log
599 WHERE %s = %s";
600 $query_args = array( '1', '1' );
601 if ( 'all' !== $date_from ) {
602 $query .= ' AND send_at >= %s';
603 $query_args[] = $date_from . ' 00:00:00';
604 }
605 switch ( $status ) {
606 case 'queued':
607 $query .= ' AND status = %s';
608 $query_args[] = '';
609 break;
610 case 'failed':
611 $query .= " AND status IN ('fail1', 'fail2')";
612 break;
613 case 'sent':
614 $query .= ' AND status = %s';
615 $query_args[] = 'sent';
616 break;
617 }
618 $query .= ' ORDER BY send_at DESC LIMIT 250';
619 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- The email queue is stored in Property Hive's custom table, which has no WP_Query API; this live filtered log must reflect asynchronous queue changes immediately. The query contains only fixed SQL branches and passes all date/status values through $wpdb->prepare().
620 $emails = $wpdb->get_results( $wpdb->prepare( $query, $query_args ) );
621
622 if ( is_array($emails) && !empty($emails) )
623 {
624 foreach ( $emails as $email )
625 {
626 ?>
627 <tr>
628 <td class="date-time"><?php echo esc_html(gmdate("jS M Y H:i", strtotime($email->send_at))); ?></td>
629 <td class="recipient"><?php echo '<a href="' . esc_url(get_edit_post_link($email->contact_id)) . '">' . esc_html(get_the_title($email->contact_id)) . '</a><br>' . esc_html($email->to_email_address); ?></td>
630 <td class="subject"><?php echo esc_html($email->subject); ?></td>
631 <td class="status"><?php
632 switch ($email->status)
633 {
634 case "fail1": { echo 'First attempt failed. Will retry'; break; }
635 case "fail2": { echo 'Failed after 2 attempts'; break; }
636 case "sent": { echo 'Sent'; break; }
637 default: { echo 'Queued'; }
638 }
639 ?></td>
640 <td class="actions">
641 <a href="<?php echo esc_url(wp_nonce_url( admin_url('?view_propertyhive_email=' . $email->email_id . '&email_id=' . $email->email_id ), 'view-email' )); ?>" target="_blank" class="button">View Email</a>
642 </td>
643 </tr>
644 <?php
645 }
646 }
647 else
648 {
649 ?>
650 <tr>
651 <td colspan="5">No emails found</td>
652 </tr>
653 <?php
654 }
655 ?>
656 </tbody>
657 </table>
658 </td>
659 </tr>
660 <?php
661 }
662
663 /**
664 * Output the settings.
665 */
666 public function output() {
667 global $current_section, $hide_save_button;
668
669 if ( $current_section )
670 {
671 switch ($current_section)
672 {
673 case "enquiry-auto-responder": { $settings = $this->get_enquiry_autoresponder_settings(); break; }
674 case "match": { $settings = $this->get_property_match_settings(); break; }
675 case "booking-confirmation": { $settings = $this->get_booking_confirmation_settings(); break; }
676 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Shared admin settings-view state; this global is intentionally used to control the common settings template and is not an arbitrary application global.
677 case "log": { $settings = $this->get_email_queue_settings(); $hide_save_button = true; break; }
678 default: { die("Unknown setting section"); }
679 }
680 }
681 else
682 {
683 $settings = $this->get_settings();
684 }
685
686 PH_Admin_Settings::output_fields( $settings );
687 }
688
689 /**
690 * Save settings.
691 */
692 public function save()
693 {
694 if ( ! current_user_can( 'manage_options' ) || ! isset( $_REQUEST['_wpnonce'] ) || ! is_string( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'propertyhive-settings' ) ) {
695 return;
696 }
697
698 global $current_section;
699
700 if ( $current_section != '' )
701 {
702 switch ($current_section)
703 {
704 case 'enquiry-auto-responder':
705 {
706 $settings = $this->get_enquiry_autoresponder_settings();
707
708 PH_Admin_Settings::save_fields( $settings );
709 break;
710 }
711 case 'match':
712 {
713 $settings = $this->get_property_match_settings();
714
715 $previous_auto_property_match = get_option( 'propertyhive_auto_property_match', '' );
716
717 PH_Admin_Settings::save_fields( $settings );
718
719 if ( isset($_POST['propertyhive_auto_property_match']) && $_POST['propertyhive_auto_property_match'] == '1' )
720 {
721 if ( $previous_auto_property_match != 'yes' )
722 {
723 // it's been activated. Stored in UTC
724 update_option( 'propertyhive_auto_property_match_enabled_date', gmdate("Y-m-d H:i:s"), FALSE);
725 }
726
727 $timestamp = wp_next_scheduled( 'propertyhive_auto_email_match' );
728 wp_unschedule_event($timestamp, 'propertyhive_auto_email_match' );
729 wp_clear_scheduled_hook('propertyhive_auto_email_match');
730
731 $recurrence = apply_filters( 'propertyhive_auto_email_match_cron_recurrence', 'daily' );
732 if ( $recurrence != 'hourly' )
733 {
734 $timestamp = strtotime( 'tomorrow +2hours' ) - ( (int)get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
735 }
736 else
737 {
738 $timestamp = strtotime( '+1 hours' );
739 }
740 wp_schedule_event(
741 apply_filters( 'propertyhive_auto_email_match_cron_timestamp', $timestamp ),
742 $recurrence,
743 'propertyhive_auto_email_match'
744 );
745 }
746 else
747 {
748 wp_clear_scheduled_hook( 'propertyhive_auto_email_match' );
749 }
750 break;
751 }
752 case 'booking-confirmation':
753 {
754 $settings = $this->get_booking_confirmation_settings();
755
756 PH_Admin_Settings::save_fields( $settings );
757 break;
758 }
759 default: { die("Unknown setting section"); }
760 }
761 }
762 else
763 {
764 PH_Admin_Settings::save_fields( $this->get_settings() );
765 }
766 }
767 }
768
769 endif;
770
771 return new PH_Settings_Emails();
772