PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.1.1
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.1.1
2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 1.7.2 All 33 releases
fluent-booking / app / Services / Integrations / FluentCRM / Bootstrap.php

Bootstrap.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 2.1.1, at app/Services/Integrations/FluentCRM/Bootstrap.php

446 lines 16.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\App\Services\Integrations\FluentCRM;
4
5 use FluentCrm\App\Models\Tag;
6 use FluentCrm\App\Models\Lists;
7 use FluentCrm\App\Models\Subscriber;
8 use FluentBooking\Framework\Support\Arr;
9 use FluentCrm\App\Models\CustomContactField;
10 use FluentBooking\App\Http\Controllers\IntegrationManagerController;
11
12 class Bootstrap extends IntegrationManagerController
13 {
14 public $hasGlobalMenu = false;
15
16 public $disableGlobalSettings = 'yes';
17
18 public function __construct()
19 {
20 parent::__construct(
21 __('FluentCRM', 'fluent-booking'),
22 'fluentcrm',
23 '_fluent_booking_fluentcrm_settings',
24 'fluentcrm_feeds',
25 10
26 );
27
28 $this->logo = FLUENTCRM_PLUGIN_URL . 'assets/images/fluentcrm-logo.svg';
29
30 $this->description = __('Connect FluentCRM with Fluent Booking and subscribe a contact when a booking is created.', 'fluent-booking');
31
32 $this->registerAdminHooks();
33
34 add_filter('fluent_booking/notifying_async_fluentcrm', '__return_false');
35 }
36
37 public function pushIntegration($integrations, $calendarEventId)
38 {
39 $integrations[$this->integrationKey] = [
40 'title' => $this->title . ' ' . __('Integration', 'fluent-booking'),
41 'logo' => $this->logo,
42 'is_active' => $this->isConfigured(),
43 'configure_title' => __('Configuration required!', 'fluent-booking'),
44 'global_configure_url' => '#',
45 'configure_message' => __('FluentCRM is not configured yet! Please configure your FluentCRM api first', 'fluent-booking'),
46 'configure_button_text' => __('Set FluentCRM', 'fluent-booking'),
47 ];
48
49 return $integrations;
50 }
51
52 public function getIntegrationDefaults($settings, $calendarEventId)
53 {
54 return [
55 'name' => '',
56 'first_name' => '{{guest.first_name}}',
57 'last_name' => '{{guest.last_name}}',
58 'email' => '{{guest.email}}',
59 'other_fields' => [
60 [
61 'item_value' => '',
62 'label' => '',
63 ],
64 ],
65 'list_ids' => '',
66 'tag_ids' => [],
67 'skip_if_exists' => false,
68 'double_opt_in' => false,
69 'force_subscribe' => false,
70 'skip_primary_data' => false,
71 'conditionals' => [
72 'conditions' => [],
73 'status' => false,
74 'type' => 'all',
75 ],
76 'run_events_only' => [],
77 'remove_tags' => [],
78 'enabled' => true,
79 ];
80 }
81
82 public function getSettingsFields($settings, $slotId)
83 {
84 $fieldOptions = [];
85
86 foreach (Subscriber::mappables() as $key => $column) {
87 $fieldOptions[$key] = $column;
88 }
89
90 foreach ((new CustomContactField)->getGlobalFields()['fields'] as $field) {
91 $fieldOptions[$field['slug']] = $field['label'];
92 }
93
94 $fieldOptions['avatar'] = __('Profile Photo', 'fluent-booking');
95
96 unset($fieldOptions['email']);
97 unset($fieldOptions['first_name']);
98 unset($fieldOptions['last_name']);
99 unset($fieldOptions['prefix']);
100 unset($fieldOptions['full_name']);
101 unset($fieldOptions['company_id']);
102
103 $fields = [
104 [
105 'key' => 'name',
106 'label' => __('Feed Name', 'fluent-booking'),
107 'required' => true,
108 'placeholder' => __('Your Feed Name', 'fluent-booking'),
109 'component' => 'text',
110 ],
111 [
112 'key' => 'CustomFields',
113 'require_list' => false,
114 'label' => __('Map Primary Fields', 'fluent-booking'),
115 'tips' => __('Associate your FluentCRM merge tags to the appropriate Fluent Form fields by selecting the appropriate form field from the list.', 'fluent-booking'),
116 'component' => 'map_fields',
117 'field_label_remote' => __('FluentCRM Field', 'fluent-booking'),
118 'field_label_local' => __('Booking Field', 'fluent-booking'),
119 'primary_fields' => [
120 [
121 'key' => 'email',
122 'label' => __('Email Address', 'fluent-booking'),
123 'required' => true,
124 'input_options' => 'emails',
125 ],
126 [
127 'key' => 'first_name',
128 'label' => __('First Name', 'fluent-booking'),
129 ],
130 [
131 'key' => 'last_name',
132 'label' => __('Last Name', 'fluent-booking'),
133 ]
134 ],
135 ],
136 [
137 'key' => 'other_fields',
138 'require_list' => false,
139 'label' => __('Other Fields', 'fluent-booking'),
140 'tips' => __('Select which Fluent Form fields pair with their<br /> respective FlunentCRM fields.', 'fluent-booking'),
141 'component' => 'dropdown_many_fields',
142 'field_label_remote' => __('FluentCRM Field', 'fluent-booking'),
143 'field_label_local' => __('Form Field', 'fluent-booking'),
144 'options' => $fieldOptions,
145 ],
146 [
147 'key' => 'list_ids',
148 'label' => __('FluentCRM Lists', 'fluent-booking'),
149 'placeholder' => __('Select FluentCRM Lists', 'fluent-booking'),
150 'tips' => __('Select the FluentCRM Lists you would like to add your contacts to.', 'fluent-booking'),
151 'component' => 'select',
152 'is_multiple' => true,
153 'required' => false,
154 'options' => $this->getLists(),
155 ],
156 [
157 'key' => 'tag_ids',
158 'require_list' => false,
159 'label' => __('Contact Tags', 'fluent-booking'),
160 'placeholder' => __('Select Tags', 'fluent-booking'),
161 'component' => 'select',
162 'is_multiple' => true,
163 'options' => $this->getTags(),
164 ],
165 [
166 'key' => 'skip_if_exists',
167 'require_list' => false,
168 'checkbox_label' => __('Skip if contact already exists in FluentCRM', 'fluent-booking'),
169 'component' => 'checkbox-single',
170 ],
171 [
172 'key' => 'skip_primary_data',
173 'require_list' => false,
174 'checkbox_label' => __('Skip name update if an existing contact has old data (per primary field)', 'fluent-booking'),
175 'component' => 'checkbox-single',
176 ],
177 [
178 'key' => 'double_opt_in',
179 'require_list' => false,
180 'checkbox_label' => __('Enable Double opt-in for new contacts', 'fluent-booking'),
181 'component' => 'checkbox-single',
182 ],
183 [
184 'key' => 'force_subscribe',
185 'require_list' => false,
186 'checkbox_label' => __('Enable Force Subscribe if contact is not in subscribed status (Existing contact only)', 'fluent-booking'),
187 'component' => 'checkbox-single',
188 'inline_tip' => __('If you enable this, the contact will be forcefully subscribed regardless of its current status', 'fluent-booking'),
189 ],
190 [
191 'require_list' => false,
192 'required' => true,
193 'key' => 'event_trigger',
194 'options' => [
195 'after_booking_scheduled' => __('Booking Confirmed', 'fluent-booking'),
196 'booking_schedule_completed' => __('Booking Completed', 'fluent-booking'),
197 'booking_schedule_cancelled' => __('Booking Canceled', 'fluent-booking'),
198 'after_booking_rescheduled' => __('Booking Rescheduled', 'fluent-booking'),
199 'booking_schedule_rejected' => __('Booking Rejected', 'fluent-booking'),
200 'booking_schedule_no_show' => __('Booking No-Show', 'fluent-booking'),
201 ],
202 'tips' => __('Select in which booking stage you want to trigger this feed', 'fluent-booking'),
203 'label' => __('Event Trigger', 'fluent-booking'),
204 'component' => 'checkbox-multiple-text',
205 'checkbox_label' => __('Event Trigger For This Feed', 'fluent-booking'),
206 ]
207 ];
208
209 $fields[] = [
210 'require_list' => false,
211 'key' => 'remove_tags',
212 'label' => __('Remove Contact Tags', 'fluent-booking'),
213 'placeholder' => __('Select Tags (remove from contact)', 'fluent-booking'),
214 'tips' => __('(Optional) The selected tags will be removed from the contact (if exist)', 'fluent-booking'),
215 'component' => 'select',
216 'is_multiple' => true,
217 'required' => false,
218 'options' => $this->getTags(),
219 ];
220
221 $fields[] = [
222 'require_list' => false,
223 'key' => 'enabled',
224 'label' => __('Status', 'fluent-booking'),
225 'component' => 'checkbox-single',
226 'checkbox_label' => __('Enable This feed', 'fluent-booking'),
227 ];
228
229 return [
230 'fields' => $fields,
231 'button_require_list' => false,
232 'integration_title' => $this->title,
233 ];
234 }
235
236 public function getMergeFields($list, $listId, $slotId)
237 {
238 return [];
239 }
240
241 public function getConfigFieldOptions($settings, $slotId)
242 {
243 return [];
244 }
245
246 protected function getLists()
247 {
248 $lists = Lists::orderBy('title', 'ASC')->get();
249 $formattedLists = [];
250 foreach ($lists as $list) {
251 $formattedLists[$list->id] = $list->title;
252 }
253
254 return $formattedLists;
255 }
256
257 protected function getTags()
258 {
259 $tags = Tag::orderBy('title', 'ASC')->get();
260 $formattedTags = [];
261 foreach ($tags as $tag) {
262 $formattedTags[strval($tag->id)] = $tag->title;
263 }
264
265 return $formattedTags;
266 }
267
268 /*
269 * Submission Hooks Here
270 */
271 public function notify($feed, $booking, $calendarEvent)
272 {
273 $data = $feed['processedValues'];
274 $contact = Arr::only($data, ['first_name', 'last_name', 'email']);
275
276 if (empty($contact['email'])) {
277 return false;
278 }
279
280 foreach (Arr::get($data, 'other_fields') as $field) {
281 if ($field['item_value']) {
282 $contact[$field['label']] = $field['item_value'];
283 }
284 }
285
286 if ($booking->ip_address) {
287 $contact['ip'] = $booking->ip_address;
288 }
289
290 if (!is_email($contact['email'])) {
291 $this->addLog(
292 $feed['settings']['name'],
293 __('FluentCRM API call was skipped because no valid email is available', 'fluent-booking'),
294 $booking->id,
295 'failed'
296 );
297 return false;
298 }
299
300 $subscriber = Subscriber::where('email', $contact['email'])->first();
301 if ($subscriber && Arr::isTrue($data, 'skip_if_exists')) {
302 $this->addLog(
303 $feed['settings']['name'],
304 __('Contact creation has been skipped because the contact already exists in the database', 'fluent-booking'),
305 $booking->id,
306 'failed'
307 );
308 return false;
309 }
310
311 if (!empty($contact['avatar'])) {
312 // validate the avatar photo
313 $validUrl = '';
314 if (false !== filter_var($contact['avatar'], FILTER_VALIDATE_URL)) {
315 $url = $contact['avatar'];
316 $dots = explode('.', $url);
317 $ext = strtolower(end($dots));
318
319 if (in_array($ext, ['png', 'jpg', 'jpeg', 'webp', 'gif'])) {
320 $validUrl = $contact['avatar'];
321 }
322 }
323
324 if (!$validUrl) {
325 unset($contact['avatar']);
326 }
327 }
328
329 if ($subscriber) {
330 if ($subscriber->ip && isset($contact['ip'])) {
331 unset($contact['ip']);
332 }
333
334 if (Arr::isTrue($data, 'skip_primary_data')) {
335 if ($subscriber->first_name) {
336 unset($contact['first_name']);
337 unset($contact['last_name']);
338 }
339 }
340 } else {
341 if (empty($contact['source'])) {
342 $contact['source'] = 'FluentBooking';
343 }
344 if (Arr::isTrue($data, 'double_opt_in')) {
345 $contact['status'] = 'pending';
346 } else {
347 $contact['status'] = 'subscribed';
348 }
349 }
350
351 $user = get_user_by('email', $contact['email']);
352 if ($user) {
353 $contact['user_id'] = $user->ID;
354 }
355
356 if (!empty($data['tag_ids'])) {
357 $contact['tags'] = $data['tag_ids'];
358 }
359
360 if (!empty($data['list_ids'])) {
361 $contact['lists'] = $data['list_ids'];
362 }
363
364 if (!$subscriber) {
365 if (Arr::isTrue($data, 'double_opt_in')) {
366 $contact['status'] = 'pending';
367 } else {
368 $contact['status'] = 'subscribed';
369 }
370
371 $subscriber = FluentCrmApi('contacts')->createOrUpdate($contact, false, false);
372
373 if (!$subscriber) {
374 return false;
375 }
376
377 if ($subscriber->status == 'pending') {
378 $subscriber->sendDoubleOptinEmail();
379 }
380
381 if ($removeTags = Arr::get($feed, 'settings.remove_tags', [])) {
382 $subscriber->detachTags($removeTags);
383 }
384
385 $this->addLog(
386 $feed['settings']['name'],
387 __('Contact has been created in FluentCRM. Contact ID: ', 'fluent-booking') . $subscriber->id,
388 $booking->id,
389 'success'
390 );
391
392 do_action('fluent_crm/contact_added_by_fluent_booking', $subscriber, $booking, $calendarEvent, $feed);
393 return true;
394 }
395
396 // We have subscriber here
397
398 $hasDouBleOptIn = Arr::isTrue($data, 'double_opt_in');
399
400 $forceSubscribed = !$hasDouBleOptIn && ($subscriber->status != 'subscribed');
401
402 if (!$forceSubscribed) {
403 $forceSubscribed = Arr::isTrue($data, 'force_subscribe');
404 }
405
406 if ($forceSubscribed) {
407 $contact['status'] = 'subscribed';
408 }
409
410 $subscriber = FluentCrmApi('contacts')->createOrUpdate($contact, $forceSubscribed, false);
411
412 if (!$subscriber) {
413 return false;
414 }
415
416 if ($hasDouBleOptIn && ($subscriber->status == 'pending' || $subscriber->status == 'unsubscribed')) {
417 $subscriber->sendDoubleOptinEmail();
418 }
419
420 do_action('fluent_crm/contact_added_by_fluent_booking', $subscriber, $booking, $calendarEvent, $feed);
421
422 if ($removeTags = Arr::get($feed, 'settings.remove_tags', [])) {
423 $subscriber->detachTags($removeTags);
424 }
425
426 $this->addLog(
427 $feed['settings']['name'],
428 __('Contact has been updated in FluentCRM. Contact ID: ', 'fluent-booking') . $subscriber->id,
429 $booking->id,
430 'success'
431 );
432
433 return true;
434 }
435
436 public function isConfigured()
437 {
438 return true;
439 }
440
441 public function isEnabled()
442 {
443 return true;
444 }
445 }
446