PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.64
Timetics – Appointment Booking Calendar & Scheduling v1.0.64
1.0.64 1.0.62 1.0.63 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 All 65 releases
← All changes | core/settings/api-settings.php +264 -24 1.0.2 → 1.0.64 View file →
@@ -5,9 +5,12 @@
5 5 * @package Timetics
6 6 */
7 7 namespace Timetics\Core\Settings;
8 8
9 +defined( 'ABSPATH' ) || exit;
10 +
9 11 use Timetics\Base\Api;
12 +use Timetics\Core\Admin\Hooks;
10 13 use Timetics\Utils\Singleton;
11 14
12 15 class Api_Settings extends Api {
13 16 use Singleton;
@@ -33,24 +36,50 @@
33 36 */
34 37 public function register_routes() {
35 38 register_rest_route(
36 39 $this->namespace, $this->rest_base, [
37 - [
38 - 'methods' => \WP_REST_Server::READABLE,
39 - 'callback' => [ $this, 'get_settings' ],
40 - 'permission_callback' => function () {
41 - return current_user_can( 'manage_timetics' );
42 - },
43 - ],
44 - [
45 - 'methods' => \WP_REST_Server::EDITABLE,
46 - 'callback' => [ $this, 'update_settings' ],
47 - 'permission_callback' => function () {
48 - return current_user_can( 'manage_timetics' );
49 - },
50 - ],
51 - ]
40 + [
41 + 'methods' => \WP_REST_Server::READABLE,
42 + 'callback' => [$this, 'get_settings'],
43 + 'permission_callback' => function () {
44 + // The public booking app needs a small, explicitly safe
45 + // settings payload. get_settings() filters it by role.
46 + return true;
47 + },
48 + ],
49 + [
50 + 'methods' => \WP_REST_Server::EDITABLE,
51 + 'callback' => [$this, 'update_settings'],
52 + 'permission_callback' => function () {
53 + return current_user_can( 'manage_options' );
54 + },
55 + ],
56 + ]
52 57 );
58 +
59 + register_rest_route(
60 + $this->namespace, $this->rest_base . '/business', [
61 + [
62 + 'methods' => \WP_REST_Server::CREATABLE,
63 + 'callback' => [$this, 'setup_business'],
64 + 'permission_callback' => function () {
65 + return current_user_can( 'manage_options' );
66 + },
67 + ],
68 + ]
69 + );
70 +
71 + register_rest_route(
72 + $this->namespace, $this->rest_base . '/business/categories', [
73 + [
74 + 'methods' => \WP_REST_Server::READABLE,
75 + 'callback' => [$this, 'get_busyness_categories'],
76 + 'permission_callback' => function () {
77 + return current_user_can( 'manage_options' );
78 + },
79 + ],
80 + ]
81 + );
53 82 }
54 83
55 84 /**
56 85 * Get settings
@@ -57,15 +86,21 @@
57 86 *
58 87 * @return JSON
59 88 */
60 89 public function get_settings() {
61 - $settings = timetics_get_settings();
90 + $settings = apply_filters( 'timetics_settings', timetics_get_settings() );
62 91
92 + // The booking and staff interfaces need non-sensitive display and scheduling
93 + // settings. Never send credentials or webhook URLs to non-administrators.
94 + if ( ! current_user_can( 'manage_options' ) ) {
95 + $settings = $this->get_staff_safe_settings( $settings );
96 + }
97 +
63 98 $data = [
64 99 'status_code' => 200,
65 - 'success' => 1,
66 - 'message' => __( 'Get all settings', 'timetics' ),
67 - 'data' => $settings,
100 + 'success' => 1,
101 + 'message' => esc_html__( 'Get all settings', 'timetics' ),
102 + 'data' => $settings,
68 103 ];
69 104
70 105 return rest_ensure_response( $settings );
71 106 }
@@ -70,8 +105,47 @@
70 105 return rest_ensure_response( $settings );
71 106 }
72 107
73 108 /**
109 + * Return only settings that staff need to use the admin interface.
110 + *
111 + * This is deliberately an allow-list. New settings remain private until they
112 + * have been reviewed and explicitly added here.
113 + *
114 + * @param array $settings Plugin settings.
115 + *
116 + * @return array
117 + */
118 + private function get_staff_safe_settings( $settings ) {
119 + $safe_keys = [
120 + 'availability',
121 + 'apple_calendar',
122 + 'blocked_days',
123 + 'busyness_category',
124 + 'calendar_locale',
125 + 'currency',
126 + 'custom_fields',
127 + 'default_booking_status',
128 + 'guest_enabled',
129 + 'guest_limit',
130 + 'google_calendar',
131 + 'locale_timezone',
132 + 'paypal_status',
133 + 'primary_color',
134 + 'remainder_time',
135 + 'secondary_color',
136 + 'slot_interval',
137 + 'stripe_status',
138 + 'outlook_calendar',
139 + 'wc_integration',
140 + 'wc_checkout_url',
141 + 'zoom_connection_type',
142 + ];
143 +
144 + return array_intersect_key( (array) $settings, array_flip( $safe_keys ) );
145 + }
146 +
147 + /**
74 148 * Update settings
75 149 *
76 150 * @param WP_Rest_Request $request
77 151 *
@@ -79,20 +153,186 @@
79 153 */
80 154 public function update_settings( $request ) {
81 155 $options = json_decode( $request->get_body(), true );
82 156
157 + if ( ! is_array( $options ) ) {
158 + return new \WP_Error(
159 + 'timetics_invalid_settings',
160 + __( 'Settings must be sent as a JSON object.', 'timetics' ),
161 + [ 'status' => 400 ]
162 + );
163 + }
164 +
165 + /**
166 + * Filter the settings payload before any of it is checked or saved.
167 + *
168 + * Runs before the checks below, so a listener's result passes through
169 + * them like the raw request does. Add-ons use it to clean their own
170 + * keys. It is an extension point, not the sanitization for core keys.
171 + *
172 + * @since 1.0.63
173 + *
174 + * @param array $options Settings payload from the request body.
175 + */
176 + $filtered = apply_filters( 'timetics_settings_update_params', $options );
177 +
178 + // A listener returning a non-array must not make the save below write nothing.
179 + $options = is_array( $filtered ) ? $filtered : $options;
180 +
181 + /**
182 + * Added temporary for leagacy sass. It will remove in future.
183 + */
184 + $data = [
185 + 'status_code' => 200,
186 + 'success' => 1,
187 + 'message' => esc_html__( 'Settings successfully updated', 'timetics' ),
188 + 'data' => timetics_get_settings(),
189 + ];
190 +
191 + // custom domain
192 + if (!empty($options['custom_domain_url']) && isset($options['custom_domain_url'])) {
193 + $custom_domain_data = [
194 + 'custom_domain_url' => $options['custom_domain_url'],
195 + 'network_slug' => $options['network_slug'],
196 + ];
197 + do_action('timetics_custom_domain_data', $custom_domain_data);
198 + }
199 +
200 +
201 + if ( !empty($options['schedule']) && $options['schedule'] && apply_filters('timetics/staff/member/availability', false)) {
202 + return rest_ensure_response( apply_filters( 'timetics/admin/staff/error_data', $data, 'availability_update' ) );
203 + }
204 +
205 + if ( !empty($options['availability']) && $options['availability'] && apply_filters('timetics/staff/meeting/availability', false)) {
206 + return rest_ensure_response( apply_filters( 'timetics/admin/appointment/error_data', $data, 'availability_update' ) );
207 + }
208 +
209 + if ( ! empty( $options['custom_fields'] ) && apply_filters( 'timetics/admin/settings/custom_fields', false, $options['custom_fields'] ) ) {
210 + return rest_ensure_response( apply_filters( 'timetics/admin/settings/error_data', $data, 'custom_fields', timetics_get_settings() ) );
211 + }
212 +
213 + if ( ! empty( $options['paypal_status'] ) && $options['paypal_status'] && apply_filters( 'timetics/admin/settings/paypal', false ) ) {
214 + return rest_ensure_response( apply_filters( 'timetics/admin/settings/error_data', $data, 'paypal', timetics_get_settings() ) );
215 + }
216 +
217 + if ( ! empty( $options['zoom_client_secret'] ) && $options['zoom_client_secret'] && apply_filters( 'timetics/admin/settings/zoom', false ) ) {
218 + return rest_ensure_response( apply_filters( 'timetics/admin/settings/error_data', $data, 'zoom', timetics_get_settings() ) );
219 + }
220 +
221 + if ( ! empty( $options['fluentcrm_webhook'] ) && $options['fluentcrm_webhook'] && apply_filters( 'timetics/admin/settings/fluentcrm_webhook', false ) ) {
222 + return rest_ensure_response( apply_filters( 'timetics/admin/settings/error_data', $data, 'fluent_crm', timetics_get_settings() ) );
223 + }
224 +
225 + if ( ! empty( $options['pabbly_webhook'] ) && $options['pabbly_webhook'] && apply_filters( 'timetics/admin/settings/pabbly_webhook', false ) ) {
226 + return rest_ensure_response( apply_filters( 'timetics/admin/settings/error_data', $data, 'pablly', timetics_get_settings() ) );
227 + }
228 +
229 + if ( ! empty( $options['zapier_webhook'] ) && $options['zapier_webhook'] && apply_filters( 'timetics/admin/settings/zapier_webhook', false ) ) {
230 + return rest_ensure_response( apply_filters( 'timetics/admin/settings/error_data', $data, 'zapier', timetics_get_settings() ) );
231 + }
232 +
233 + if ( ! empty( $options['flowmattic_webhook'] ) && $options['flowmattic_webhook'] && apply_filters( 'timetics/admin/settings/flowmattic_webhook', false ) ) {
234 + return rest_ensure_response( apply_filters( 'timetics/admin/settings/error_data', $data, 'flowmattic', timetics_get_settings() ) );
235 + }
236 +
237 + if (!empty($options['google_app_client_id']) && $options['google_app_client_id'] && apply_filters('timetics/admin/settings/google_calendar', false)) {
238 + return rest_ensure_response(apply_filters('timetics/admin/settings/error_data', $data, 'google-calendar', timetics_get_settings()));
239 + }
240 +
241 + if ( ! empty( $options['google_app_client_secret'] ) && $options['google_app_client_secret'] && apply_filters( 'timetics/admin/settings/google-calendar', false ) ) {
242 + return rest_ensure_response( apply_filters( 'timetics/admin/settings/error_data', $data, 'google-calendar', timetics_get_settings() ) );
243 + }
244 +
245 + if ( ! empty( $options['outlook_calendar'] ) && $options['outlook_calendar'] && apply_filters( 'timetics/admin/settings/outlook_calendar', false ) ) {
246 + return rest_ensure_response( apply_filters( 'timetics/admin/settings/error_data', $data, 'outlook', timetics_get_settings() ) );
247 + }
248 +
249 + if ( ! empty( $options['apple_calendar'] ) && $options['apple_calendar'] && apply_filters( 'timetics/admin/settings/apple_calendar', false ) ) {
250 + return rest_ensure_response( apply_filters( 'timetics/admin/settings/error_data', $data, 'paypal', timetics_get_settings() ) );
251 + }
252 +
253 + if ( ! empty( $options['uncanny_automator'] ) && $options['uncanny_automator'] && apply_filters( 'timetics/admin/settings/uncanny_automator', false ) ) {
254 + return rest_ensure_response( apply_filters( 'timetics/admin/settings/error_data', $data, 'uncanny_automator', timetics_get_settings() ) );
255 + }
256 +
257 + if (!empty($options['twillo_message']) && $options['twillo_message'] && apply_filters('timetics/admin/settings/twillo_messaging', false)) {
258 + return rest_ensure_response(apply_filters('timetics/admin/settings/error_data', $data, 'twillo_messaging', timetics_get_settings()));
259 + }
260 +
83 261 if ( $options ) {
84 262 foreach ( $options as $key => $value ) {
263 + // Webhook URLs are pasted from FlowMattic, so keep them a URL.
264 + if ( 'flowmattic_webhook' === $key ) {
265 + $value = esc_url_raw( $value );
266 + }
267 +
268 + // Stored as the strings `yes`/`no`: this
269 + // option defaults to on, and timetics_get_option() treats an
270 + // empty value as "unset" and hands back the default, so a
271 + // boolean false could never switch it off.
272 + if ( 'uncanny_automator' === $key ) {
273 + $value = $value && 'no' !== $value ? 'yes' : 'no';
274 + }
275 +
276 + // Clamp: the cleanup cron only runs every 5 minutes, so a
277 + // lower value would silently do nothing and 0/negative would
278 + // expire bookings instantly.
279 + if ( 'unpaid_booking_expiry_minutes' === $key ) {
280 + $value = max( 5, absint( $value ) );
281 + }
282 +
85 283 timetics_update_option( $key, $value );
86 284 }
87 285 }
88 286
89 - $data = [
90 - 'status_code' => 200,
287 + $data['data'] = timetics_get_settings();
288 +
289 + return rest_ensure_response( $data );
290 + }
291 +
292 + /**
293 + * Business setup
294 + *
295 + * @param WP_Rest_Request $request
296 + *
297 + * @return void
298 + */
299 + public function setup_business( $request ) {
300 + $data = json_decode( $request->get_body(), true );
301 +
302 + $email = ! empty( $data['email'] ) ? $data['email'] : '';
303 +
304 + $body = array(
305 + 'email' => $email,
306 + );
307 +
308 + $response_user = wp_remote_post( 'https://arraytics.com/?fluentcrm=1&route=contact&hash=0d9cd3d1-514d-4e1a-9147-09dfd5f9e997', ['body' => $body] );
309 +
310 + $response = [
311 + 'status' => 200,
91 312 'success' => 1,
92 - 'message' => __( 'Settings successfully updated', 'timetics' ),
93 - 'data' => timetics_get_settings(),
313 + 'message' => __( 'Successfully updated business', 'timetics' ),
94 314 ];
95 315
96 - return rest_ensure_response( $data );
316 + return rest_ensure_response( $response );
97 317 }
318 +
319 + /**
320 + * Get busyness categories
321 + *
322 + * @param WP_Rest_Request $request
323 + *
324 + * @return JSON
325 + */
326 + public function get_busyness_categories( $request ) {
327 + $busyness_categories = timetics_get_busyness_categories();
328 +
329 + $response = [
330 + 'success' => 1,
331 + 'status_code' => 200,
332 + 'data' => $busyness_categories,
333 + ];
334 +
335 + return rest_ensure_response( $response );
336 + }
337 +
98 338 }