PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.3.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.3.0
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.3.0, at app/Services/Integrations/FluentCRM/Bootstrap.php

454 lines 16.6 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 $canCreateTaxonomy = \FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_contact_cats');
104
105 $fields = [
106 [
107 'key' => 'name',
108 'label' => __('Feed Name', 'fluent-booking'),
109 'required' => true,
110 'placeholder' => __('Your Feed Name', 'fluent-booking'),
111 'component' => 'text',
112 ],
113 [
114 'key' => 'CustomFields',
115 'require_list' => false,
116 'label' => __('Map Primary Fields', 'fluent-booking'),
117 'tips' => __('Associate your FluentCRM merge tags to the appropriate Fluent Form fields by selecting the appropriate form field from the list.', 'fluent-booking'),
118 'component' => 'map_fields',
119 'field_label_remote' => __('FluentCRM Field', 'fluent-booking'),
120 'field_label_local' => __('Booking Field', 'fluent-booking'),
121 'primary_fields' => [
122 [
123 'key' => 'email',
124 'label' => __('Email Address', 'fluent-booking'),
125 'required' => true,
126 'input_options' => 'emails',
127 ],
128 [
129 'key' => 'first_name',
130 'label' => __('First Name', 'fluent-booking'),
131 ],
132 [
133 'key' => 'last_name',
134 'label' => __('Last Name', 'fluent-booking'),
135 ]
136 ],
137 ],
138 [
139 'key' => 'other_fields',
140 'require_list' => false,
141 'label' => __('Other Fields', 'fluent-booking'),
142 'tips' => __('Select which Fluent Form fields pair with their<br /> respective FlunentCRM fields.', 'fluent-booking'),
143 'component' => 'dropdown_many_fields',
144 'field_label_remote' => __('FluentCRM Field', 'fluent-booking'),
145 'field_label_local' => __('Form Field', 'fluent-booking'),
146 'options' => $fieldOptions,
147 ],
148 [
149 'key' => 'list_ids',
150 'label' => __('FluentCRM Lists', 'fluent-booking'),
151 'placeholder' => __('Select FluentCRM Lists', 'fluent-booking'),
152 'tips' => __('Select the FluentCRM Lists you would like to add your contacts to.', 'fluent-booking'),
153 'component' => 'select',
154 'is_multiple' => true,
155 'required' => false,
156 'options' => $this->getLists(),
157 'creatable' => $canCreateTaxonomy,
158 'taxonomy' => 'lists',
159 'create_placeholder' => __('New list name', 'fluent-booking'),
160 ],
161 [
162 'key' => 'tag_ids',
163 'require_list' => false,
164 'label' => __('Contact Tags', 'fluent-booking'),
165 'placeholder' => __('Select Tags', 'fluent-booking'),
166 'component' => 'select',
167 'is_multiple' => true,
168 'options' => $this->getTags(),
169 'creatable' => $canCreateTaxonomy,
170 'taxonomy' => 'tags',
171 'create_placeholder' => __('New tag name', 'fluent-booking'),
172 ],
173 [
174 'key' => 'skip_if_exists',
175 'require_list' => false,
176 'checkbox_label' => __('Skip if contact already exists in FluentCRM', 'fluent-booking'),
177 'component' => 'checkbox-single',
178 ],
179 [
180 'key' => 'skip_primary_data',
181 'require_list' => false,
182 'checkbox_label' => __('Skip name update if an existing contact has old data (per primary field)', 'fluent-booking'),
183 'component' => 'checkbox-single',
184 ],
185 [
186 'key' => 'double_opt_in',
187 'require_list' => false,
188 'checkbox_label' => __('Enable Double opt-in for new contacts', 'fluent-booking'),
189 'component' => 'checkbox-single',
190 ],
191 [
192 'key' => 'force_subscribe',
193 'require_list' => false,
194 'checkbox_label' => __('Enable Force Subscribe if contact is not in subscribed status (Existing contact only)', 'fluent-booking'),
195 'component' => 'checkbox-single',
196 'inline_tip' => __('If you enable this, the contact will be forcefully subscribed regardless of its current status', 'fluent-booking'),
197 ],
198 [
199 'require_list' => false,
200 'required' => true,
201 'key' => 'event_trigger',
202 'options' => [
203 'after_booking_scheduled' => __('Booking Confirmed', 'fluent-booking'),
204 'booking_schedule_completed' => __('Booking Completed', 'fluent-booking'),
205 'booking_schedule_cancelled' => __('Booking Canceled', 'fluent-booking'),
206 'after_booking_rescheduled' => __('Booking Rescheduled', 'fluent-booking'),
207 'booking_schedule_rejected' => __('Booking Rejected', 'fluent-booking'),
208 'booking_schedule_no_show' => __('Booking No-Show', 'fluent-booking'),
209 ],
210 'tips' => __('Select in which booking stage you want to trigger this feed', 'fluent-booking'),
211 'label' => __('Event Trigger', 'fluent-booking'),
212 'component' => 'checkbox-multiple-text',
213 'checkbox_label' => __('Event Trigger For This Feed', 'fluent-booking'),
214 ]
215 ];
216
217 $fields[] = [
218 'require_list' => false,
219 'key' => 'remove_tags',
220 'label' => __('Remove Contact Tags', 'fluent-booking'),
221 'placeholder' => __('Select Tags (remove from contact)', 'fluent-booking'),
222 'tips' => __('(Optional) The selected tags will be removed from the contact (if exist)', 'fluent-booking'),
223 'component' => 'select',
224 'is_multiple' => true,
225 'required' => false,
226 'options' => $this->getTags(),
227 ];
228
229 $fields[] = [
230 'require_list' => false,
231 'key' => 'enabled',
232 'label' => __('Status', 'fluent-booking'),
233 'component' => 'checkbox-single',
234 'checkbox_label' => __('Enable This feed', 'fluent-booking'),
235 ];
236
237 return [
238 'fields' => $fields,
239 'button_require_list' => false,
240 'integration_title' => $this->title,
241 ];
242 }
243
244 public function getMergeFields($list, $listId, $slotId)
245 {
246 return [];
247 }
248
249 public function getConfigFieldOptions($settings, $slotId)
250 {
251 return [];
252 }
253
254 protected function getLists()
255 {
256 $lists = Lists::orderBy('title', 'ASC')->get();
257 $formattedLists = [];
258 foreach ($lists as $list) {
259 $formattedLists[$list->id] = $list->title;
260 }
261
262 return $formattedLists;
263 }
264
265 protected function getTags()
266 {
267 $tags = Tag::orderBy('title', 'ASC')->get();
268 $formattedTags = [];
269 foreach ($tags as $tag) {
270 $formattedTags[strval($tag->id)] = $tag->title;
271 }
272
273 return $formattedTags;
274 }
275
276 /*
277 * Submission Hooks Here
278 */
279 public function notify($feed, $booking, $calendarEvent)
280 {
281 $data = $feed['processedValues'];
282 $contact = Arr::only($data, ['first_name', 'last_name', 'email']);
283
284 if (empty($contact['email'])) {
285 return false;
286 }
287
288 foreach (Arr::get($data, 'other_fields') as $field) {
289 if ($field['item_value']) {
290 $contact[$field['label']] = $field['item_value'];
291 }
292 }
293
294 if ($booking->ip_address) {
295 $contact['ip'] = $booking->ip_address;
296 }
297
298 if (!is_email($contact['email'])) {
299 $this->addLog(
300 $feed['settings']['name'],
301 __('FluentCRM API call was skipped because no valid email is available', 'fluent-booking'),
302 $booking->id,
303 'failed'
304 );
305 return false;
306 }
307
308 $subscriber = Subscriber::where('email', $contact['email'])->first();
309 if ($subscriber && Arr::isTrue($data, 'skip_if_exists')) {
310 $this->addLog(
311 $feed['settings']['name'],
312 __('Contact creation has been skipped because the contact already exists in the database', 'fluent-booking'),
313 $booking->id,
314 'failed'
315 );
316 return false;
317 }
318
319 if (!empty($contact['avatar'])) {
320 // validate the avatar photo
321 $validUrl = '';
322 if (false !== filter_var($contact['avatar'], FILTER_VALIDATE_URL)) {
323 $url = $contact['avatar'];
324 $dots = explode('.', $url);
325 $ext = strtolower(end($dots));
326
327 if (in_array($ext, ['png', 'jpg', 'jpeg', 'webp', 'gif'])) {
328 $validUrl = $contact['avatar'];
329 }
330 }
331
332 if (!$validUrl) {
333 unset($contact['avatar']);
334 }
335 }
336
337 if ($subscriber) {
338 if ($subscriber->ip && isset($contact['ip'])) {
339 unset($contact['ip']);
340 }
341
342 if (Arr::isTrue($data, 'skip_primary_data')) {
343 if ($subscriber->first_name) {
344 unset($contact['first_name']);
345 unset($contact['last_name']);
346 }
347 }
348 } else {
349 if (empty($contact['source'])) {
350 $contact['source'] = 'FluentBooking';
351 }
352 if (Arr::isTrue($data, 'double_opt_in')) {
353 $contact['status'] = 'pending';
354 } else {
355 $contact['status'] = 'subscribed';
356 }
357 }
358
359 $user = get_user_by('email', $contact['email']);
360 if ($user) {
361 $contact['user_id'] = $user->ID;
362 }
363
364 if (!empty($data['tag_ids'])) {
365 $contact['tags'] = $data['tag_ids'];
366 }
367
368 if (!empty($data['list_ids'])) {
369 $contact['lists'] = $data['list_ids'];
370 }
371
372 if (!$subscriber) {
373 if (Arr::isTrue($data, 'double_opt_in')) {
374 $contact['status'] = 'pending';
375 } else {
376 $contact['status'] = 'subscribed';
377 }
378
379 $subscriber = FluentCrmApi('contacts')->createOrUpdate($contact, false, false);
380
381 if (!$subscriber) {
382 return false;
383 }
384
385 if ($subscriber->status == 'pending') {
386 $subscriber->sendDoubleOptinEmail();
387 }
388
389 if ($removeTags = Arr::get($feed, 'settings.remove_tags', [])) {
390 $subscriber->detachTags($removeTags);
391 }
392
393 $this->addLog(
394 $feed['settings']['name'],
395 __('Contact has been created in FluentCRM. Contact ID: ', 'fluent-booking') . $subscriber->id,
396 $booking->id,
397 'success'
398 );
399
400 do_action('fluent_crm/contact_added_by_fluent_booking', $subscriber, $booking, $calendarEvent, $feed);
401 return true;
402 }
403
404 // We have subscriber here
405
406 $hasDouBleOptIn = Arr::isTrue($data, 'double_opt_in');
407
408 $forceSubscribed = !$hasDouBleOptIn && ($subscriber->status != 'subscribed');
409
410 if (!$forceSubscribed) {
411 $forceSubscribed = Arr::isTrue($data, 'force_subscribe');
412 }
413
414 if ($forceSubscribed) {
415 $contact['status'] = 'subscribed';
416 }
417
418 $subscriber = FluentCrmApi('contacts')->createOrUpdate($contact, $forceSubscribed, false);
419
420 if (!$subscriber) {
421 return false;
422 }
423
424 if ($hasDouBleOptIn && ($subscriber->status == 'pending' || $subscriber->status == 'unsubscribed')) {
425 $subscriber->sendDoubleOptinEmail();
426 }
427
428 do_action('fluent_crm/contact_added_by_fluent_booking', $subscriber, $booking, $calendarEvent, $feed);
429
430 if ($removeTags = Arr::get($feed, 'settings.remove_tags', [])) {
431 $subscriber->detachTags($removeTags);
432 }
433
434 $this->addLog(
435 $feed['settings']['name'],
436 __('Contact has been updated in FluentCRM. Contact ID: ', 'fluent-booking') . $subscriber->id,
437 $booking->id,
438 'success'
439 );
440
441 return true;
442 }
443
444 public function isConfigured()
445 {
446 return true;
447 }
448
449 public function isEnabled()
450 {
451 return true;
452 }
453 }
454