WordPress Online Booking and Scheduling Plugin – Bookly - Version 16.8

Version Description

Download this release

Release Info

Developer Ladela
Plugin Icon 128x128 WordPress Online Booking and Scheduling Plugin – Bookly
Version 16.8
Comparing to
See all releases

Code changes from version 16.7 to 16.8

Files changed (226) hide show
  1. backend/Backend.php +21 -9
  2. backend/components/controls/Container.php +37 -0
  3. backend/components/controls/templates/container.php +4 -0
  4. backend/components/dialogs/appointment/customer_details/templates/customer_details.php +3 -10
  5. backend/components/dialogs/appointment/delete/Ajax.php +12 -6
  6. backend/components/dialogs/appointment/delete/templates/delete.php +1 -0
  7. backend/components/dialogs/appointment/edit/Ajax.php +31 -19
  8. backend/components/dialogs/appointment/edit/resources/js/ng-appointment.js +2 -1
  9. backend/components/dialogs/appointment/edit/templates/edit.php +14 -7
  10. backend/components/dialogs/customer/delete/Ajax.php +9 -3
  11. backend/components/dialogs/customer/edit/Ajax.php +1 -1
  12. backend/components/dialogs/customer/proxy/CustomerGroups.php +1 -1
  13. backend/components/dialogs/customer/proxy/CustomerInformation.php +1 -1
  14. backend/components/dialogs/customer/proxy/Pro.php +1 -1
  15. backend/components/dialogs/notifications/Dialog.php +48 -0
  16. backend/components/dialogs/notifications/templates/_attach.php +13 -0
  17. backend/components/dialogs/notifications/templates/_codes.php +19 -0
  18. backend/components/dialogs/notifications/templates/_editor.php +19 -0
  19. backend/components/dialogs/notifications/templates/_subject.php +9 -0
  20. backend/components/dialogs/notifications/templates/_types.php +100 -0
  21. backend/components/dialogs/sms/Ajax.php +48 -0
  22. backend/components/dialogs/sms/Dialog.php +80 -0
  23. backend/components/dialogs/sms/resources/js/notification-dialog.js +267 -0
  24. backend/components/dialogs/sms/templates/_codes.php +17 -0
  25. backend/components/dialogs/sms/templates/_editor.php +8 -0
  26. backend/components/dialogs/sms/templates/_modal_body.php +52 -0
  27. backend/components/dialogs/sms/templates/_settings.php +135 -0
  28. backend/components/dialogs/sms/templates/_subject.php +2 -0
  29. backend/components/dialogs/sms/templates/_types.php +100 -0
  30. backend/components/dialogs/sms/templates/dialog.php +22 -0
  31. backend/components/gutenberg/bookly_form/Block.php +1 -1
  32. backend/components/gutenberg/bookly_form/resources/js/booking-form-block.js +14 -14
  33. backend/components/notices/resources/js/collect-stats.js +2 -2
  34. backend/components/notices/resources/js/lite-rebranding.js +1 -1
  35. backend/components/notices/resources/js/nps.js +3 -3
  36. backend/components/notices/resources/js/subscribe.js +2 -2
  37. backend/components/settings/Selects.php +43 -0
  38. backend/components/settings/templates/price_correction.php +9 -3
  39. backend/components/sms/custom/Notification.php +0 -46
  40. backend/components/sms/custom/templates/after_event.php +0 -93
  41. backend/components/sms/custom/templates/existing_event_with_date.php +0 -31
  42. backend/components/sms/custom/templates/existing_event_with_date_and_time.php +0 -93
  43. backend/components/sms/custom/templates/existing_event_with_date_before.php +0 -28
  44. backend/components/sms/custom/templates/layout.php +0 -125
  45. backend/components/support/Buttons.php +19 -10
  46. backend/components/support/ButtonsAjax.php +20 -4
  47. backend/components/support/resources/js/support.js +89 -61
  48. backend/components/support/templates/buttons.php +164 -96
  49. backend/modules/appearance/proxy/Cart.php +1 -0
  50. backend/modules/appearance/proxy/ChainAppointments.php +2 -1
  51. backend/modules/appearance/resources/js/appearance.js +49 -9
  52. backend/modules/appearance/templates/_1_service.php +7 -4
  53. backend/modules/appearance/templates/_3_time.php +2 -2
  54. backend/modules/appearance/templates/_6_details.php +4 -5
  55. backend/modules/appearance/templates/index.php +12 -0
  56. backend/modules/appointments/Ajax.php +15 -3
  57. backend/modules/appointments/templates/index.php +3 -10
  58. backend/modules/calendar/Ajax.php +2 -2
  59. backend/modules/calendar/Page.php +3 -2
  60. backend/modules/calendar/templates/calendar.php +3 -3
  61. backend/modules/customers/Ajax.php +1 -1
  62. backend/modules/notifications/Ajax.php +14 -73
  63. backend/modules/notifications/Page.php +14 -41
  64. backend/modules/notifications/forms/Notifications.php +0 -315
  65. backend/modules/notifications/lib/Codes.php +32 -72
  66. backend/modules/notifications/proxy/Invoices.php +1 -1
  67. backend/modules/notifications/proxy/Pro.php +2 -5
  68. backend/modules/notifications/proxy/Shared.php +0 -3
  69. backend/modules/notifications/resources/js/ng-app.js +0 -121
  70. backend/modules/notifications/resources/js/notification.js +0 -232
  71. backend/modules/notifications/templates/_common_settings.php +36 -0
  72. backend/modules/notifications/templates/_general_settings_modal.php +33 -0
  73. backend/modules/notifications/templates/_test_email_modal.php +56 -0
  74. backend/modules/notifications/templates/_test_email_notifications_modal.php +0 -114
  75. backend/modules/notifications/templates/index.php +42 -150
  76. backend/modules/payments/Ajax.php +7 -0
  77. backend/modules/payments/resources/js/payments.js +9 -9
  78. backend/modules/services/Ajax.php +14 -4
  79. backend/modules/services/forms/Service.php +4 -0
  80. backend/modules/services/resources/js/service.js +4 -0
  81. backend/modules/services/templates/_list.php +1 -1
  82. backend/modules/settings/Page.php +3 -2
  83. backend/modules/settings/proxy/Pro.php +0 -1
  84. backend/modules/settings/proxy/Shared.php +1 -0
  85. backend/modules/settings/templates/_payment_local.php +1 -1
  86. backend/modules/shop/Ajax.php +21 -19
  87. backend/modules/shop/resources/css/shop.css +8 -0
  88. backend/modules/shop/templates/index.php +3 -0
  89. backend/modules/sms/Ajax.php +65 -24
  90. backend/modules/sms/Page.php +17 -24
  91. backend/modules/sms/proxy/Shared.php +2 -5
  92. backend/modules/sms/resources/js/notifications-list.js +235 -0
  93. backend/modules/sms/resources/js/sms.js +56 -176
  94. backend/modules/sms/templates/_invoice.php +1 -1
  95. backend/modules/sms/templates/_notifications.php +47 -110
  96. backend/modules/sms/templates/_purchases.php +1 -0
  97. backend/modules/staff/Ajax.php +6 -3
  98. backend/modules/staff/forms/StaffServices.php +1 -1
  99. backend/modules/staff/proxy/Pro.php +1 -0
  100. backend/modules/staff/resources/js/staff.js +9 -6
  101. backend/modules/staff/templates/index.php +2 -1
  102. backend/resources/bootstrap/css/bootstrap-theme.min.css +1 -1
  103. backend/resources/css/fontawesome-all.min.css +1 -5
  104. backend/resources/images/sprite.png +0 -0
  105. backend/resources/webfonts/fa-brands-400.eot +0 -0
  106. backend/resources/webfonts/fa-brands-400.svg +169 -13
  107. backend/resources/webfonts/fa-brands-400.ttf +0 -0
  108. backend/resources/webfonts/fa-brands-400.woff +0 -0
  109. backend/resources/webfonts/fa-brands-400.woff2 +0 -0
  110. backend/resources/webfonts/fa-regular-400.eot +0 -0
  111. backend/resources/webfonts/fa-regular-400.svg +113 -14
  112. backend/resources/webfonts/fa-regular-400.ttf +0 -0
  113. backend/resources/webfonts/fa-regular-400.woff +0 -0
  114. backend/resources/webfonts/fa-regular-400.woff2 +0 -0
  115. backend/resources/webfonts/fa-solid-900.eot +0 -0
  116. backend/resources/webfonts/fa-solid-900.svg +927 -60
  117. backend/resources/webfonts/fa-solid-900.ttf +0 -0
  118. backend/resources/webfonts/fa-solid-900.woff +0 -0
  119. backend/resources/webfonts/fa-solid-900.woff2 +0 -0
  120. frontend/modules/booking/Ajax.php +55 -80
  121. frontend/modules/booking/proxy/Cart.php +1 -0
  122. frontend/modules/booking/proxy/ChainAppointments.php +15 -0
  123. frontend/modules/booking/templates/1_service.php +3 -2
  124. frontend/modules/booking/templates/3_time.php +1 -3
  125. frontend/resources/css/bookly-main.css +18 -1
  126. frontend/resources/images/cart.png +0 -0
  127. frontend/resources/js/bookly.js +17 -19
  128. frontend/resources/js/bookly.min.js +1 -1
  129. frontend/resources/js/bookly.min.js.map +1 -1
  130. frontend/resources/js/src/payment_step.js +2 -0
  131. frontend/resources/js/src/service_step.js +15 -19
  132. languages/bookly-cs_CZ.mo +0 -0
  133. languages/bookly-cs_CZ.po +705 -332
  134. languages/bookly-de_DE.mo +0 -0
  135. languages/bookly-de_DE.po +1118 -540
  136. languages/bookly-es_ES.mo +0 -0
  137. languages/bookly-es_ES.po +2199 -1771
  138. languages/bookly-fr_FR.mo +0 -0
  139. languages/bookly-fr_FR.po +795 -363
  140. languages/bookly-it_IT.mo +0 -0
  141. languages/bookly-it_IT.po +825 -459
  142. languages/bookly-ja.mo +0 -0
  143. languages/bookly-ja.po +819 -392
  144. languages/bookly-nl_NL.mo +0 -0
  145. languages/bookly-nl_NL.po +778 -353
  146. languages/bookly-pt_BR.mo +0 -0
  147. languages/bookly-pt_BR.po +776 -351
  148. languages/bookly-pt_PT.mo +0 -0
  149. languages/bookly-pt_PT.po +776 -351
  150. languages/bookly-ro_RO.mo +0 -0
  151. languages/bookly-ro_RO.po +704 -329
  152. languages/bookly-ru_RU.mo +0 -0
  153. languages/bookly-ru_RU.po +2433 -2004
  154. languages/bookly-sv_SE.mo +0 -0
  155. languages/bookly-sv_SE.po +713 -338
  156. languages/bookly-tr_TR.mo +0 -0
  157. languages/bookly-tr_TR.po +720 -272
  158. languages/bookly-zh_TW.mo +0 -0
  159. languages/bookly-zh_TW.po +705 -331
  160. languages/bookly.pot +565 -326
  161. lib/Cart.php +4 -1
  162. lib/Config.php +0 -10
  163. lib/Installer.php +189 -192
  164. lib/Plugin.php +2 -1
  165. lib/Routines.php +4 -1
  166. lib/SMS.php +13 -0
  167. lib/Scheduler.php +6 -4
  168. lib/Updater.php +268 -2
  169. lib/base/Ajax.php +4 -3
  170. lib/base/Block.php +6 -12
  171. lib/base/Cache.php +1 -1
  172. lib/base/Component.php +1 -1
  173. lib/base/Schema.php +19 -1
  174. lib/base/Updater.php +20 -0
  175. lib/data_holders/booking/Collaborative.php +37 -0
  176. lib/data_holders/booking/Compound.php +40 -3
  177. lib/data_holders/booking/Item.php +60 -1
  178. lib/data_holders/booking/Series.php +10 -0
  179. lib/data_holders/booking/Simple.php +19 -0
  180. lib/data_holders/notification/Settings.php +122 -107
  181. lib/entities/Appointment.php +12 -6
  182. lib/entities/Customer.php +1 -0
  183. lib/entities/CustomerAppointment.php +27 -24
  184. lib/entities/Notification.php +181 -150
  185. lib/entities/Shop.php +79 -2
  186. lib/entities/Staff.php +11 -5
  187. lib/notifications/Codes.php +0 -530
  188. lib/notifications/Routine.php +222 -523
  189. lib/notifications/Sender.php +0 -894
  190. lib/notifications/WPML.php +82 -0
  191. lib/notifications/assets/base/Attachments.php +34 -0
  192. lib/notifications/assets/base/Codes.php +85 -0
  193. lib/notifications/assets/client_birthday/Codes.php +55 -0
  194. lib/notifications/assets/item/Attachments.php +41 -0
  195. lib/notifications/assets/item/Codes.php +225 -0
  196. lib/notifications/{ICS.php → assets/item/ICS.php} +3 -3
  197. lib/notifications/assets/item/proxy/RecurringAppointments.php +17 -0
  198. lib/notifications/assets/item/proxy/Shared.php +17 -0
  199. lib/notifications/assets/order/Attachments.php +46 -0
  200. lib/notifications/assets/order/Codes.php +129 -0
  201. lib/notifications/assets/order/proxy/Shared.php +17 -0
  202. lib/notifications/assets/staff_agenda/Codes.php +59 -0
  203. lib/notifications/assets/test/Codes.php +131 -0
  204. lib/notifications/assets/test/proxy/Shared.php +17 -0
  205. lib/notifications/base/Reminder.php +227 -0
  206. lib/notifications/base/Sender.php +49 -0
  207. lib/notifications/booking/BaseSender.php +94 -0
  208. lib/notifications/booking/Reminder.php +73 -0
  209. lib/notifications/booking/Sender.php +89 -0
  210. lib/notifications/cart/Sender.php +48 -0
  211. lib/notifications/cart/proxy/Pro.php +16 -0
  212. lib/notifications/test/Sender.php +130 -0
  213. lib/notifications/test/proxy/Shared.php +15 -0
  214. lib/proxy/CollaborativeServices.php +0 -1
  215. lib/proxy/CompoundServices.php +0 -1
  216. lib/proxy/CustomStatuses.php +20 -0
  217. lib/proxy/Pro.php +0 -1
  218. lib/proxy/RecurringAppointments.php +1 -2
  219. lib/proxy/ServiceExtras.php +1 -0
  220. lib/proxy/Shared.php +2 -5
  221. lib/slots/Finder.php +2 -2
  222. lib/utils/Common.php +10 -0
  223. lib/utils/DateTime.php +24 -0
  224. lib/utils/Price.php +2 -0
  225. main.php +1 -1
  226. readme.txt +2 -2
backend/Backend.php CHANGED
@@ -41,7 +41,8 @@ abstract class Backend
41
/** @var \WP_User $current_user */
42
global $current_user, $submenu;
43
44
- if ( $current_user->has_cap( 'administrator' ) || Lib\Entities\Staff::query()->where( 'wp_user_id', $current_user->ID )->count() ) {
45
$dynamic_position = '80.0000001' . mt_rand( 1, 1000 ); // position always is under `Settings`
46
$badge_number = Modules\Messages\Page::getMessagesCount() +
47
Modules\Shop\Page::getNotSeenCount() +
@@ -71,14 +72,18 @@ abstract class Backend
71
72
add_submenu_page( 'bookly-menu', $calendar, $calendar, 'read',
73
Modules\Calendar\Page::pageSlug(), function () { Modules\Calendar\Page::render(); } );
74
- add_submenu_page( 'bookly-menu', $appointments, $appointments, 'manage_options',
75
- Modules\Appointments\Page::pageSlug(), function () { Modules\Appointments\Page::render(); } );
76
Lib\Proxy\Locations::addBooklyMenuItem();
77
- Lib\Proxy\Packages::addBooklyMenuItem();
78
if ( $current_user->has_cap( 'administrator' ) ) {
79
add_submenu_page( 'bookly-menu', $staff_members, $staff_members, 'manage_options',
80
Modules\Staff\Page::pageSlug(), function () { Modules\Staff\Page::render(); } );
81
- } else {
82
if ( get_option( 'bookly_gen_allow_staff_edit_profile' ) == 1 ) {
83
add_submenu_page( 'bookly-menu', __( 'Profile', 'bookly' ), __( 'Profile', 'bookly' ), 'read',
84
Modules\Staff\Page::pageSlug(), function () { Modules\Staff\Page::render(); } );
@@ -87,15 +92,19 @@ abstract class Backend
87
add_submenu_page( 'bookly-menu', $services, $services, 'manage_options',
88
Modules\Services\Page::pageSlug(), function () { Modules\Services\Page::render(); } );
89
Lib\Proxy\Taxes::addBooklyMenuItem();
90
- add_submenu_page( 'bookly-menu', $customers, $customers, 'manage_options',
91
- Modules\Customers\Page::pageSlug(), function () { Modules\Customers\Page::render(); } );
92
Lib\Proxy\CustomerInformation::addBooklyMenuItem();
93
Lib\Proxy\CustomerGroups::addBooklyMenuItem();
94
add_submenu_page( 'bookly-menu', $notifications, $notifications, 'manage_options',
95
Modules\Notifications\Page::pageSlug(), function () { Modules\Notifications\Page::render(); } );
96
Modules\Sms\Page::addBooklyMenuItem();
97
- add_submenu_page( 'bookly-menu', $payments, $payments, 'manage_options',
98
- Modules\Payments\Page::pageSlug(), function () { Modules\Payments\Page::render(); } );
99
add_submenu_page( 'bookly-menu', $appearance, $appearance, 'manage_options',
100
Modules\Appearance\Page::pageSlug(), function () { Modules\Appearance\Page::render(); } );
101
Lib\Proxy\Coupons::addBooklyMenuItem();
@@ -110,6 +119,9 @@ abstract class Backend
110
add_submenu_page( 'bookly-menu', 'Debug', 'Debug', 'manage_options',
111
Modules\Debug\Page::pageSlug(), function () { Modules\Debug\Page::render(); } );
112
}
113
}
114
115
unset ( $submenu['bookly-menu'][0] );
41
/** @var \WP_User $current_user */
42
global $current_user, $submenu;
43
44
+ $is_staff = Lib\Entities\Staff::query()->where( 'wp_user_id', $current_user->ID )->count() > 0;
45
+ if ( $current_user->has_cap( 'administrator' ) || $current_user->has_cap( 'manage_bookly_appointments' ) || $is_staff ) {
46
$dynamic_position = '80.0000001' . mt_rand( 1, 1000 ); // position always is under `Settings`
47
$badge_number = Modules\Messages\Page::getMessagesCount() +
48
Modules\Shop\Page::getNotSeenCount() +
72
73
add_submenu_page( 'bookly-menu', $calendar, $calendar, 'read',
74
Modules\Calendar\Page::pageSlug(), function () { Modules\Calendar\Page::render(); } );
75
+ if ( $current_user->has_cap( 'manage_options' ) || $current_user->has_cap( 'manage_bookly_appointments' ) ) {
76
+ add_submenu_page( 'bookly-menu', $appointments, $appointments, 'read',
77
+ Modules\Appointments\Page::pageSlug(), function () { Modules\Appointments\Page::render(); } );
78
+ }
79
Lib\Proxy\Locations::addBooklyMenuItem();
80
+ if ( $current_user->has_cap( 'manage_options' ) || $current_user->has_cap( 'manage_bookly_appointments' ) ) {
81
+ Lib\Proxy\Packages::addBooklyMenuItem();
82
+ }
83
if ( $current_user->has_cap( 'administrator' ) ) {
84
add_submenu_page( 'bookly-menu', $staff_members, $staff_members, 'manage_options',
85
Modules\Staff\Page::pageSlug(), function () { Modules\Staff\Page::render(); } );
86
+ } elseif ( $is_staff ) {
87
if ( get_option( 'bookly_gen_allow_staff_edit_profile' ) == 1 ) {
88
add_submenu_page( 'bookly-menu', __( 'Profile', 'bookly' ), __( 'Profile', 'bookly' ), 'read',
89
Modules\Staff\Page::pageSlug(), function () { Modules\Staff\Page::render(); } );
92
add_submenu_page( 'bookly-menu', $services, $services, 'manage_options',
93
Modules\Services\Page::pageSlug(), function () { Modules\Services\Page::render(); } );
94
Lib\Proxy\Taxes::addBooklyMenuItem();
95
+ if ( $current_user->has_cap( 'manage_options' ) || $current_user->has_cap( 'manage_bookly_appointments' ) ) {
96
+ add_submenu_page( 'bookly-menu', $customers, $customers, 'read',
97
+ Modules\Customers\Page::pageSlug(), function () { Modules\Customers\Page::render(); } );
98
+ }
99
Lib\Proxy\CustomerInformation::addBooklyMenuItem();
100
Lib\Proxy\CustomerGroups::addBooklyMenuItem();
101
add_submenu_page( 'bookly-menu', $notifications, $notifications, 'manage_options',
102
Modules\Notifications\Page::pageSlug(), function () { Modules\Notifications\Page::render(); } );
103
Modules\Sms\Page::addBooklyMenuItem();
104
+ if ( $current_user->has_cap( 'manage_options' ) || $current_user->has_cap( 'manage_bookly_appointments' ) ) {
105
+ add_submenu_page( 'bookly-menu', $payments, $payments, 'read',
106
+ Modules\Payments\Page::pageSlug(), function () { Modules\Payments\Page::render(); } );
107
+ }
108
add_submenu_page( 'bookly-menu', $appearance, $appearance, 'manage_options',
109
Modules\Appearance\Page::pageSlug(), function () { Modules\Appearance\Page::render(); } );
110
Lib\Proxy\Coupons::addBooklyMenuItem();
119
add_submenu_page( 'bookly-menu', 'Debug', 'Debug', 'manage_options',
120
Modules\Debug\Page::pageSlug(), function () { Modules\Debug\Page::render(); } );
121
}
122
+ if ( ! Lib\Config::proActive() ) {
123
+ $submenu['bookly-menu'][] = array( esc_attr__( 'Get Bookly Pro', 'bookly' ) . ' <i class="fas fa-certificate" style="color: #f4662f"></i>', 'read', Lib\Utils\Common::prepareUrlReferrers( 'https://codecanyon.net/item/bookly/7226091?ref=ladela', 'admin_menu' ), );
124
+ }
125
}
126
127
unset ( $submenu['bookly-menu'][0] );
backend/components/controls/Container.php ADDED
@@ -0,0 +1,37 @@
1
+ <?php
2
+
3
+ namespace Bookly\Backend\Components\Controls;
4
+
5
+ use Bookly\Lib as BooklyLib;
6
+
7
+ /**
8
+ * Class Container
9
+ *
10
+ * @package Bookly\Backend\Components\Controls
11
+ */
12
+ class Container extends BooklyLib\Base\Component
13
+ {
14
+ /**
15
+ * Render header for container.
16
+ *
17
+ * @param string $title
18
+ * @param string $id
19
+ * @param bool $opened
20
+ */
21
+ public static function renderHeader( $title, $id = null, $opened = true )
22
+ {
23
+ if ( empty( $id ) ) {
24
+ $id = 'container_' . mt_rand( 10000, 99999 );
25
+ }
26
+
27
+ self::renderTemplate( 'container', compact( 'title', 'id', 'opened' ) );
28
+ }
29
+
30
+ /**
31
+ * Render the end of container.
32
+ */
33
+ public static function renderFooter()
34
+ {
35
+ print '</div></div>';
36
+ }
37
+ }
backend/components/controls/templates/container.php ADDED
@@ -0,0 +1,4 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
+ <div class="bookly-collapse">
3
+ <a class="h4" href="#<?php echo $id ?>" data-toggle="collapse" role="button" aria-expanded="true"><?php echo esc_html( $title ) ?></a>
4
+ <div id="<?php echo $id ?>" class="bookly-margin-top-lg collapse in" aria-expanded="true">
backend/components/dialogs/appointment/customer_details/templates/customer_details.php CHANGED
@@ -17,16 +17,9 @@ use Bookly\Lib\Config;
17
<div class="form-group">
18
<label for="bookly-appointment-status"><?php _e( 'Status', 'bookly' ) ?></label>
19
<select class="bookly-custom-field form-control" id="bookly-appointment-status">
20
- <option value="<?php echo CustomerAppointment::STATUS_PENDING ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_PENDING ) ) ?></option>
21
- <option value="<?php echo CustomerAppointment::STATUS_APPROVED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_APPROVED ) ) ?></option>
22
- <option value="<?php echo CustomerAppointment::STATUS_CANCELLED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_CANCELLED ) ) ?></option>
23
- <option value="<?php echo CustomerAppointment::STATUS_REJECTED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_REJECTED ) ) ?></option>
24
- <?php if ( Config::waitingListActive() ) : ?>
25
- <option value="<?php echo CustomerAppointment::STATUS_WAITLISTED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_WAITLISTED ) ) ?></option>
26
- <?php endif ?>
27
- <?php if ( Config::tasksActive() ) : ?>
28
- <option value="<?php echo CustomerAppointment::STATUS_DONE ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_DONE ) ) ?></option>
29
- <?php endif ?>
30
</select>
31
</div>
32
<div class="form-group" <?php if ( ! Config::groupBookingActive() ) echo ' style="display:none"' ?>>
17
<div class="form-group">
18
<label for="bookly-appointment-status"><?php _e( 'Status', 'bookly' ) ?></label>
19
<select class="bookly-custom-field form-control" id="bookly-appointment-status">
20
+ <?php foreach ( CustomerAppointment::getStatuses() as $status ): ?>
21
+ <option value="<?php echo $status ?>"><?php echo esc_html( CustomerAppointment::statusToString( $status ) ) ?></option>
22
+ <?php endforeach ?>
23
</select>
24
</div>
25
<div class="form-group" <?php if ( ! Config::groupBookingActive() ) echo ' style="display:none"' ?>>
backend/components/dialogs/appointment/delete/Ajax.php CHANGED
@@ -2,7 +2,6 @@
2
namespace Bookly\Backend\Components\Dialogs\Appointment\Delete;
3
4
use Bookly\Lib;
5
- use Bookly\Lib\DataHolders\Booking as DataHolders;
6
7
/**
8
* Class Ajax
@@ -33,6 +32,10 @@ class Ajax extends Lib\Base\Ajax
33
/** @var Lib\Entities\CustomerAppointment $ca */
34
foreach ( $ca_list as $ca ) {
35
switch ( $ca->getStatus() ) {
36
case Lib\Entities\CustomerAppointment::STATUS_PENDING:
37
case Lib\Entities\CustomerAppointment::STATUS_WAITLISTED:
38
$ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_REJECTED );
@@ -40,12 +43,15 @@ class Ajax extends Lib\Base\Ajax
40
case Lib\Entities\CustomerAppointment::STATUS_APPROVED:
41
$ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_CANCELLED );
42
break;
43
}
44
- Lib\Notifications\Sender::sendSingle(
45
- DataHolders\Simple::create( $ca ),
46
- null,
47
- array( 'cancellation_reason' => $reason )
48
- );
49
}
50
}
51
2
namespace Bookly\Backend\Components\Dialogs\Appointment\Delete;
3
4
use Bookly\Lib;
5
6
/**
7
* Class Ajax
32
/** @var Lib\Entities\CustomerAppointment $ca */
33
foreach ( $ca_list as $ca ) {
34
switch ( $ca->getStatus() ) {
35
+ case Lib\Entities\CustomerAppointment::STATUS_CANCELLED:
36
+ case Lib\Entities\CustomerAppointment::STATUS_REJECTED:
37
+ case Lib\Entities\CustomerAppointment::STATUS_DONE:
38
+ continue 2;
39
case Lib\Entities\CustomerAppointment::STATUS_PENDING:
40
case Lib\Entities\CustomerAppointment::STATUS_WAITLISTED:
41
$ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_REJECTED );
43
case Lib\Entities\CustomerAppointment::STATUS_APPROVED:
44
$ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_CANCELLED );
45
break;
46
+ default:
47
+ $busy_statuses = (array) Lib\Proxy\CustomStatuses::prepareBusyStatuses( array() );
48
+ if ( in_array( $ca->getStatus(), $busy_statuses ) ) {
49
+ $ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_CANCELLED );
50
+ } else {
51
+ $ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_REJECTED );
52
+ }
53
}
54
+ Lib\Notifications\Booking\Sender::sendForCA( $ca, null, array( 'cancellation_reason' => $reason ) );
55
}
56
}
57
backend/components/dialogs/appointment/delete/templates/delete.php CHANGED
@@ -9,6 +9,7 @@ use Bookly\Backend\Components\Controls\Buttons;
9
<div class="modal-title h2"><?php _e( 'Delete', 'bookly' ) ?></div>
10
</div>
11
<div class="modal-body">
12
<div class="checkbox">
13
<label>
14
<input id="bookly-delete-notify" type="checkbox" />
9
<div class="modal-title h2"><?php _e( 'Delete', 'bookly' ) ?></div>
10
</div>
11
<div class="modal-body">
12
+ <p><?php esc_html_e( 'You are going to delete appointment(s). Notifications will be sent in accordance with your settings.', 'bookly' ) ?></p>
13
<div class="checkbox">
14
<label>
15
<input id="bookly-delete-notify" type="checkbox" />
backend/components/dialogs/appointment/edit/Ajax.php CHANGED
@@ -28,6 +28,19 @@ class Ajax extends Lib\Base\Ajax
28
? Lib\Entities\Service::TYPE_PACKAGE
29
: Lib\Entities\Service::TYPE_SIMPLE;
30
31
$result = array(
32
'staff' => array(),
33
'customers' => array(),
@@ -38,14 +51,7 @@ class Ajax extends Lib\Base\Ajax
38
'week_days' => array(),
39
'time_interval' => Lib\Config::getTimeSlotLength(),
40
'status' => array(
41
- 'items' => array(
42
- 'pending' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_PENDING ),
43
- 'approved' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_APPROVED ),
44
- 'cancelled' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_CANCELLED ),
45
- 'rejected' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_REJECTED ),
46
- 'waitlisted' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_WAITLISTED ),
47
- 'done' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_DONE ),
48
- ),
49
),
50
'extras_consider_duration' => (int) Lib\Proxy\ServiceExtras::considerDuration( true ),
51
'extras_multiply_nop' => (int) get_option( 'bookly_service_extras_multiply_nop', 1 ),
@@ -53,7 +59,7 @@ class Ajax extends Lib\Base\Ajax
53
54
// Staff list.
55
$staff = Lib\Entities\Staff::query()->findOne();
56
- $staff_members = $staff ? Lib\Config::proActive() ? Lib\Entities\Staff::query()->find() : array( $staff ) : array();
57
$postfix_archived = sprintf( ' (%s)', __( 'Archived', 'bookly' ) );
58
59
$max_duration = 0;
@@ -359,9 +365,10 @@ class Ajax extends Lib\Base\Ajax
359
$total_number_of_persons = 0;
360
$max_extras_duration = 0;
361
foreach ( $customers as $i => $customer ) {
362
- if ( $customer['status'] == Lib\Entities\CustomerAppointment::STATUS_PENDING ||
363
- $customer['status'] == Lib\Entities\CustomerAppointment::STATUS_APPROVED
364
- ) {
365
$total_number_of_persons += $customer['number_of_persons'];
366
if ( $customer['extras_consider_duration'] ) {
367
$extras_duration = Lib\Proxy\ServiceExtras::getTotalDuration( $customer['extras'] );
@@ -436,8 +443,10 @@ class Ajax extends Lib\Base\Ajax
436
->leftJoin( 'CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
437
->where( 'a.staff_id', $staff_id )
438
->where( 'a.service_id', $service_id )
439
- ->whereNot( 'ca.status', Lib\Entities\CustomerAppointment::STATUS_CANCELLED )
440
- ->whereNot( 'ca.status', Lib\Entities\CustomerAppointment::STATUS_REJECTED )
441
->where( 'start_date', $start_date )
442
->findOne();
443
@@ -490,7 +499,7 @@ class Ajax extends Lib\Base\Ajax
490
}
491
if ( $notification != 'no' ) {
492
foreach ( $orders as $order ) {
493
- Lib\Proxy\RecurringAppointments::sendRecurring( $order->getItem( 0 ), $order );
494
}
495
}
496
}
@@ -535,17 +544,17 @@ class Ajax extends Lib\Base\Ajax
535
// Send notifications.
536
if ( $notification == 'changed_status' ) {
537
foreach ( $ca_status_changed as $ca ) {
538
- Lib\Notifications\Sender::sendSingle( DataHolders\Simple::create( $ca )->setAppointment( $appointment ) );
539
}
540
} elseif ( $notification == 'all' ) {
541
$ca_list = $appointment->getCustomerAppointments( true );
542
foreach ( $ca_status_changed as $ca ) {
543
// The value "just_created" was initialized for the objects of this array
544
- Lib\Notifications\Sender::sendSingle( DataHolders\Simple::create( $ca )->setAppointment( $appointment ) );
545
unset( $ca_list[ $ca->getId() ] );
546
}
547
foreach ( $ca_list as $ca ) {
548
- Lib\Notifications\Sender::sendSingle( DataHolders\Simple::create( $ca )->setAppointment( $appointment ) );
549
}
550
}
551
@@ -588,7 +597,10 @@ class Ajax extends Lib\Base\Ajax
588
589
$max_extras_duration = 0;
590
foreach ( $customers as $customer ) {
591
- if ( in_array( $customer['status'], array( Lib\Entities\CustomerAppointment::STATUS_PENDING, Lib\Entities\CustomerAppointment::STATUS_APPROVED ) ) ) {
592
if ( $customer['extras_consider_duration'] ) {
593
$extras_duration = Lib\Proxy\ServiceExtras::getTotalDuration( $customer['extras'] );
594
if ( $extras_duration > $max_extras_duration ) {
28
? Lib\Entities\Service::TYPE_PACKAGE
29
: Lib\Entities\Service::TYPE_SIMPLE;
30
31
+ $statuses = Lib\Proxy\CustomStatuses::prepareAllStatuses( array(
32
+ Lib\Entities\CustomerAppointment::STATUS_PENDING,
33
+ Lib\Entities\CustomerAppointment::STATUS_APPROVED,
34
+ Lib\Entities\CustomerAppointment::STATUS_CANCELLED,
35
+ Lib\Entities\CustomerAppointment::STATUS_REJECTED,
36
+ Lib\Entities\CustomerAppointment::STATUS_WAITLISTED,
37
+ Lib\Entities\CustomerAppointment::STATUS_DONE,
38
+ ) );
39
+ $status_items = array();
40
+ foreach ( $statuses as $status ) {
41
+ $status_items[ $status ] = Lib\Entities\CustomerAppointment::statusToString( $status );
42
+ }
43
+
44
$result = array(
45
'staff' => array(),
46
'customers' => array(),
51
'week_days' => array(),
52
'time_interval' => Lib\Config::getTimeSlotLength(),
53
'status' => array(
54
+ 'items' => $status_items,
55
),
56
'extras_consider_duration' => (int) Lib\Proxy\ServiceExtras::considerDuration( true ),
57
'extras_multiply_nop' => (int) get_option( 'bookly_service_extras_multiply_nop', 1 ),
59
60
// Staff list.
61
$staff = Lib\Entities\Staff::query()->findOne();
62
+ $staff_members = $staff ? Lib\Config::proActive() ? Lib\Utils\Common::isCurrentUserSupervisor() ? Lib\Entities\Staff::query()->find() : Lib\Entities\Staff::query()->where( 'wp_user_id', get_current_user_id() )->find() : array( $staff ) : array();
63
$postfix_archived = sprintf( ' (%s)', __( 'Archived', 'bookly' ) );
64
65
$max_duration = 0;
365
$total_number_of_persons = 0;
366
$max_extras_duration = 0;
367
foreach ( $customers as $i => $customer ) {
368
+ if ( in_array( $customer['status'], Lib\Proxy\CustomStatuses::prepareBusyStatuses( array(
369
+ Lib\Entities\CustomerAppointment::STATUS_PENDING,
370
+ Lib\Entities\CustomerAppointment::STATUS_APPROVED
371
+ ) ) ) ) {
372
$total_number_of_persons += $customer['number_of_persons'];
373
if ( $customer['extras_consider_duration'] ) {
374
$extras_duration = Lib\Proxy\ServiceExtras::getTotalDuration( $customer['extras'] );
443
->leftJoin( 'CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
444
->where( 'a.staff_id', $staff_id )
445
->where( 'a.service_id', $service_id )
446
+ ->whereNotIn( 'ca.status', Lib\Proxy\CustomStatuses::prepareFreeStatuses( array(
447
+ Lib\Entities\CustomerAppointment::STATUS_CANCELLED,
448
+ Lib\Entities\CustomerAppointment::STATUS_REJECTED
449
+ ) ) )
450
->where( 'start_date', $start_date )
451
->findOne();
452
499
}
500
if ( $notification != 'no' ) {
501
foreach ( $orders as $order ) {
502
+ Lib\Notifications\Booking\Sender::sendForOrder( $order, array(), $notification == 'all' );
503
}
504
}
505
}
544
// Send notifications.
545
if ( $notification == 'changed_status' ) {
546
foreach ( $ca_status_changed as $ca ) {
547
+ Lib\Notifications\Booking\Sender::sendForCA( $ca, $appointment );
548
}
549
} elseif ( $notification == 'all' ) {
550
$ca_list = $appointment->getCustomerAppointments( true );
551
foreach ( $ca_status_changed as $ca ) {
552
// The value "just_created" was initialized for the objects of this array
553
+ Lib\Notifications\Booking\Sender::sendForCA( $ca, $appointment, array(), true );
554
unset( $ca_list[ $ca->getId() ] );
555
}
556
foreach ( $ca_list as $ca ) {
557
+ Lib\Notifications\Booking\Sender::sendForCA( $ca, $appointment, array(), true );
558
}
559
}
560
597
598
$max_extras_duration = 0;
599
foreach ( $customers as $customer ) {
600
+ if ( in_array( $customer['status'], Lib\Proxy\CustomStatuses::prepareBusyStatuses( array(
601
+ Lib\Entities\CustomerAppointment::STATUS_PENDING,
602
+ Lib\Entities\CustomerAppointment::STATUS_APPROVED
603
+ ) ) ) ) {
604
if ( $customer['extras_consider_duration'] ) {
605
$extras_duration = Lib\Proxy\ServiceExtras::getTotalDuration( $customer['extras'] );
606
if ( $extras_duration > $max_extras_duration ) {
backend/components/dialogs/appointment/edit/resources/js/ng-appointment.js CHANGED
@@ -1222,7 +1222,8 @@
1222
with_options : 1,
1223
exclude : exclude,
1224
extras : extras,
1225
- duration : $scope.form.service.id ? undefined : $scope.dataSource.getServiceDuration()
1226
},
1227
function (response) {
1228
$scope.$apply(function($scope) {
1222
with_options : 1,
1223
exclude : exclude,
1224
extras : extras,
1225
+ nop : $scope.dataSource.getTotalNumberOfPersons(),
1226
+ duration : $scope.form.service.id ? undefined : $scope.dataSource.getServiceDuration()
1227
},
1228
function (response) {
1229
$scope.$apply(function($scope) {
backend/components/dialogs/appointment/edit/templates/edit.php CHANGED
@@ -3,6 +3,7 @@ use Bookly\Backend\Components\Controls\Buttons;
3
use Bookly\Backend\Components\Dialogs;
4
use Bookly\Backend\Components\Dialogs\Appointment\Edit\Proxy;
5
use Bookly\Backend\Components\Dialogs\Appointment\AttachPayment\Proxy as AttachPaymentProxy;
6
use Bookly\Lib\Config;
7
use Bookly\Lib\Entities\CustomerAppointment;
8
?>
@@ -122,7 +123,7 @@ use Bookly\Lib\Entities\CustomerAppointment;
122
<?php Proxy\Shared::renderAppointmentDialogCustomersList() ?>
123
<span class="dropdown">
124
<button type="button" class="btn btn-sm btn-default bookly-margin-left-xs" data-toggle="dropdown" popover="<?php esc_attr_e( 'Status', 'bookly' ) ?>: {{statusToString(customer.status)}}">
125
- <span ng-class="{'fa fa-fw': true, 'fa-clock': customer.status == 'pending', 'fa-check': customer.status == 'approved', 'fa-times': customer.status == 'cancelled', 'fa-times-circle': customer.status == 'rejected', 'fa-list-alt': customer.status == 'waitlisted', 'fa-check-circle': customer.status == 'done'}"></span>
126
<span class="caret"></span>
127
</button>
128
<ul class="dropdown-menu">
@@ -153,7 +154,7 @@ use Bookly\Lib\Entities\CustomerAppointment;
153
<?php if ( Config::waitingListActive() ): ?>
154
<li>
155
<a href ng-click="customer.status = 'waitlisted'">
156
- <span class="fa fa-fw fa-list-alt"></span>
157
<?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_WAITLISTED ) ) ?>
158
</a>
159
</li>
@@ -166,6 +167,14 @@ use Bookly\Lib\Entities\CustomerAppointment;
166
</a>
167
</li>
168
<?php endif ?>
169
</ul>
170
</span>
171
<button type="button" class="btn btn-sm btn-default bookly-margin-left-xs" data-toggle="modal" href="#bookly-payment-details-modal" data-payment_id="{{customer.payment_id}}" ng-show="customer.payment_id || customer.payment_create" popover="<?php esc_attr_e( 'Payment', 'bookly' ) ?>: {{customer.payment_title}}" ng-disabled="customer.payment_create">
@@ -216,13 +225,11 @@ use Bookly\Lib\Entities\CustomerAppointment;
216
217
<div class=form-group>
218
<label for="bookly-notification"><?php esc_html_e( 'Send notifications', 'bookly' ) ?></label>
219
- <p class="help-block"><?php is_admin() ?
220
- esc_html_e( 'If email or SMS notifications are enabled and you want customers or staff member to be notified about this appointment after saving, select appropriate option before clicking Save. With "If status changed" the notifications are sent to those customers whose status has just been changed. With "To all customers" the notifications are sent to everyone in the list.', 'bookly' ) :
221
- esc_html_e( 'If email or SMS notifications are enabled and you want customers or yourself to be notified about this appointment after saving, select appropriate option before clicking Save. With "If status changed" the notifications are sent to those customers whose status has just been changed. With "To all customers" the notifications are sent to everyone in the list.', 'bookly' ) ?></p>
222
<select class="form-control" style="margin-top: 0" ng-model=form.notification id="bookly-notification" ng-init="form.notification = '<?php echo get_user_meta( get_current_user_id(), 'bookly_appointment_form_send_notifications', true ) ?>' || 'no'" >
223
<option value="no"><?php esc_html_e( 'Don\'t send', 'bookly' ) ?></option>
224
- <option value="changed_status"><?php esc_html_e( 'If status changed', 'bookly' ) ?></option>
225
- <option value="all"><?php esc_html_e( 'To all customers', 'bookly' ) ?></option>
226
</select>
227
</div>
228
3
use Bookly\Backend\Components\Dialogs;
4
use Bookly\Backend\Components\Dialogs\Appointment\Edit\Proxy;
5
use Bookly\Backend\Components\Dialogs\Appointment\AttachPayment\Proxy as AttachPaymentProxy;
6
+ use Bookly\Lib;
7
use Bookly\Lib\Config;
8
use Bookly\Lib\Entities\CustomerAppointment;
9
?>
123
<?php Proxy\Shared::renderAppointmentDialogCustomersList() ?>
124
<span class="dropdown">
125
<button type="button" class="btn btn-sm btn-default bookly-margin-left-xs" data-toggle="dropdown" popover="<?php esc_attr_e( 'Status', 'bookly' ) ?>: {{statusToString(customer.status)}}">
126
+ <span ng-class="{'fa fa-fw': true, 'fa-clock': customer.status == 'pending', 'fa-check': customer.status == 'approved', 'fa-times': customer.status == 'cancelled', 'fa-times-circle': customer.status == 'rejected', 'fa-list-ol': customer.status == 'waitlisted', 'fa-check-circle': customer.status == 'done', 'fa-lock': 0<?php foreach ( Lib\Proxy\CustomStatuses::prepareBusyStatuses( array() ) as $status ): ?> || customer.status == '<?php echo $status ?>'<?php endforeach ?>, 'fa-lock-open': 0<?php foreach ( Lib\Proxy\CustomStatuses::prepareFreeStatuses( array() ) as $status ): ?> || customer.status == '<?php echo $status ?>'<?php endforeach ?>}"></span>
127
<span class="caret"></span>
128
</button>
129
<ul class="dropdown-menu">
154
<?php if ( Config::waitingListActive() ): ?>
155
<li>
156
<a href ng-click="customer.status = 'waitlisted'">
157
+ <span class="fa fa-fw fa-list-ol"></span>
158
<?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_WAITLISTED ) ) ?>
159
</a>
160
</li>
167
</a>
168
</li>
169
<?php endif ?>
170
+ <?php foreach ( (array) Lib\Proxy\CustomStatuses::getAll() as $status ): ?>
171
+ <li>
172
+ <a href ng-click="customer.status = '<?php echo $status->getSlug() ?>'">
173
+ <span class="fa fa-fw fa-lock<?php if ( ! $status->getBusy() ): ?>-open<?php endif ?>"></span>
174
+ <?php echo esc_html( $status->getName() ) ?>
175
+ </a>
176
+ </li>
177
+ <?php endforeach ?>
178
</ul>
179
</span>
180
<button type="button" class="btn btn-sm btn-default bookly-margin-left-xs" data-toggle="modal" href="#bookly-payment-details-modal" data-payment_id="{{customer.payment_id}}" ng-show="customer.payment_id || customer.payment_create" popover="<?php esc_attr_e( 'Payment', 'bookly' ) ?>: {{customer.payment_title}}" ng-disabled="customer.payment_create">
225
226
<div class=form-group>
227
<label for="bookly-notification"><?php esc_html_e( 'Send notifications', 'bookly' ) ?></label>
228
+ <p class="help-block"><?php esc_html_e( 'If you have added a new customer to this appointment or changed the appointment status for an existing customer, and for these records you want the corresponding email or SMS notifications to be sent to their recipients, select the "Send if new or status changed" option before clicking Save. You can also send notifications as if all customers were added as new by selecting "Send as for new".', 'bookly' ) ?></p>
229
<select class="form-control" style="margin-top: 0" ng-model=form.notification id="bookly-notification" ng-init="form.notification = '<?php echo get_user_meta( get_current_user_id(), 'bookly_appointment_form_send_notifications', true ) ?>' || 'no'" >
230
<option value="no"><?php esc_html_e( 'Don\'t send', 'bookly' ) ?></option>
231
+ <option value="changed_status"><?php esc_html_e( 'Send if new or status changed', 'bookly' ) ?></option>
232
+ <option value="all"><?php esc_html_e( 'Send as for new', 'bookly' ) ?></option>
233
</select>
234
</div>
235
backend/components/dialogs/customer/delete/Ajax.php CHANGED
@@ -75,13 +75,19 @@ class Ajax extends Lib\Base\Ajax
75
$events = Lib\Entities\Appointment::query( 'a' )
76
->leftJoin( 'CustomerAppointment', 'ca', 'a.id = ca.appointment_id' )
77
->whereIn( 'ca.customer_id', $customer_ids )
78
- ->whereIn( 'ca.status', array( Lib\Entities\CustomerAppointment::STATUS_APPROVED, Lib\Entities\CustomerAppointment::STATUS_PENDING ) )
79
->whereGte( 'a.start_date', current_time( 'mysql' ) )
80
->count() > 0;
81
$tasks = Lib\Entities\Appointment::query( 'a' )
82
->leftJoin( 'CustomerAppointment', 'ca', 'a.id = ca.appointment_id' )
83
->whereIn( 'ca.customer_id', $customer_ids )
84
- ->whereIn( 'ca.status', array( Lib\Entities\CustomerAppointment::STATUS_APPROVED, Lib\Entities\CustomerAppointment::STATUS_PENDING ) )
85
->where( 'a.start_date', null )
86
->count() > 0;
87
$meta = get_user_meta( get_current_user_id(), 'bookly_delete_customers_options', true );
@@ -112,7 +118,7 @@ class Ajax extends Lib\Base\Ajax
112
protected static function hasAccess( $action )
113
{
114
if ( parent::hasAccess( $action ) ) {
115
- if ( ! Lib\Utils\Common::isCurrentUserAdmin() ) {
116
switch ( $action ) {
117
case 'deleteCustomers':
118
case 'checkCustomers':
75
$events = Lib\Entities\Appointment::query( 'a' )
76
->leftJoin( 'CustomerAppointment', 'ca', 'a.id = ca.appointment_id' )
77
->whereIn( 'ca.customer_id', $customer_ids )
78
+ ->whereIn( 'ca.status', Lib\Proxy\CustomStatuses::prepareBusyStatuses( array(
79
+ Lib\Entities\CustomerAppointment::STATUS_PENDING,
80
+ Lib\Entities\CustomerAppointment::STATUS_APPROVED,
81
+ ) ) )
82
->whereGte( 'a.start_date', current_time( 'mysql' ) )
83
->count() > 0;
84
$tasks = Lib\Entities\Appointment::query( 'a' )
85
->leftJoin( 'CustomerAppointment', 'ca', 'a.id = ca.appointment_id' )
86
->whereIn( 'ca.customer_id', $customer_ids )
87
+ ->whereIn( 'ca.status', Lib\Proxy\CustomStatuses::prepareBusyStatuses( array(
88
+ Lib\Entities\CustomerAppointment::STATUS_PENDING,
89
+ Lib\Entities\CustomerAppointment::STATUS_APPROVED,
90
+ ) ) )
91
->where( 'a.start_date', null )
92
->count() > 0;
93
$meta = get_user_meta( get_current_user_id(), 'bookly_delete_customers_options', true );
118
protected static function hasAccess( $action )
119
{
120
if ( parent::hasAccess( $action ) ) {
121
+ if ( ! Lib\Utils\Common::isCurrentUserSupervisor() ) {
122
switch ( $action ) {
123
case 'deleteCustomers':
124
case 'checkCustomers':
backend/components/dialogs/customer/edit/Ajax.php CHANGED
@@ -86,7 +86,7 @@ class Ajax extends Lib\Base\Ajax
86
protected static function hasAccess( $action )
87
{
88
if ( parent::hasAccess( $action ) ) {
89
- if ( ! Lib\Utils\Common::isCurrentUserAdmin() ) {
90
switch ( $action ) {
91
case 'saveCustomer':
92
return Lib\Entities\Staff::query()
86
protected static function hasAccess( $action )
87
{
88
if ( parent::hasAccess( $action ) ) {
89
+ if ( ! Lib\Utils\Common::isCurrentUserSupervisor() ) {
90
switch ( $action ) {
91
case 'saveCustomer':
92
return Lib\Entities\Staff::query()
backend/components/dialogs/customer/proxy/CustomerGroups.php CHANGED
@@ -5,7 +5,7 @@ use Bookly\Lib;
5
6
/**
7
* @since Bookly 16.2
8
- * @deprecated
9
* Proxy file was moved
10
*/
11
abstract class CustomerGroups extends Lib\Base\Proxy
5
6
/**
7
* @since Bookly 16.2
8
+ * @deprecated To be removed in the future.
9
* Proxy file was moved
10
*/
11
abstract class CustomerGroups extends Lib\Base\Proxy
backend/components/dialogs/customer/proxy/CustomerInformation.php CHANGED
@@ -5,7 +5,7 @@ use Bookly\Lib;
5
6
/**
7
* @since Bookly 16.2
8
- * @deprecated
9
* Proxy file was moved
10
*/
11
abstract class CustomerInformation extends Lib\Base\Proxy
5
6
/**
7
* @since Bookly 16.2
8
+ * @deprecated To be removed in the future.
9
* Proxy file was moved
10
*/
11
abstract class CustomerInformation extends Lib\Base\Proxy
backend/components/dialogs/customer/proxy/Pro.php CHANGED
@@ -5,7 +5,7 @@ use Bookly\Lib;
5
6
/**
7
* @since Bookly 16.2
8
- * @deprecated
9
* Proxy file was moved
10
*/
11
abstract class Pro extends Lib\Base\Proxy
5
6
/**
7
* @since Bookly 16.2
8
+ * @deprecated To be removed in the future.
9
* Proxy file was moved
10
*/
11
abstract class Pro extends Lib\Base\Proxy
backend/components/dialogs/notifications/Dialog.php ADDED
@@ -0,0 +1,48 @@
1
+ <?php
2
+ namespace Bookly\Backend\Components\Dialogs\Notifications;
3
+
4
+ use Bookly\Lib as BooklyLib;
5
+ use Bookly\Backend\Components\Dialogs\Sms\Dialog as SmsDialog;
6
+
7
+ /**
8
+ * Class Dialog
9
+ * @package Bookly\Backend\Components\Dialogs\Notifications
10
+ */
11
+ class Dialog extends SmsDialog
12
+ {
13
+ /**
14
+ * Render payment details dialog.
15
+ */
16
+ public static function render()
17
+ {
18
+ self::enqueueStyles( array(
19
+ 'frontend' => array( 'css/ladda.min.css', ),
20
+ 'backend' => array( 'css/fontawesome-all.min.css', 'css/select2.min.css' ),
21
+ ) );
22
+
23
+ self::enqueueScripts( array(
24
+ 'frontend' => array(
25
+ 'js/spin.min.js' => array( 'jquery', ),
26
+ 'js/ladda.min.js' => array( 'jquery', ),
27
+ ),
28
+ 'backend' => array( 'js/select2.full.min.js' => array( 'jquery' ), ),
29
+ 'bookly' => array( 'backend/components/dialogs/sms/resources/js/notification-dialog.js' => array( 'jquery' ), ),
30
+ ) );
31
+
32
+ wp_localize_script( 'bookly-notification-dialog.js', 'BooklyNotificationDialogL10n', array(
33
+ 'csrfToken' => BooklyLib\Utils\Common::getCsrfToken(),
34
+ 'recurringActive' => (int) BooklyLib\Config::recurringAppointmentsActive(),
35
+ 'gateway' => 'email',
36
+ 'defaultNotification' => self::getDefaultNotification(),
37
+ 'title' => array(
38
+ 'container' => __( 'Email', 'bookly' ),
39
+ 'new' => __( 'New email notification', 'bookly' ),
40
+ 'edit' => __( 'Edit email notification', 'bookly' ),
41
+ 'create' => __( 'Create notification', 'bookly' ),
42
+ 'save' => __( 'Save notification', 'bookly' ),
43
+ ),
44
+ ) );
45
+
46
+ SmsDialog::renderTemplate( 'dialog', array( 'self' => __CLASS__ ) );
47
+ }
48
+ }
backend/components/dialogs/notifications/templates/_attach.php ADDED
@@ -0,0 +1,13 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use Bookly\Backend\Modules\Notifications\Proxy;
3
+ ?>
4
+ <div class="bookly-js-attach-container">
5
+ <div class="form-group bookly-js-attach bookly-js-ics">
6
+ <input type="hidden" name="notification[attach_ics]" value="0">
7
+ <div class="checkbox"><label for="notification_attach_ics">
8
+ <input id="notification_attach_ics" name="notification[attach_ics]" type="checkbox" value="1"/> <?php esc_attr_e( 'Attach ICS file', 'bookly' ) ?>
9
+ </label>
10
+ </div>
11
+ </div>
12
+ <?php Proxy\Invoices::renderAttach() ?>
13
+ </div>
backend/components/dialogs/notifications/templates/_codes.php ADDED
@@ -0,0 +1,19 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use Bookly\Lib\Entities\Notification;
3
+ $codes = new \Bookly\Backend\Modules\Notifications\Lib\Codes( 'email' )
4
+ ?>
5
+ <div class="form-group bookly-js-codes-container">
6
+ <label><?php esc_attr_e( 'Codes', 'bookly' ) ?></label>
7
+ <div class="bookly-max-height-md">
8
+ <?php foreach ( Notification::getTypes() as $notification_type ) :
9
+ if ( in_array( $notification_type, array(
10
+ Notification::TYPE_NEW_BOOKING_RECURRING,
11
+ Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED_RECURRING,
12
+ ) ) ) {
13
+ $codes->render( $notification_type, true );
14
+ } else {
15
+ $codes->render( $notification_type );
16
+ }
17
+ endforeach ?>
18
+ </div>
19
+ </div>
backend/components/dialogs/notifications/templates/_editor.php ADDED
@@ -0,0 +1,19 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
+ <div class="row">
3
+ <div class="col-md-12">
4
+ <div class="form-group">
5
+ <label><?php esc_html_e( 'Body', 'bookly' ) ?></label>
6
+ <?php wp_editor( '', 'bookly-js-tinymce-message', array(
7
+ 'textarea_name' => 'notification[message]',
8
+ 'media_buttons' => false,
9
+ 'editor_height' => 250,
10
+ 'default_editor' => 'tinymce',
11
+ 'tinymce' => array(
12
+ 'resize' => true,
13
+ 'wp_autoresize_on' => true,
14
+ )
15
+ ) ) ?>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ <?php static::renderTemplate( '_attach' ) ?>
backend/components/dialogs/notifications/templates/_subject.php ADDED
@@ -0,0 +1,9 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
+ <div class="row">
3
+ <div class="col-md-12">
4
+ <div class="form-group">
5
+ <label for="notification_subject"><?php esc_attr_e( 'Subject', 'bookly' ) ?></label>
6
+ <input type="text" class="form-control" id="notification_subject" name="notification[subject]" value=""/>
7
+ </div>
8
+ </div>
9
+ </div>
backend/components/dialogs/notifications/templates/_types.php ADDED
@@ -0,0 +1,100 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use Bookly\Lib\Entities\Notification;
3
+ use Bookly\Lib\Config;
4
+ ?>
5
+ <div class="row">
6
+ <div class="col-md-12">
7
+ <div class="form-group">
8
+ <label for="notification_type"><?php esc_attr_e( 'Type', 'bookly' ) ?></label>
9
+ <p class="help-block"><?php esc_html_e( 'Select the type of event at which the notification is sent.', 'bookly' ) ?></p>
10
+ <select class="form-control" name="notification[type]" id="notification_type">
11
+ <optgroup label="<?php esc_attr_e( 'Instant notifications', 'bookly' ) ?>">
12
+ <option value="<?php echo Notification::TYPE_NEW_BOOKING ?>"
13
+ data-set="instantly"
14
+ data-recipients='["customer","staff","admin"]'
15
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_NEW_BOOKING ) ) ?>'
16
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_NEW_BOOKING ) ) ?></option>
17
+ <?php if ( Config::recurringAppointmentsActive() ) : ?>
18
+ <option value="<?php echo Notification::TYPE_NEW_BOOKING_RECURRING ?>"
19
+ data-set="instantly"
20
+ data-recipients='["customer","staff","admin"]'
21
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_NEW_BOOKING_RECURRING ) ) ?>'
22
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_NEW_BOOKING_RECURRING ) ) ?></option>
23
+ <?php endif ?>
24
+ <?php if ( Config::proActive() ) : ?>
25
+ <option value="<?php echo Notification::TYPE_NEW_BOOKING_COMBINED ?>"
26
+ data-set="instantly"
27
+ data-recipients='["customer"]'
28
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_NEW_BOOKING_COMBINED ) ) ?>'
29
+ data-attach='["invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_NEW_BOOKING_COMBINED ) ) ?></option>
30
+ <?php endif ?>
31
+ <option value="<?php echo Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED ?>"
32
+ data-set="instantly"
33
+ data-recipients='["customer","staff","admin"]'
34
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED ) ) ?>'
35
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED ) ) ?></option>
36
+ <?php if ( Config::recurringAppointmentsActive() ) : ?>
37
+ <option value="<?php echo Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED_RECURRING ?>"
38
+ data-set="instantly"
39
+ data-recipients='["customer","staff","admin"]'
40
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED_RECURRING ) ) ?>'
41
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED_RECURRING ) ) ?></option>
42
+ <?php endif ?>
43
+ <?php if ( Config::packagesActive() ) : ?>
44
+ <option value="<?php echo Notification::TYPE_NEW_PACKAGE ?>"
45
+ data-set="instantly"
46
+ data-recipients='["customer","staff","admin"]'
47
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_NEW_PACKAGE ) ) ?>'
48
+ data-attach='[]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_NEW_PACKAGE ) ) ?></option>
49
+ <?php endif ?>
50
+ <?php if ( Config::packagesActive() ) : ?>
51
+ <option value="<?php echo Notification::TYPE_PACKAGE_DELETED ?>"
52
+ data-set="instantly"
53
+ data-recipients='["customer","staff","admin"]'
54
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_PACKAGE_DELETED ) ) ?>'
55
+ data-attach='[]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_PACKAGE_DELETED ) ) ?></option>
56
+ <?php endif ?>
57
+ <?php if ( Config::proActive() ) : ?>
58
+ <option value="<?php echo Notification::TYPE_CUSTOMER_NEW_WP_USER ?>"
59
+ data-set="instantly"
60
+ data-recipients='["customer"]'
61
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_CUSTOMER_NEW_WP_USER ) ) ?>'
62
+ data-attach='[]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_CUSTOMER_NEW_WP_USER ) ) ?></option>
63
+ <?php endif ?>
64
+ <?php if ( Config::waitingListActive() ) : ?>
65
+ <option value="<?php echo Notification::TYPE_STAFF_WAITING_LIST ?>"
66
+ data-set="instantly"
67
+ data-recipients='["staff","admin"]'
68
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_STAFF_WAITING_LIST ) ) ?>'
69
+ data-attach='[]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_STAFF_WAITING_LIST ) ) ?></option>
70
+ <?php endif ?>
71
+ </optgroup>
72
+ <?php if ( Config::proActive() ) : ?>
73
+ <optgroup label="<?php esc_attr_e( 'Scheduled notifications (require cron setup)', 'bookly' ) ?>">
74
+ <option value="<?php echo Notification::TYPE_APPOINTMENT_REMINDER ?>"
75
+ data-set="bidirectional full"
76
+ data-recipients='["customer","staff","admin"]'
77
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_APPOINTMENT_REMINDER ) ) ?>'
78
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_APPOINTMENT_REMINDER ) ) ?></option>
79
+ <option value="<?php echo Notification::TYPE_LAST_CUSTOMER_APPOINTMENT ?>"
80
+ data-set="bidirectional full"
81
+ data-recipients='["customer","staff","admin"]'
82
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_LAST_CUSTOMER_APPOINTMENT ) ) ?>'
83
+ data-attach='["ics"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_LAST_CUSTOMER_APPOINTMENT ) ) ?></option>
84
+ <option value="<?php echo Notification::TYPE_CUSTOMER_BIRTHDAY ?>"
85
+ data-set="bidirectional at-time"
86
+ data-recipients='["customer"]'
87
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_CUSTOMER_BIRTHDAY ) ) ?>'
88
+ data-attach='[]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_CUSTOMER_BIRTHDAY ) ) ?></option>
89
+ <option value="<?php echo Notification::TYPE_STAFF_DAY_AGENDA ?>"
90
+ data-set="before"
91
+ data-recipients='["staff","admin"]'
92
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_STAFF_DAY_AGENDA ) ) ?>'
93
+ data-attach='[]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_STAFF_DAY_AGENDA ) ) ?></option>
94
+ </optgroup>
95
+ <?php endif ?>
96
+ </select>
97
+ <p class="help-block bookly-js-help-block bookly-margin-top-xs <?php echo Notification::TYPE_NEW_BOOKING_COMBINED ?>"><?php esc_html_e( 'This notification is sent once for a booking made by a customer and includes all cart items.', 'bookly' ) ?></p>
98
+ </div>
99
+ </div>
100
+ </div>
backend/components/dialogs/sms/Ajax.php ADDED
@@ -0,0 +1,48 @@
1
+ <?php
2
+ namespace Bookly\Backend\Components\Dialogs\Sms;
3
+
4
+ use Bookly\Lib;
5
+
6
+ /**
7
+ * Class Ajax
8
+ * @package Bookly\Backend\Components\Sms
9
+ */
10
+ class Ajax extends Lib\Base\Ajax
11
+ {
12
+ /**
13
+ * @inheritdoc
14
+ */
15
+ protected static function permissions()
16
+ {
17
+ return array( '_default' => 'user' );
18
+ }
19
+
20
+ /**
21
+ * Save notification.
22
+ */
23
+ public function saveNotification()
24
+ {
25
+ $data = self::parameter( 'notification' );
26
+ $notification = new Lib\Entities\Notification();
27
+ $is_new = ! $notification->load( $data['id'] );
28
+ if ( ! $is_new ) {
29
+ unset( $data['id'] );
30
+ }
31
+ $notification->setFields( $data )->save();
32
+
33
+ wp_send_json_success();
34
+ }
35
+
36
+ /**
37
+ * Get notification data.
38
+ */
39
+ public static function getNotificationData()
40
+ {
41
+ $notification = new Lib\Entities\Notification();
42
+ $notification->load( self::parameter( 'id' ) );
43
+ $data = $notification->getFields();
44
+ $data['settings'] = array_merge( Lib\DataHolders\Notification\Settings::getDefault(), json_decode( $data['settings'], true ) );
45
+
46
+ wp_send_json_success( $data );
47
+ }
48
+ }
backend/components/dialogs/sms/Dialog.php ADDED
@@ -0,0 +1,80 @@
1
+ <?php
2
+ namespace Bookly\Backend\Components\Dialogs\Sms;
3
+
4
+ use Bookly\Lib as BooklyLib;
5
+ use Bookly\Backend\Components\Controls\Buttons;
6
+
7
+ /**
8
+ * Class Dialog
9
+ * @package Bookly\Backend\Components\Dialogs\Sms
10
+ */
11
+ class Dialog extends BooklyLib\Base\Component
12
+ {
13
+ /**
14
+ * Render payment details dialog.
15
+ */
16
+ public static function render()
17
+ {
18
+ add_filter( 'wp_default_editor', function() { return 'tinymce'; } );
19
+ self::enqueueStyles( array(
20
+ 'frontend' => array( 'css/ladda.min.css', ),
21
+ 'backend' => array( 'css/fontawesome-all.min.css', 'css/select2.min.css' ),
22
+ ) );
23
+
24
+ self::enqueueScripts( array(
25
+ 'frontend' => array(
26
+ 'js/spin.min.js' => array( 'jquery', ),
27
+ 'js/ladda.min.js' => array( 'jquery', ),
28
+ ),
29
+ 'backend' => array(
30
+ 'js/select2.full.min.js' => array( 'jquery' ),
31
+ 'js/dropdown.js' => array( 'jquery' ),),
32
+ 'module' => array( 'js/notification-dialog.js' => array( 'bookly-dropdown.js', 'bookly-select2.full.min.js' ), ),
33
+ ) );
34
+
35
+ wp_localize_script( 'bookly-notification-dialog.js', 'BooklyNotificationDialogL10n', array(
36
+ 'csrfToken' => BooklyLib\Utils\Common::getCsrfToken(),
37
+ 'recurringActive' => (int) BooklyLib\Config::recurringAppointmentsActive(),
38
+ 'gateway' => 'sms',
39
+ 'defaultNotification' => self::getDefaultNotification(),
40
+ 'title' => array(
41
+ 'container' => __( 'Sms', 'bookly' ),
42
+ 'new' => __( 'New sms notification', 'bookly' ),
43
+ 'edit' => __( 'Edit sms notification', 'bookly' ),
44
+ 'create' => __( 'Create notification', 'bookly' ),
45
+ 'save' => __( 'Save notification', 'bookly' ),
46
+ ),
47
+ ) );
48
+
49
+ self::renderTemplate( 'dialog' );
50
+ }
51
+
52
+ public static function renderNewNotificationButton()
53
+ {
54
+ print '<div class="form-group">';
55
+ Buttons::renderCustom( 'bookly-js-new-notification', 'btn-success', esc_html__( 'New notification...', 'bookly' ) );
56
+ print '</div>';
57
+ }
58
+
59
+ /**
60
+ * @return array
61
+ */
62
+ protected static function getDefaultNotification()
63
+ {
64
+ $default = array(
65
+ 'type' => BooklyLib\Entities\Notification::TYPE_NEW_BOOKING,
66
+ 'active' => 1,
67
+ 'attach_ics' => 0,
68
+ 'attach_invoice' => 0,
69
+ 'message' => '',
70
+ 'name' => '',
71
+ 'subject' => '',
72
+ 'to_admin' => 0,
73
+ 'to_customer' => 1,
74
+ 'to_staff' => 0,
75
+ 'settings' => BooklyLib\DataHolders\Notification\Settings::getDefault(),
76
+ );
77
+
78
+ return $default;
79
+ }
80
+ }
backend/components/dialogs/sms/resources/js/notification-dialog.js ADDED
@@ -0,0 +1,267 @@
1
+ jQuery(function ($) {
2
+ var $notificationList = $('#bookly-js-notification-list'),
3
+ $btnNewNotification = $('#bookly-js-new-notification'),
4
+ $modalNotification = $('#bookly-js-notification-modal'),
5
+ containers = {
6
+ settings : $('#bookly-js-settings-container', $modalNotification),
7
+ statuses : $('.bookly-js-statuses-container', $modalNotification),
8
+ services : $('.bookly-js-services-container', $modalNotification),
9
+ recipient: $('.bookly-js-recipient-container', $modalNotification),
10
+ message : $('#bookly-js-message-container', $modalNotification),
11
+ attach : $('.bookly-js-attach-container', $modalNotification),
12
+ codes : $('.bookly-js-codes-container', $modalNotification)
13
+ },
14
+ $offsets = $('.bookly-js-offset', containers.settings),
15
+ $notificationType = $('select[name=\'notification[type]\']', containers.settings),
16
+ $labelSend = $('.bookly-js-offset-exists', containers.settings),
17
+ $offsetBidirectional = $('.bookly-js-offset-bidirectional', containers.settings),
18
+ $offsetBefore = $('.bookly-js-offset-before', containers.settings),
19
+ $btnSaveNotification = $('.bookly-js-save', $modalNotification),
20
+ $helpType = $('.bookly-js-help-block', containers.settings),
21
+ $codes = $('table.bookly-codes', $modalNotification),
22
+ $status = $("select[name='notification[settings][status]']", containers.settings),
23
+ $defaultStatuses
24
+ ;
25
+
26
+ function format(option) {
27
+ return option.id && option.element.dataset.icon ? '<i class="fa fa-fw ' + option.element.dataset.icon + '"></i> ' + option.text : option.text;
28
+ }
29
+
30
+ $modalNotification
31
+ .on('show.bs.modal.first', function () {
32
+ $notificationType.trigger('change');
33
+ $modalNotification.unbind('show.bs.modal.first');
34
+ if (BooklyNotificationDialogL10n.gateway == 'email') {
35
+ tinymce.init(tinyMCEPreInit);
36
+ }
37
+ containers.message.siblings('a[data-toggle=collapse]').html(BooklyNotificationDialogL10n.title.container);
38
+ $('.bookly-js-services', containers.settings).booklyDropdown();
39
+ $('.modal-title', $modalNotification).html(BooklyNotificationDialogL10n.title.edit);
40
+ });
41
+
42
+ /**
43
+ * Notification
44
+ */
45
+ $notificationType
46
+ .on('change', function () {
47
+ if ($(':selected', $notificationType).length == 0) {
48
+ // Un supported notification type (without Pro)
49
+ $notificationType.val('new_booking');
50
+ }
51
+ var $modalBody = $(this).closest('.modal-body'),
52
+ $attach = $modalBody.find('.bookly-js-attach'),
53
+ $selected = $(':selected', $notificationType),
54
+ set = $selected.data('set').split(' '),
55
+ recipients = $selected.data('recipients'),
56
+ showAttach = $selected.data('attach') || [],
57
+ hideServices = true,
58
+ hideStatuses = true,
59
+ notification_type = $selected.val()
60
+ ;
61
+
62
+ $helpType.hide();
63
+ $offsets.hide();
64
+
65
+ switch (notification_type) {
66
+ case 'appointment_reminder':
67
+ hideServices = false;
68
+ break;
69
+ case 'ca_status_changed':
70
+ case 'ca_status_changed_recurring':
71
+ hideStatuses = false;
72
+ hideServices = false;
73
+ break;
74
+ case 'customer_birthday':
75
+ break;
76
+ case 'customer_new_wp_user':
77
+ break;
78
+ case 'last_appointment':
79
+ break;
80
+ case 'new_booking':
81
+ case 'new_booking_recurring':
82
+ hideStatuses = false;
83
+ hideServices = false;
84
+ break;
85
+ case 'new_booking_combined':
86
+ $helpType.filter('.' + notification_type).show();
87
+ break;
88
+ case 'new_package':
89
+ break;
90
+ case 'package_deleted':
91
+ break;
92
+ case 'staff_day_agenda':
93
+ $("input[name='notification[settings][option]'][value=3]", containers.settings).prop('checked', true);
94
+ break;
95
+ case 'staff_waiting_list':
96
+ break;
97
+ }
98
+
99
+ containers.statuses.toggle(!hideStatuses);
100
+ containers.services.toggle(!hideServices);
101
+
102
+ switch (set[0]) {
103
+ case 'bidirectional':
104
+ $labelSend.show();
105
+ $('.bookly-js-offsets', $offsetBidirectional).each(function () {
106
+ $(this).toggle($(this).hasClass('bookly-js-' + set[1]));
107
+ });
108
+ if (set[1] !== 'full') {
109
+ $('.bookly-js-' + set[1] + ' input:radio', $offsetBidirectional).prop('checked', true);
110
+ }
111
+ $offsetBidirectional.show();
112
+ break;
113
+ case 'before':
114
+ $offsetBefore.show();
115
+ $labelSend.show();
116
+ break;
117
+ }
118
+
119
+ // Hide/un hide recipient
120
+ $.each(['customer', 'staff', 'admin'], function (index, value) {
121
+ $("[name$='[to_" + value + "]']:checkbox", containers.recipient).closest('.checkbox').toggle(recipients.indexOf(value) != -1);
122
+ });
123
+
124
+ // Hide/un hide attach
125
+ $attach.hide();
126
+ $.each(showAttach, function (index, value) {
127
+ $('.bookly-js-' + value, containers.attach).show();
128
+ });
129
+ $codes.hide();
130
+ $codes.filter('.bookly-js-codes-' + notification_type).show();
131
+ })
132
+ .select2({
133
+ minimumResultsForSearch: -1,
134
+ width : '100%',
135
+ theme : 'bootstrap',
136
+ allowClear : false,
137
+ templateResult : format,
138
+ templateSelection : format,
139
+ dropdownCssClass : 'bookly-notifications',
140
+ escapeMarkup : function (m) {
141
+ return m;
142
+ }
143
+ });
144
+
145
+ $('.bookly-js-services', $modalNotification).booklyDropdown({});
146
+
147
+ $btnNewNotification
148
+ .on('click', function () {
149
+ showNotificationDialog();
150
+ });
151
+
152
+ $btnSaveNotification
153
+ .on('click', function () {
154
+ var data = $modalNotification.serializeArray(),
155
+ ladda = Ladda.create(this);
156
+ ladda.start();
157
+ data.push({name: 'action', value: 'bookly_save_notification'});
158
+ data.push({name: 'notification[gateway]', value: BooklyNotificationDialogL10n.gateway});
159
+ if (BooklyNotificationDialogL10n.gateway == 'email') {
160
+ data.push({name: 'notification[message]', value: tinyMCE.activeEditor.getContent({format: 'raw'})});
161
+ }
162
+
163
+ $.ajax({
164
+ url : ajaxurl,
165
+ type : 'POST',
166
+ data : data,
167
+ dataType: 'json',
168
+ success : function (response) {
169
+ ladda.stop();
170
+ if (response.success) {
171
+ $notificationList.DataTable().ajax.reload();
172
+ $modalNotification.modal('hide');
173
+ }
174
+ }
175
+ });
176
+ });
177
+
178
+ $notificationList
179
+ .on('click', '[data-action=edit]', function () {
180
+ var row = $notificationList.DataTable().row($(this).closest('td')),
181
+ data = row.data();
182
+ showNotificationDialog(data.id);
183
+ });
184
+
185
+ function showNotificationDialog(id) {
186
+ $('.bookly-js-loading:first-child', $modalNotification).addClass('bookly-loading').removeClass('collapse');
187
+ $('.bookly-js-loading:last-child', $modalNotification).addClass('collapse');
188
+ $modalNotification.modal('show');
189
+ if (id === undefined) {
190
+ setNotificationData(BooklyNotificationDialogL10n.defaultNotification);
191
+ } else {
192
+ $.ajax({
193
+ url : ajaxurl,
194
+ type : 'POST',
195
+ data : {
196
+ action : 'bookly_get_notification_data',
197
+ csrf_token: BooklyNotificationDialogL10n.csrfToken,
198
+ id : id
199
+ },
200
+ dataType: 'json',
201
+ success : function (response) {
202
+ setNotificationData(response.data);
203
+ }
204
+ });
205
+ }
206
+ }
207
+
208
+ function setNotificationData(data) {
209
+ // Notification settings
210
+ $("input[name='notification[id]']", containers.settings).val(data.id);
211
+ $("input[name='notification[name]']", containers.settings).val(data.name);
212
+ $("input[name='notification[active]'][value=" + data.active + "]", containers.settings).prop('checked', true);
213
+ if ($defaultStatuses) {
214
+ $status.html($defaultStatuses);
215
+ } else {
216
+ $defaultStatuses = $status.html();
217
+ }
218
+ if ($status.find('option[value="' + data.settings.status + '"]').length > 0) {
219
+ $status.val(data.settings.status);
220
+ } else {
221
+ var custom_status = data.settings.status.charAt(0).toUpperCase() + data.settings.status.slice(1);
222
+
223
+ $status.append($("<option></option>", {value: data.settings.status, text: custom_status.replace(/\-/g, ' ')})).val(data.settings.status);
224
+ }
225
+
226
+ $("input[name='notification[settings][services][any]'][value='" + data.settings.services.any + "']", containers.settings).prop('checked', true);
227
+ $('.bookly-js-services', containers.settings).booklyDropdown('setSelected', data.settings.services.ids);
228
+
229
+ $("input[name='notification[settings][option]'][value=" + data.settings.option + "]", containers.settings).prop('checked', true);
230
+ $("select[name='notification[settings][offset_hours]']", containers.settings).val(data.settings.offset_hours);
231
+ $("select[name='notification[settings][perform]']", containers.settings).val(data.settings.perform);
232
+ $("select[name='notification[settings][at_hour]']", containers.settings).val(data.settings.at_hour);
233
+ $("select[name='notification[settings][offset_bidirectional_hours]']", containers.settings).val(data.settings.offset_bidirectional_hours);
234
+ $("select[name='notification[settings][offset_before_hours]']", containers.settings).val(data.settings.offset_before_hours);
235
+ $("select[name='notification[settings][before_at_hour]']", containers.settings).val(data.settings.before_at_hour);
236
+
237
+ // Recipients
238
+ $("input[name='notification[to_staff]']", containers.settings).prop('checked', data.to_staff == '1');
239
+ $("input[name='notification[to_customer]']", containers.settings).prop('checked', data.to_customer == '1');
240
+ $("input[name='notification[to_admin]']", containers.settings).prop('checked', data.to_admin == '1');
241
+
242
+ // Message
243
+ $("input[name='notification[subject]']", containers.message).val(data.subject);
244
+ $("input[name='notification[attach_ics]']", containers.message).prop('checked', data.attach_ics == '1');
245
+ $("input[name='notification[attach_invoice]']", containers.message).prop('checked', data.attach_invoice == '1');
246
+
247
+ if (BooklyNotificationDialogL10n.gateway == 'email') {
248
+ tinymce.activeEditor.setContent(data.message);
249
+ } else {
250
+ $("textarea[name='notification[message]']", containers.message).val(data.message);
251
+ }
252
+ if (data.hasOwnProperty('id')) {
253
+ $('.modal-title', $modalNotification).html(BooklyNotificationDialogL10n.title.edit);
254
+ containers.settings.collapse('hide');
255
+ containers.message.collapse('show');
256
+ $('.bookly-js-save > span.ladda-label', $modalNotification).text(BooklyNotificationDialogL10n.title.save);
257
+ } else {
258
+ $('.modal-title', $modalNotification).html(BooklyNotificationDialogL10n.title.new);
259
+ containers.settings.collapse('show');
260
+ $('.bookly-js-save > span.ladda-label', $modalNotification).text(BooklyNotificationDialogL10n.title.create);
261
+ }
262
+
263
+ $notificationType.val(data.type).trigger('change');
264
+
265
+ $('.bookly-js-loading', $modalNotification).toggleClass('collapse');
266
+ }
267
+ });
backend/components/dialogs/sms/templates/_codes.php ADDED
@@ -0,0 +1,17 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use Bookly\Lib\Entities\Notification;
3
+ $codes = new \Bookly\Backend\Modules\Notifications\Lib\Codes( 'sms' )
4
+ ?>
5
+ <div class="form-group bookly-js-codes-container bookly-max-height-md">
6
+ <label><?php esc_attr_e( 'Codes', 'bookly' ) ?></label>
7
+ <?php foreach ( Notification::getTypes( 'sms' ) as $notification_type ) :
8
+ if ( in_array( $notification_type, array(
9
+ Notification::TYPE_NEW_BOOKING_RECURRING,
10
+ Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED_RECURRING,
11
+ ) ) ) {
12
+ $codes->render( $notification_type, true );
13
+ } else {
14
+ $codes->render( $notification_type );
15
+ }
16
+ endforeach ?>
17
+ </div>
backend/components/dialogs/sms/templates/_editor.php ADDED
@@ -0,0 +1,8 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
+ <div class="row">
3
+ <div class="col-md-12">
4
+ <div class="form-group"><label for="notification_message"><?php esc_html_e( 'Body', 'bookly' ) ?></label>
5
+ <textarea class="form-control" name="notification[message]" rows="8" id="notification_message"></textarea>
6
+ </div>
7
+ </div>
8
+ </div>
backend/components/dialogs/sms/templates/_modal_body.php ADDED
@@ -0,0 +1,52 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use Bookly\Backend\Components\Controls\Container;
3
+ ?>
4
+ <div class="bookly-js-loading" style="height: 200px;"></div>
5
+ <div class="bookly-js-loading">
6
+ <?php Container::renderHeader( __( 'Notification settings', 'bookly' ), 'bookly-js-settings-container' ) ?>
7
+ <input type="hidden" name="notification[id]" value="0">
8
+ <div class="row">
9
+ <div class="col-md-12">
10
+ <div class="form-group">
11
+ <label for="notification_name"><?php esc_attr_e( 'Name', 'bookly' ) ?></label>
12
+ <p class="help-block"><?php esc_html_e( 'Enter notification name which will be displayed in the list.', 'bookly' ) ?></p>
13
+ <input type="text" class="form-control" id="notification_name" name="notification[name]" value=""/>
14
+ </div>
15
+ </div>
16
+ </div>
17
+ <div class="row">
18
+ <div class="col-md-12">
19
+ <div class="form-group"><label><?php esc_html_e( 'State', 'bookly' ) ?></label>
20
+ <p class="help-block"><?php esc_html_e( 'Choose whether notification is enabled and sending messages or it is disabled and no messages are sent until you activate the notification.', 'bookly' ) ?></p>
21
+ <div class="radio"><label><input type="radio" name="notification[active]" value="1" checked="checked"> <?php esc_html_e( 'Enabled', 'bookly' ) ?></label></div>
22
+ <div class="radio"><label><input type="radio" name="notification[active]" value="0"> <?php esc_html_e( 'Disabled', 'bookly' ) ?></label></div>
23
+ </div>
24
+ </div>
25
+ </div>
26
+
27
+ <?php $self::renderTemplate( '_types' ) ?>
28
+ <?php static::renderTemplate( '_settings' ) ?>
29
+
30
+ <div class="row bookly-js-recipient-container">
31
+ <div class="col-md-12">
32
+ <div class="form-group">
33
+ <label><?php esc_attr_e( 'Recipients', 'bookly' ) ?></label>
34
+ <p class="help-block"><?php esc_html_e( 'Choose who will receive this notification.', 'bookly' ) ?></p>
35
+ <input type="hidden" name="notification[to_customer]" value="0">
36
+ <div class="checkbox"><label><input type="checkbox" name="notification[to_customer]" value="1"> <?php esc_attr_e( 'Client', 'bookly' ) ?></label></div>
37
+ <input type="hidden" name="notification[to_staff]" value="0">
38
+ <div class="checkbox"><label><input type="checkbox" name="notification[to_staff]" value="1"> <?php esc_attr_e( 'Staff', 'bookly' ) ?></label></div>
39
+ <input type="hidden" name="notification[to_admin]" value="0">
40
+ <div class="checkbox"><label><input type="checkbox" name="notification[to_admin]" value="1"> <?php esc_attr_e( 'Administrators', 'bookly' ) ?></label></div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ <?php Container::renderFooter() ?>
46
+ <?php Container::renderHeader( '', 'bookly-js-message-container' ) ?>
47
+
48
+ <?php $self::renderTemplate( '_subject' ) ?>
49
+ <?php $self::renderTemplate( '_editor' ) ?>
50
+ <?php $self::renderTemplate( '_codes' ) ?>
51
+ <?php Container::renderFooter() ?>
52
+ </div>
backend/components/dialogs/sms/templates/_settings.php ADDED
@@ -0,0 +1,135 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use Bookly\Lib\Entities\CustomerAppointment;
3
+ $statuses = \Bookly\Lib\Entities\CustomerAppointment::getStatuses();
4
+ $service_dropdown_data = \Bookly\Lib\Utils\Common::getServiceDataForDropDown( 's.type <> "package"' );
5
+ ?>
6
+ <div class="bookly-js-statuses-container">
7
+ <div class="row">
8
+ <div class="col-md-12">
9
+ <div class="form-group">
10
+ <label for="notification_status_1"><?php esc_attr_e( 'Appointment status', 'bookly' ) ?></label>
11
+ <p class="help-block"><?php esc_html_e( 'Select what status an appointment should have for the notification to be sent.', 'bookly' ) ?></p>
12
+ <select class="form-control" name="notification[settings][status]" id="notification_status_1">
13
+ <option value="any"><?php esc_attr_e( 'Any', 'bookly' ) ?></option>
14
+ <?php foreach ( $statuses as $status ) : ?>
15
+ <option value="<?php echo $status ?>"><?php echo CustomerAppointment::statusToString( $status ) ?></option>
16
+ <?php endforeach ?>
17
+ </select>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ <div class="bookly-js-services-container">
23
+ <div class="row">
24
+ <div class="col-md-12">
25
+ <div class="form-group"><label><?php esc_html_e( 'Services', 'bookly' ) ?></label>
26
+ <p class="help-block"><?php esc_html_e( 'Choose whether notification should be sent for specific services only or not.', 'bookly' ) ?></p>
27
+ <div class="radio"><label><input type="radio" name="notification[settings][services][any]" value="any" checked="checked"> <?php esc_html_e( 'Any', 'bookly' ) ?></label></div>
28
+ <div class="form-inline">
29
+ <div class="form-group">
30
+ <label><input type="radio" name="notification[settings][services][any]" value="selected"></label>
31
+ <div class="form-group">
32
+ <ul class="bookly-js-services"
33
+ data-icon-class="glyphicon glyphicon-tag"
34
+ data-txt-select-all="<?php esc_attr_e( 'All services', 'bookly' ) ?>"
35
+ data-txt-all-selected="<?php esc_attr_e( 'All services', 'bookly' ) ?>"
36
+ data-txt-nothing-selected="<?php esc_attr_e( 'No service selected', 'bookly' ) ?>"
37
+ >
38
+ <?php foreach ( $service_dropdown_data as $category_id => $category ): ?>
39
+ <li<?php if ( ! $category_id ) : ?> data-flatten-if-single<?php endif ?>><?php echo esc_html( $category['name'] ) ?>
40
+ <ul>
41
+ <?php foreach ( $category['items'] as $service ) : ?>
42
+ <li data-input-name="notification[settings][services][ids][]"
43
+ data-value="<?php echo $service['id'] ?>"
44
+ >
45
+ <?php echo esc_html( $service['title'] ) ?>
46
+ </li>
47
+ <?php endforeach ?>
48
+ </ul>
49
+ </li>
50
+ <?php endforeach ?>
51
+ </ul>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
59
+
60
+ <div class="row bookly-js-offset bookly-js-offset-exists">
61
+ <div class="col-md-12">
62
+ <div class="form-group bookly-margin-bottom-remove">
63
+ <label><?php esc_attr_e( 'Send', 'bookly' ) ?></label>
64
+ </div>
65
+ </div>
66
+ </div>
67
+
68
+ <div class="bookly-js-offset bookly-js-offset-bidirectional">
69
+ <div class="row bookly-js-offsets bookly-js-relative bookly-js-full">
70
+ <div class="col-md-12 bookly-margin-bottom-md">
71
+ <div class="form-inline">
72
+ <div class="form-group">
73
+ <label><input type="radio" name="notification[settings][option]" value="1" checked></label>
74
+ <select class="form-control" name="notification[settings][offset_hours]">
75
+ <?php foreach ( array_merge( range( 1, 24 ), range( 48, 336, 24 ), array( 504, 672 ) ) as $hour ) : ?>
76
+ <option value="<?php echo $hour ?>"><?php echo \Bookly\Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) ?></option>
77
+ <?php endforeach ?>
78
+ <option value="43200">30 <?php esc_attr_e( 'days', 'bookly' ) ?></option>
79
+ </select>
80
+ <select class="form-control" name="notification[settings][perform]">
81
+ <option value="before"><?php esc_attr_e( 'before', 'bookly' ) ?></option>
82
+ <option value="after"><?php esc_attr_e( 'after', 'bookly' ) ?></option>
83
+ </select>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ </div>
88
+
89
+ <div class="row bookly-js-offsets bookly-js-at-time bookly-js-full">
90
+ <div class="col-md-12">
91
+ <div class="form-inline bookly-margin-bottom-sm">
92
+ <div class="form-group">
93
+ <label><input type="radio" name="notification[settings][option]" value="2"></label>
94
+ <select class="form-control" name="notification[settings][offset_bidirectional_hours]">
95
+ <?php foreach ( array_merge( array( - 672, - 504 ), range( - 336, - 24, 24 ) ) as $hour ) : ?>
96
+ <option value="<?php echo $hour ?>"><?php echo \Bookly\Lib\Utils\DateTime::secondsToInterval( abs( $hour ) * HOUR_IN_SECONDS ) ?>&nbsp;<?php esc_attr_e( 'before', 'bookly' ) ?></option>
97
+ <?php endforeach ?>
98
+ <option value="0" selected><?php esc_attr_e( 'on the same day', 'bookly' ) ?></option>
99
+ <?php foreach ( array_merge( range( 24, 336, 24 ), array( 504, 672 ) ) as $hour ) : ?>
100
+ <option value="<?php echo $hour ?>"><?php echo \Bookly\Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) ?>&nbsp;<?php esc_attr_e( 'after', 'bookly' ) ?></option>
101
+ <?php endforeach ?>
102
+ </select>
103
+ <?php esc_attr_e( 'at', 'bookly' ) ?>
104
+ <select class="form-control" name="notification[settings][at_hour]">
105
+ <?php foreach ( range( 0, 23 ) as $hour ) : ?>
106
+ <option value="<?php echo $hour ?>"><?php echo \Bookly\Lib\Utils\DateTime::buildTimeString( $hour * HOUR_IN_SECONDS, false ) ?></option>
107
+ <?php endforeach ?>
108
+ </select>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </div>
114
+
115
+ <div class="row bookly-js-offset bookly-js-offset-before">
116
+ <div class="col-md-12">
117
+ <div class="form-inline bookly-margin-bottom-sm">
118
+ <div class="form-group">
119
+ <label><input type="radio" name="notification[settings][option]" value="3"></label>
120
+ <select class="form-control" name="notification[settings][offset_before_hours]" id="notification_send_2">
121
+ <?php foreach ( array_merge( array( - 672, - 504 ), range( - 336, - 24, 24 ) ) as $hour ) : ?>
122
+ <option value="<?php echo $hour ?>"><?php echo \Bookly\Lib\Utils\DateTime::secondsToInterval( abs( $hour ) * HOUR_IN_SECONDS ) ?>&nbsp;<?php esc_attr_e( 'before', 'bookly' ) ?></option>
123
+ <?php endforeach ?>
124
+ <option value="0" selected><?php esc_attr_e( 'on the same day', 'bookly' ) ?></option>
125
+ </select>
126
+ <?php esc_attr_e( 'at', 'bookly' ) ?>
127
+ <select class="form-control" name="notification[settings][before_at_hour]">
128
+ <?php foreach ( range( 0, 23 ) as $hour ) : ?>
129
+ <option value="<?php echo $hour ?>"><?php echo \Bookly\Lib\Utils\DateTime::buildTimeString( $hour * HOUR_IN_SECONDS, false ) ?></option>
130
+ <?php endforeach ?>
131
+ </select>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ </div>
backend/components/dialogs/sms/templates/_subject.php ADDED
@@ -0,0 +1,2 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
+ <input type="hidden" name="notification[subject]" value=""/>
backend/components/dialogs/sms/templates/_types.php ADDED
@@ -0,0 +1,100 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use Bookly\Lib\Entities\Notification;
3
+ use Bookly\Lib\Config;
4
+ ?>
5
+ <div class="row">
6
+ <div class="col-md-12">
7
+ <div class="form-group">
8
+ <label for="notification_type"><?php esc_attr_e( 'Type', 'bookly' ) ?></label>
9
+ <p class="help-block"><?php esc_html_e( 'Select the type of event at which the notification is sent.', 'bookly' ) ?></p>
10
+ <select class="form-control" name="notification[type]" id="notification_type">
11
+ <optgroup label="<?php esc_attr_e( 'Instant notifications', 'bookly' ) ?>">
12
+ <option value="<?php echo Notification::TYPE_NEW_BOOKING ?>"
13
+ data-set="instantly"
14
+ data-recipients='["customer","staff","admin"]'
15
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_NEW_BOOKING ) ) ?>'
16
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_NEW_BOOKING ) ) ?></option>
17
+ <?php if ( Config::recurringAppointmentsActive() ): ?>
18
+ <option value="<?php echo Notification::TYPE_NEW_BOOKING_RECURRING ?>"
19
+ data-set="instantly"
20
+ data-recipients='["customer","staff","admin"]'
21
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_NEW_BOOKING_RECURRING ) ) ?>'
22
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_NEW_BOOKING_RECURRING ) ) ?></option>
23
+ <?php endif ?>
24
+ <?php if ( Config::proActive() ): ?>
25
+ <option value="<?php echo Notification::TYPE_NEW_BOOKING_COMBINED ?>"
26
+ data-set="instantly"
27
+ data-recipients='["customer"]'
28
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_NEW_BOOKING_COMBINED ) ) ?>'
29
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_NEW_BOOKING_COMBINED ) ) ?></option>
30
+ <?php endif ?>
31
+ <option value="<?php echo Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED ?>"
32
+ data-set="instantly"
33
+ data-recipients='["customer","staff","admin"]'
34
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED ) ) ?>'
35
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED ) ) ?></option>
36
+ <?php if ( Config::recurringAppointmentsActive() ): ?>
37
+ <option value="<?php echo Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED_RECURRING ?>"
38
+ data-set="instantly"
39
+ data-recipients='["customer","staff","admin"]'
40
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED_RECURRING ) ) ?>'
41
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED_RECURRING ) ) ?></option>
42
+ <?php endif ?>
43
+ <?php if ( Config::packagesActive() ) : ?>
44
+ <option value="<?php echo Notification::TYPE_NEW_PACKAGE ?>"
45
+ data-set="instantly"
46
+ data-recipients='["customer","staff","admin"]'
47
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_NEW_PACKAGE ) ) ?>'
48
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_NEW_PACKAGE ) ) ?></option>
49
+ <?php endif ?>
50
+ <?php if ( Config::packagesActive() ) : ?>
51
+ <option value="<?php echo Notification::TYPE_PACKAGE_DELETED ?>"
52
+ data-set="instantly"
53
+ data-recipients='["customer","staff","admin"]'
54
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_PACKAGE_DELETED ) ) ?>'
55
+ data-attach='["ics","invoice"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_PACKAGE_DELETED ) ) ?></option>
56
+ <?php endif ?>
57
+ <?php if ( Config::proActive() ) : ?>
58
+ <option value="<?php echo Notification::TYPE_CUSTOMER_NEW_WP_USER ?>"
59
+ data-set="instantly"
60
+ data-recipients='["customer"]'
61
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_CUSTOMER_NEW_WP_USER ) ) ?>'
62
+ data-attach='[]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_CUSTOMER_NEW_WP_USER ) ) ?></option>
63
+ <?php endif ?>
64
+ <?php if ( Config::waitingListActive() ) : ?>
65
+ <option value="<?php echo Notification::TYPE_STAFF_WAITING_LIST ?>"
66
+ data-set="instantly"
67
+ data-recipients='["staff","admin"]'
68
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_STAFF_WAITING_LIST ) ) ?>'
69
+ data-attach='[]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_STAFF_WAITING_LIST ) ) ?></option>
70
+ <?php endif ?>
71
+ </optgroup>
72
+ <optgroup label="<?php esc_attr_e( 'Scheduled notifications (require cron setup)', 'bookly' ) ?>">
73
+ <option value="<?php echo Notification::TYPE_APPOINTMENT_REMINDER ?>"
74
+ data-set="bidirectional full"
75
+ data-recipients='["customer","staff","admin"]'
76
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_APPOINTMENT_REMINDER ) ) ?>'
77
+ data-attach='["ics"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_APPOINTMENT_REMINDER ) ) ?></option>
78
+ <option value="<?php echo Notification::TYPE_LAST_CUSTOMER_APPOINTMENT ?>"
79
+ data-set="bidirectional full"
80
+ data-recipients='["customer","staff","admin"]'
81
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_LAST_CUSTOMER_APPOINTMENT ) ) ?>'
82
+ data-attach='["ics"]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_LAST_CUSTOMER_APPOINTMENT ) ) ?></option>
83
+ <?php if ( Config::proActive() ) : ?>
84
+ <option value="<?php echo Notification::TYPE_CUSTOMER_BIRTHDAY ?>"
85
+ data-set="bidirectional at-time"
86
+ data-recipients='["customer"]'
87
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_CUSTOMER_BIRTHDAY ) ) ?>'
88
+ data-attach='[]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_CUSTOMER_BIRTHDAY ) ) ?></option>
89
+ <?php endif ?>
90
+ <option value="<?php echo Notification::TYPE_STAFF_DAY_AGENDA ?>"
91
+ data-set="before"
92
+ data-recipients='["staff","admin"]'
93
+ data-icon='<?php echo esc_attr( Notification::getIcon( Notification::TYPE_STAFF_DAY_AGENDA ) ) ?>'
94
+ data-attach='[]'><?php echo esc_attr( Notification::getTitle( Notification::TYPE_STAFF_DAY_AGENDA ) ) ?></option>
95
+ </optgroup>
96
+ </select>
97
+ <p class="help-block bookly-js-help-block bookly-margin-top-xs <?php echo Notification::TYPE_NEW_BOOKING_COMBINED ?>"><?php esc_html_e( 'This notification is sent once for a booking made by a customer and includes all cart items.', 'bookly' ) ?></p>
98
+ </div>
99
+ </div>
100
+ </div>
backend/components/dialogs/sms/templates/dialog.php ADDED
@@ -0,0 +1,22 @@
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use Bookly\Backend\Components\Controls\Buttons;
3
+ use Bookly\Backend\Components\Controls\Inputs;
4
+ ?>
5
+ <form id="bookly-js-notification-modal" class="modal fade" tabindex=-1 role="dialog">
6
+ <div class="modal-dialog modal-lg">
7
+ <div class="modal-content">
8
+ <div class="modal-header">
9
+ <button type="button" class="close" data-dismiss="modal"><span>×</span></button>
10
+ <div class="modal-title h2"></div>
11
+ </div>
12
+ <div class="modal-body">
13
+ <?php static::renderTemplate( '_modal_body', compact( 'self' ) ) ?>
14
+ </div>
15
+ <div class="modal-footer">
16
+ <?php Inputs::renderCsrf() ?>
17
+ <?php Buttons::renderCustom( null, 'bookly-js-save btn-lg btn-success', esc_html__( 'Save notification', 'bookly' ) ) ?>
18
+ <?php Buttons::renderCustom( null, 'btn-lg btn-default', esc_html__( 'Close', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </form>
backend/components/gutenberg/bookly_form/Block.php CHANGED
@@ -23,7 +23,7 @@ class Block extends Lib\Base\Block
23
wp_localize_script( 'bookly-booking-form-block.js', 'BooklyFormL10n', array(
24
'casest' => Lib\Config::getCaSeSt(),
25
'block' => array(
26
- 'title' => __( 'Bookly - Booking form', 'bookly' ),
27
'description' => __( 'A custom block for displaying booking form', 'bookly' ),
28
),
29
'selectLocation' => __( 'Select location', 'bookly' ),
23
wp_localize_script( 'bookly-booking-form-block.js', 'BooklyFormL10n', array(
24
'casest' => Lib\Config::getCaSeSt(),
25
'block' => array(
26
+ 'title' => 'Bookly - ' . __( 'Booking form', 'bookly' ),
27
'description' => __( 'A custom block for displaying booking form', 'bookly' ),
28
),
29
'selectLocation' => __( 'Select location', 'bookly' ),
backend/components/gutenberg/bookly_form/resources/js/booking-form-block.js CHANGED
@@ -8,7 +8,7 @@
8
wp.blocks.registerBlockType('bookly/form', {
9
title: BooklyFormL10n.block.title,
10
description: BooklyFormL10n.block.description,
11
- icon: el('svg', { width: '20', height: '20', viewBox: "0 0 64 64" },
12
el('path', {style: {fill: "rgb(0, 0, 0)"}, d: "M 8 0 H 56 A 8 8 0 0 1 64 8 V 22 H 0 V 8 A 8 8 0 0 1 8 0 Z"}),
13
el('path', {style: {fill: "rgb(244, 102, 47)"}, d: "M 0 22 H 64 V 56 A 8 8 0 0 1 56 64 H 8 A 8 8 0 0 1 0 56 V 22 Z"}),
14
el('rect', {style: {fill: "rgb(98, 86, 86)"}, x: 6, y: 6, width: 52, height: 10}),
@@ -327,9 +327,9 @@
327
options: options.locations,
328
onChange: function (selectControl) {
329
var location_id = selectControl,
330
- category_id = $select_category.val(),
331
- service_id = $select_service.val(),
332
- staff_id = $select_employee.val()
333
;
334
335
// Validate selected values.
@@ -395,10 +395,10 @@
395
),
396
options: options.categories,
397
onChange: function (selectControl) {
398
- var location_id = $select_location.val(),
399
category_id = selectControl,
400
- service_id = $select_service.val(),
401
- staff_id = $select_employee.val()
402
;
403
404
// Validate selected values.
@@ -443,10 +443,10 @@
443
),
444
options: options.services,
445
onChange: function (selectControl) {
446
- var location_id = $select_location.val(),
447
category_id = '',
448
service_id = selectControl,
449
- staff_id = $select_employee.val()
450
;
451
452
// Validate selected values.
@@ -483,9 +483,9 @@
483
),
484
options: options.staff,
485
onChange: function (selectControl) {
486
- var location_id = $select_location.val(),
487
- category_id = $select_category.val(),
488
- service_id = $select_service.val(),
489
staff_id = selectControl
490
;
491
@@ -520,7 +520,7 @@
520
}
521
522
// Number of persons
523
- if (BooklyFormL10n.addons.multiplyAppointments == '1') {
524
inspectorElements.push(el(components.PanelRow,
525
{},
526
el('label', {htmlFor: 'bookly-js-hide-nop'}, BooklyFormL10n.nop),
@@ -537,7 +537,7 @@
537
}
538
539
// Quantity
540
- if (BooklyFormL10n.addons.groupBooking == '1') {
541
inspectorElements.push(el(components.PanelRow,
542
{},
543
el('label', {htmlFor: 'bookly-js-hide-quantity'}, BooklyFormL10n.quantity),
8
wp.blocks.registerBlockType('bookly/form', {
9
title: BooklyFormL10n.block.title,
10
description: BooklyFormL10n.block.description,
11
+ icon: el('svg', { width: '24', height: '24', viewBox: "0 0 64 64" },
12
el('path', {style: {fill: "rgb(0, 0, 0)"}, d: "M 8 0 H 56 A 8 8 0 0 1 64 8 V 22 H 0 V 8 A 8 8 0 0 1 8 0 Z"}),
13
el('path', {style: {fill: "rgb(244, 102, 47)"}, d: "M 0 22 H 64 V 56 A 8 8 0 0 1 56 64 H 8 A 8 8 0 0 1 0 56 V 22 Z"}),
14
el('rect', {style: {fill: "rgb(98, 86, 86)"}, x: 6, y: 6, width: 52, height: 10}),
327
options: options.locations,
328
onChange: function (selectControl) {
329
var location_id = selectControl,
330
+ category_id = $select_category.val()||'',
331
+ service_id = $select_service.val()||'',
332
+ staff_id = $select_employee.val()||''
333
;
334
335
// Validate selected values.
395
),
396
options: options.categories,
397
onChange: function (selectControl) {
398
+ var location_id = $select_location.val()||'',
399
category_id = selectControl,
400
+ service_id = $select_service.val()||'',
401
+ staff_id = $select_employee.val()||''
402
;
403
404
// Validate selected values.
443
),
444
options: options.services,
445
onChange: function (selectControl) {
446
+ var location_id = $select_location.val()||'',
447
category_id = '',
448
service_id = selectControl,
449
+ staff_id = $select_employee.val()||''
450
;
451
452
// Validate selected values.
483
),
484
options: options.staff,
485
onChange: function (selectControl) {
486
+ var location_id = $select_location.val()||'',
487
+ category_id = $select_category.val()||'',
488
+ service_id = $select_service.val()||'',
489
staff_id = selectControl
490
;
491
520
}
521
522
// Number of persons
523
+ if (BooklyFormL10n.addons.groupBooking == '1') {
524
inspectorElements.push(el(components.PanelRow,
525
{},
526
el('label', {htmlFor: 'bookly-js-hide-nop'}, BooklyFormL10n.nop),
537
}
538
539
// Quantity
540
+ if (BooklyFormL10n.addons.multiplyAppointments == '1') {
541
inspectorElements.push(el(components.PanelRow,
542
{},
543
el('label', {htmlFor: 'bookly-js-hide-quantity'}, BooklyFormL10n.quantity),
backend/components/notices/resources/js/collect-stats.js CHANGED
@@ -1,9 +1,9 @@
1
jQuery(function ($) {
2
var $notice = $('#bookly-collect-stats-notice');
3
$notice.on('close.bs.alert', function () {
4
- $.post(ajaxurl, {action: $notice.data('action'), csrf_token : SupportL10n.csrf_token});
5
});
6
$notice.find('#bookly-enable-collecting-stats-btn').on('click', function () {
7
- $.post(ajaxurl, {action: 'bookly_enable_collecting_stats', csrf_token : SupportL10n.csrf_token});
8
});
9
});
1
jQuery(function ($) {
2
var $notice = $('#bookly-collect-stats-notice');
3
$notice.on('close.bs.alert', function () {
4
+ $.post(ajaxurl, {action: $notice.data('action'), csrf_token : BooklySupportL10n.csrfToken});
5
});
6
$notice.find('#bookly-enable-collecting-stats-btn').on('click', function () {
7
+ $.post(ajaxurl, {action: 'bookly_enable_collecting_stats', csrf_token : BooklySupportL10n.csrfToken});
8
});
9
});
backend/components/notices/resources/js/lite-rebranding.js CHANGED
@@ -1,6 +1,6 @@
1
jQuery(function ($) {
2
var $notice = $('#bookly-lite-rebranding-notice');
3
$notice.on('close.bs.alert', function () {
4
- $.post(ajaxurl, {action: $notice.data('action'), csrf_token : SupportL10n.csrf_token});
5
});
6
});
1
jQuery(function ($) {
2
var $notice = $('#bookly-lite-rebranding-notice');
3
$notice.on('close.bs.alert', function () {
4
+ $.post(ajaxurl, {action: $notice.data('action'), csrf_token : BooklySupportL10n.csrfToken});
5
});
6
});
backend/components/notices/resources/js/nps.js CHANGED
@@ -16,7 +16,7 @@ jQuery(function ($) {
16
if (value <= 7) {
17
$form.show();
18
} else {
19
- $.post(ajaxurl, {action: 'bookly_nps_send', csrf_token : SupportL10n.csrf_token, rate: value});
20
$quiz.hide();
21
$form.hide();
22
$thanks.show();
@@ -35,7 +35,7 @@ jQuery(function ($) {
35
ajaxurl,
36
{
37
action : 'bookly_nps_send',
38
- csrf_token : SupportL10n.csrf_token,
39
rate : $stars.val(),
40
msg : $msg.val(),
41
email : $email.val()
@@ -52,7 +52,7 @@ jQuery(function ($) {
52
});
53
54
$alert.on('close.bs.alert', function () {
55
- $.post(ajaxurl, {action: 'bookly_dismiss_nps_notice', csrf_token : SupportL10n.csrf_token}, function () {
56
// Indicator for Selenium that request has completed.
57
$('.bookly-js-nps-notice').remove();
58
});
16
if (value <= 7) {
17
$form.show();
18
} else {
19
+ $.post(ajaxurl, {action: 'bookly_nps_send', csrf_token : BooklySupportL10n.csrfToken, rate: value});
20
$quiz.hide();
21
$form.hide();
22
$thanks.show();
35
ajaxurl,
36