PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.59
Timetics – Appointment Booking Calendar & Scheduling v1.0.59
1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 1.0.22 1.0.23 1.0.24 All 62 releases
timetics / core / admin / notification.php

notification.php in Timetics – Appointment Booking Calendar & Scheduling 1.0.59, at core/admin/notification.php

302 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Notification class — boots themewinter/email-notification-sdk and registers
4 * Timetics triggers so the React automation builder can wire up email flows.
5 *
6 * @package Timetics
7 */
8
9 namespace Timetics\Core\Admin;
10
11 use Ens\Core\SDK;
12 use Timetics\Core\Appointments\Appointment;
13 use Timetics\Core\Bookings\Booking;
14 use Timetics\Core\Customers\Customer;
15 use Timetics\Core\Staffs\Staff;
16 use Timetics\Utils\Singleton;
17
18 defined( 'ABSPATH' ) || exit;
19
20 require_once __DIR__ . '/notification-seeder.php';
21
22 class Notification {
23
24 use Singleton;
25
26 /**
27 * Boot SDK and register trigger definitions.
28 *
29 * @return void
30 */
31 public function init() {
32 if ( ! class_exists( SDK::class ) ) {
33 return;
34 }
35
36 SDK::get_instance()
37 ->setup(
38 array(
39 'plugin_name' => 'Timetics',
40 'plugin_slug' => 'timetics',
41 'general_prefix' => 'tt',
42 'hook_prefix' => 'timetics',
43 'text_domain' => 'timetics',
44 'admin_script_handler' => 'timetics-dashboard-scripts',
45 'sub_menu_filter_hook' => 'timetics_menu',
46 'sub_menu_details' => array(
47 'id' => 'timetics-automation',
48 'title' => __( 'Automation', 'timetics' ),
49 'link' => '/automation',
50 'capability' => apply_filters( 'timetics_menu_permission_automation', 'manage_options' ),
51 'position' => apply_filters( 'timetics_menu_position_automation', 11 ),
52 ),
53 )
54 )
55 ->init();
56
57 add_filter( 'ens_tt_available_actions', array( $this, 'register_triggers' ) );
58 add_filter( 'timetics_notification_sdk_email_body', array( $this, 'wrap_email_body' ), 10, 1 );
59 add_filter( 'timetics_notification_sdk_to_emails', array( $this, 'expand_custom_email' ), 10, 2 );
60
61 add_action( 'admin_init', array( 'Timetics\Core\Admin\Notification_Seeder', 'maybe_seed' ), 20 );
62 }
63
64 /**
65 * Register the 3 booking triggers with their tag fields and receivers.
66 *
67 * @param array $actions
68 * @return array
69 */
70 public function register_triggers( $actions ) {
71 $trigger_data = array(
72 array(
73 'label' => __( 'Meeting Title', 'timetics' ),
74 'value' => 'meeting_title',
75 'type' => 'string',
76 ),
77 array(
78 'label' => __( 'Meeting Date', 'timetics' ),
79 'value' => 'meeting_date',
80 'type' => 'date',
81 ),
82 array(
83 'label' => __( 'Meeting Time', 'timetics' ),
84 'value' => 'meeting_time',
85 'type' => 'string',
86 ),
87 array(
88 'label' => __( 'Meeting Location', 'timetics' ),
89 'value' => 'meeting_location',
90 'type' => 'string',
91 ),
92 array(
93 'label' => __( 'Google Meet Link', 'timetics' ),
94 'value' => 'meeting_meet_link',
95 'type' => 'string',
96 ),
97 array(
98 'label' => __( 'Meeting Duration', 'timetics' ),
99 'value' => 'meeting_duration',
100 'type' => 'string',
101 ),
102 array(
103 'label' => __( 'Host Name', 'timetics' ),
104 'value' => 'host_name',
105 'type' => 'string',
106 ),
107 array(
108 'label' => __( 'Host Email', 'timetics' ),
109 'value' => 'host_email',
110 'type' => 'string',
111 ),
112 array(
113 'label' => __( 'Customer Name', 'timetics' ),
114 'value' => 'customer_name',
115 'type' => 'string',
116 ),
117 array(
118 'label' => __( 'Customer Email', 'timetics' ),
119 'value' => 'customer_email',
120 'type' => 'string',
121 ),
122 array(
123 'label' => __( 'Login URL', 'timetics' ),
124 'value' => 'login_url',
125 'type' => 'string',
126 ),
127 array(
128 'label' => __( 'Login Username', 'timetics' ),
129 'value' => 'login_username',
130 'type' => 'string',
131 ),
132 array(
133 'label' => __( 'Set Password URL', 'timetics' ),
134 'value' => 'set_password_url',
135 'type' => 'string',
136 ),
137 );
138
139 $email_receivers = array(
140 array(
141 'label' => __( 'Customer Email', 'timetics' ),
142 'value' => 'customer_email',
143 ),
144 array(
145 'label' => __( 'Host Email', 'timetics' ),
146 'value' => 'host_email',
147 ),
148 array(
149 'label' => __( 'Custom Email', 'timetics' ),
150 'value' => 'custom_email',
151 ),
152 );
153
154 $actions = array(
155 array(
156 'trigger_label' => __( 'After Booking Confirmation', 'timetics' ),
157 'trigger_value' => 'booking_created',
158 'trigger_data' => $trigger_data,
159 'delay_dependencies' => array(
160 array(
161 'label' => __( 'Meeting Date', 'timetics' ),
162 'value' => 'meeting_date',
163 ),
164 ),
165 'email_receivers' => $email_receivers,
166 ),
167 array(
168 'trigger_label' => __( 'After Booking Cancellation', 'timetics' ),
169 'trigger_value' => 'booking_canceled',
170 'trigger_data' => $trigger_data,
171 'delay_dependencies' => array(),
172 'email_receivers' => $email_receivers,
173 ),
174 array(
175 'trigger_label' => __( 'After Booking Rescheduled', 'timetics' ),
176 'trigger_value' => 'booking_rescheduled',
177 'trigger_data' => $trigger_data,
178 'delay_dependencies' => array(),
179 'email_receivers' => $email_receivers,
180 ),
181 );
182
183 return $actions;
184 }
185
186 /**
187 * Expand a comma-separated custom_email setting into an array of addresses.
188 *
189 * Only expands when the email value matches hook_data['custom_email'],
190 * so customer/host emails pass through untouched.
191 *
192 * Hooked to `timetics_notification_sdk_to_emails`.
193 *
194 * @param string $email Raw email value being sent.
195 * @param array $hook_data Full hook data payload.
196 * @return string|array Single email or array of emails.
197 */
198 public function expand_custom_email( $email, $hook_data ) {
199 if ( ! isset( $hook_data['custom_email'] ) || $email !== $hook_data['custom_email'] ) {
200 return $email;
201 }
202
203 if ( strpos( $email, ',' ) === false ) {
204 return sanitize_email( $email );
205 }
206
207 return array_values(
208 array_filter(
209 array_map( 'sanitize_email', array_map( 'trim', explode( ',', $email ) ) )
210 )
211 );
212 }
213
214 /**
215 * Wrap the SDK email body in Timetics' branded HTML template.
216 *
217 * Hooked to `notification_sdk_email_body`.
218 *
219 * @param string $message Raw email body HTML from the SDK.
220 * @return string Full HTML email.
221 */
222 public function wrap_email_body( $message ) {
223 $template_path = TIMETICS_PLUGIN_DIR . '/templates/emails/email-notification-wrapper.html';
224
225 if ( ! file_exists( $template_path ) ) {
226 return $message;
227 }
228
229 $template = file_get_contents( $template_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
230 $primary_color = timetics_get_option( 'primary_color', '#3161F1' );
231 $company_name = timetics_get_option( 'business_name', get_bloginfo( 'name' ) );
232
233 $show_powered_by = apply_filters( 'timetics_show_email_powered_by', true );
234 $powered_by_html = '';
235 if ( $show_powered_by ) {
236 $powered_by_html = '<p style="margin:8px 0 0;font-size:11px;color:#b0bec9;font-style:italic;">'
237 . esc_html__( 'Powered by Timetics', 'timetics' )
238 . '</p>';
239 }
240
241 $variables = array(
242 '{{MESSAGE}}' => wp_kses_post( $message ),
243 '{{COMPANY_NAME}}' => esc_html( $company_name ),
244 '{{PRIMARY_COLOR}}' => esc_attr( $primary_color ),
245 '{%powered_by_section%}' => $powered_by_html,
246 );
247
248 return str_replace( array_keys( $variables ), array_values( $variables ), $template );
249 }
250
251 /**
252 * Build the hook data payload from a Booking object.
253 * Called from any file that needs to fire a timetics_gln_hook action.
254 *
255 * @param Booking $booking
256 * @return array
257 */
258 public static function get_hook_data( Booking $booking ) {
259 $meeting = new Appointment( $booking->get_appointment() );
260 $staff = new Staff( $booking->get_staff_id() );
261 $customer = new Customer( $booking->get_customer_id() );
262
263 $formatted = timetics_format_email_datetime( $booking->get_start_date(), $booking->get_start_time() );
264 $booking_timestamp = strtotime( $booking->get_start_date() . ' ' . $booking->get_start_time() );
265
266 /* Pull the Google Meet link from the stored calendar event so it can be inserted as the {%meeting_meet_link%} tag in automation emails.
267 */
268 $calendar_event = $booking->get_event();
269 $meet_link = ( is_array( $calendar_event ) && ! empty( $calendar_event['hangoutLink'] ) ) ? $calendar_event['hangoutLink'] : '';
270
271 $set_password_url = '';
272 $customer_user = get_user_by( 'email', $customer->get_email() );
273 if ( $customer_user ) {
274 $reset_key = get_password_reset_key( $customer_user );
275 if ( ! is_wp_error( $reset_key ) ) {
276 $set_password_url = network_site_url(
277 'wp-login.php?action=rp&key=' . $reset_key . '&login=' . rawurlencode( $customer_user->user_login ),
278 'login'
279 );
280 }
281 }
282
283 return array(
284 'customer_email' => $customer->get_email(),
285 'host_email' => $staff->get_email(),
286 'custom_email' => apply_filters( 'timetics_custom_notification_email', timetics_get_option( 'custom_notification_email', get_option( 'admin_email' ) ) ),
287 'meeting_title' => $meeting->get_name(),
288 'meeting_date' => $formatted['date'],
289 'meeting_date_timestamp' => $booking_timestamp,
290 'meeting_time' => $formatted['time'],
291 'meeting_location' => $booking->get_location(),
292 'meeting_meet_link' => $meet_link,
293 'meeting_duration' => $meeting->get_duration(),
294 'host_name' => $staff->get_display_name(),
295 'customer_name' => $customer->get_display_name(),
296 'login_url' => wp_login_url(),
297 'login_username' => $customer->get_email(),
298 'set_password_url' => $set_password_url,
299 );
300 }
301 }
302